DESK-Expander

Schon seit ich auf meinem ST Programme schreibe, hat es mich gestört, dass man von Accessories aus nicht auf die Directory-Fenster des Desktop zugreifen kann, sondern sich stattdessen der reichlich unkomfortablen Fileselectorbox bedienen muss.

Zwar gibt es mittlerweile eine ganze Reihe von verbesserten Boxen, doch liegt es in der Natur der Sache, daß auch diese nicht an den Komfort der Desktop-Fenster heranreichen. Abhilfe mußte her und so beschloß ich, in den Eingeweiden meines Computers zu wühlen, um festzustellen, wo und wie Informationen über die Directory-Fenster und die darin selektierten Dateien, bzw. Ordner, im Speicher abgelegt werden. Recht schnell fand ich bei dieser Suche den Objektbaum des Desktops (Bild 1) und feste Zeiger auf sein Wurzelobjekt.

Beim Betrachten des Objektbaums erkennt man, daß jedes Fenster aus einer weißen Box (für den Hintergrund) und untergeordneten ICONs, bzw. PROGDEF-Objekten (für die Darstellung der Files) aufgebaut ist. Ob die Files durch ICONs oder PROGDEF-Objekte dargestellt werden hängt ganz einfach davon ab, welche Darstellungsart im Menü ‘Index' gewählt wurde. Um nun die in einem Fenster selektierten Files zu ermitteln, braucht man nur festzustellen, welche der für die Darstellung der Files zuständigen Objekte den Status SELECTED besitzen. Doch wie bestimmt man den Namen des zugehörigen Files? Dazu muß man wissen, daß das AES intern eine Liste anlegt, in der zu jedem File der dargestellten Directories ein Eintrag existiert. Wie dieser Eintrag aussieht, entnimmt man am besten der Definition der FILEINFO-Struktur im Listing. Sind nun die Files als Icons dargestellt, so zeigt das Feld ib_ptext der ICONBLK-Struktur direkt auf den Filenamen. Im anderen Fall der Darstellung zeigt das Feld ub_parm der APPLBLK-Struktur auf das Dateiattribut. Damit ist also auch geklärt, wie man die Namen der selektierten Files ermittelt (und noch einiges mehr). Doch leider fehlt zu den vollständigen Dateinamen noch der jeweilige Pfad. Die Aufgabe, diesen zu ermitteln, übernimmt die Funktion desk_path, die den Pfad einfach nur aus den Titelzeilen der Fenster herauskopiert.

Wie werden die Funktionen desk_files und desk_path nun genau genutzt? Zunächst erwarten beide Funktionen als Parameter die Nummer des Fensters, mit dem sie arbeiten sollen. Als zweiten Parameter erwartet desk_files ein Array, in dem es Zeiger auf die FILEINFO-Strukturen der selektierten Files einträgt. Die Funktion desk_path erwartet hier ein char-Array zur Aufnahme des Pfadstrings. Bei fehlerfreier Abarbeitung liefert desk_files die Zahl der selektierten Files zurück; desk_path liefert hier eine 0. Anderenfalls liefern beide eine negative Fehlernummer zurück, deren Bedeutung am Beginn des Listings definiert ist. Da leider keine dokumentierten Systemvariablen existieren, über die man auf den Objektbaum des Desktops und die Titelzeilen der Fenster zugreifen kann, mußte ich, um das Projekt DESK-EXPANDER zu realisieren, zwangsläufig auf undokumentierte Adressen zugreifen. Hierzu habe ich diese Adressen sowohl für TOS 1.0 als auch TOS 1.2 (Blitter-TOS) unter tatkräftiger Mitwirkung eines Freundes (Hallo Matthias) ermittelt und die Funktionen so gehalten, daß sie die vorliegende TOS-Version erkennen und entsprechend auf unterschiedliche Adressen zugreifen. Dadurch sind die Funktionen garantiert unter beiden TOS-Versionen lauffähig. Ob dies auch auf das ‘Disketten-TOS’ zutrifft, kann ich leider nicht sagen, ich vermute es jedoch.

Abschließend nun noch die wichtigste Frage: Wozu kann man diese Funktionen nutzen? Ich meine, es gibt wirklich viele Möglichkeiten. Wie wäre es zum Beispiel mit einem Accessory, das sämtliche selektierte Dateien verschlüsselt? Oder ein Druckerspooler, dem man auf diese Weise die Liste der zu druckenden Dateien übermittelt? Kurz gesagt, jedes Accessory, das sonst die Fileselectorbox nutzen müßte, profitiert von diesen Routinen. Zudem eröffnet sicherlich die Kenntnis über den Desktop-Objektbaum viel Raum für interessante Spielereien.

/*------------------------------------*/
/*  Torsten Beuck, Hamburg 88/89      */
/*  © MAXON Computer GmbH             */
/*  Programmiert in MEGAMAX LASER C   */
/*------------------------------------*/

#include <osbind.h>
#include <define.h>     /* Nützliche Definitionen wie TRUE und FALSE */
#include <obdefs.h>
#include <gemdefs.h>
#include <strings.h>    /* Deklaration der Stringfunktionen. */

#define SYS_BASE        ((char**) 0x4F2)
#define EOSVERS         -1 /* TOS-Version wird nicht unterstützt */ 
#define EINVWNR         -2 /* ungültige Fensternummer            */ 
#define EDKTNAP         -3 /* Desktop ist nicht aktueller Prozeß */
#define ENOPATH         -4 /* Kein Pfad vorhanden. 
                              Fenster wurde noch nie D.h. der Zeiger 
                              auf den Pfadnamen ist 0L           */

/* In dieser Struktur werden Informationen zu den selektierten */
/* Files geführt.                                              */

typedef struct
{
    char    res;        /* Ist immer 0x36       */
    char    attr;       /* Fileattribut         */
    int     date;       /* Erstellungsdatum     */
    int     time;       /* Erstellungszeit      */
    long    size;       /* Länge des Files      */
    char    name[14];   /* Name + Extension     */
} FILEINFO;

extern  int gl_apid;    /* Application ID für menu_register() */

/* In diesen Arrays stehen die Adressen der Zeiger auf die Titel- */ 
/* zeilen der max. 4 DESKTOP - Fenster.                           */

char    **wt_oldtos[4] = {0x9C7A, 0x9CB2, 0x9CEA, 0x9D22};
char    **wt_blttos[4] = (0xC4EE, 0xC526, 0xC55E, 0xC596};


/* tos_version() ermittelt die Versionsnummer des TOS, indem es über  */
/* die Systemvariable _sysbase (0x4F2) auf den System Header Block    */
/* des TOS zugreift. In diesem ist das zweite Wort die Versionsnummer.*/

int tos_version()
{
    char    *sys_header;
    long    ssave;

    ssave = Super(0L); 
    sys_header = *SYS_BASE;
    Super(ssave);

    return (*((int*)(sys_header + 2)));
}

/* act_process() gibt einen Zeiger auf den PD des gerade aktiven      */
/* Prozesses zurück. Diesen Zeiger findet man in der GEMDOS-Variablen */
/* act_pd. Diese Variable liegt beim 'alten' TOS an der Adresse 602C. */
/* Das Blitter-TOS hält die Adresse im System Header Block fest.      */

long *act_process()
{
    char    *sys_header;
    long    **act_pd;
    int     version;
    long    ssave;

    if ((version = tos_version()) == 0x0102)
    /* Blitter-TOS */
    {
        ssave = Super(0L);
        sys_header = *SYS_BASE;
        act_pd = *((long***)(sys_header + 0x28)); 
        Super(ssave);
    }
    else if (version == 0x0100)     /* TOS vom 6.2.1986 */
    {
        act_pd = ((long**) 0x602C);
    }
    else
        return(NIL); 

    return (*act_pd);
}

/* desktop_tree() ermittelt die Adresse des Objektbaums des DESKTOPS.   */
/* Zeiger auf diesen Baum stehen an den Adressen 0xA0C4 ('altes' TOS),  */
/* bzw. 0xC942 (Blitter-TOS).                                           */

OBJECT *desktop_ob_tree()
{
    OBJECT  *tree;
    int     version;

    if ((version = tos_version()) == 0x0102)
    {
        tree = *((OBJECT**) 0xC942);
    }
    else if (version == 0x0100)
    {
        tree = *((OBJECT**) 0xA0C4);
    }
    else    /* Andere TOS-Versionen werden nicht unterstützt */ 
        return (NIL);

    return (tree);
}

/* wind_titlep gibt einen Zeiger auf die Titelzeile des Fensters */
/* <window> zurück.                                              */

char **wind_titlep(window)

int window;
{
    int     version;

    if ((version = tos_version()) == 0x0102) 
        return (wt_blttos[window]);

    else if (version == 0x0100)
        return (wt_oldtos[window]);
    else
        return (NIL);
}

/* 1st das DESKTOP aktiver Prozeß ?                                   */
/* Wenn ja, dann ist im PD als Textsegmentlänge (4. Langwort) 0L      */
/* eingetragen.                                                       */
/* (Ist das DESKTOP nicht aktueller Prozeß, so ist es auch nicht sehr */
/* sinnvoll, die selektierten Dateien erfragen zu wollen.)            */

int desktop()
{
    long    *help;

    if ((help = act_process()) == NIL) 
        return (FALSE); 
    else if (help[4] != 0L)
        return (FALSE); 
    else
        return (TRUE);
}

/* top_window ermittelt die Nummer des obersten Fensters. Diese   */
/* kann direkt als Parameter für die desk_xxxxx-Funktionen dienen */

int top_window()
{
    int     nr, dummy;

    wind_get(0,WF_TOP, &nr, &dummy, &dummy, &dummy); 
    return (nr);
}

/* desk_path() ermittelt den Pfadnamen des Fensters <wind> und  */
/* kopiert ihn nach <pname>.                                    */

int     desk_path(wind, pname)

int     wind;
char    *pname;
{
    char    *h;
    char    *pp, **ppp; /* PathPointer und Pointer_to_PathPointer */ 
            /* (Uugh, grausig einfallsreich !!) */
    wind -= 1;
    if (wind < 0 || wind > 3) 
        return (EINVWNR);

    if (!desktop())
        return (EDKTNAP);

    if ((ppp = wind_titlep(wind)) == NIL) 
        return (EOSVERS); 
    else if (*ppp == 0)
        return (ENOPATH);

    pp = *ppp;

    /* xtrcpy(s1, s2) unterscheidet sich nur dadurch von    */
    /* strcpy(s1, s2), als daß ein Zeiger auf das Ende von  */
    /* s1 zurückgegeben wird.                               */

    h=xtrcpy(pname, &pp[1]); /* Führendes Leerzeichen unterschlagen. */ 
    *(h-1) = '\0';           /* Ebenso das Leerzeichen am Ende.      */
    return (0);
}

/* desk_files() ermittelt sämtliche Files, die in dem Fenster <wind>  */
/* selektiert sind, kopiert Zeiger auf deren FILEINFO-Strukturen nach */
/* <list> und gibt die Anzahl der selektierten Files zurück.          */

int     desk_files(wind, list)

int         wind;
FILEINFO *list[];
{
    OBJECT  *tree;
    int     file, n=0;
    ICONBLK *icon;
    APPLBLK *appl;
    char    *ptr, s [80];

    wind -= 1;
    if (wind < 0 || wind > 3) 
        return (EINVWNR);

    if (!desktop())
        return (EDKTNAP);

    if ((tree = desktop_ob_tree()) == NIL) 
        return (EOSVERS);

    /* Window nicht existent oder leer ? */
    if ((file = tree[wind+2].ob_head) == -1)
        return(0); /* kein File selektiert ! */
    do
    {
        if (tree[file].ob_state & SELECTED)
        {
            switch (tree[file].ob_type)
            {
                case G_ICON:

                    icon = (ICONBLK*) tree[file].ob_spec; 
                    ptr = icon->ib_ptext - 10; 
                    list[n++] = (FILEINFO*)ptr;
                    break;

                case G_PROGDEF:

                    appl = (APPLBLK*)tree[file].ob_spec; 
                    ptr =(char*) (appl->ub_parm - 1); 
                    list[n++] =(FILEINFO*)ptr;
                    break;

                default: /* An sich überflüssig */

                    break;

                }
            }
            file = tree[file].ob_next;
    } while (file != wind+2);   /* Solange ob_next nicht auf die */
    return(n);                  /* Wurzel zurückzeigt */
}

/* Eine beispielhafte Anwendung. Das Accessory tut nichts weiter, als   */
/* zu sämtlichen (in allen Fenstern) selektierten Files den Namen samt  */
/* Pfad in aufeinanderfolgenden Dialogboxen auszugeben.                 */
/* Hier wären sicherlich sinnvollere Anwendungen angebracht, wie zum    */
/* Beispiel die angewählten Files auszudrucken oder zu (de)codieren.    */

main()
{
    char        path[80], message[80];
    FILEINFO    *files[40];
    OBJECT      *desk;
    int         menu_id, tw, msg[8], n, i, j;
    long        *help;

    appl_init();
    menu_id = menu_register(gl_apid, "  DESK-EXPANDER") ;

    while (TRUE)
    {
        evnt_mesag (msg);

        if (msg[0] == AC_OPEN && msg[4] == menu_id)
        {
            if (!desktop())
                form_alert(1,"[1][DAS GEHT HIER NICHT][OK]");
            else
            {
                for (i=1; i<5; i++)
                {
                    if ((n = desk_files(i, files)) < 0 || desk_path (i, path) < 0)
                    {
                        sprintf(message,"[1][Fenster #%d: Fehler #%d][SCHADE]", i, n); 
                        form_alert (1, message);
                    }
                    else
                    {
                        for(j=0; j<n;j++)
                        {
                            sprintf(message,"[0][%s%s][OK]", path, files[j]-> name);
                            form_alert (1, message);
                        }
                    }
                }
            }
        }
    }
}

Thorsten Beuck
Aus: ST-Computer 09 / 1989, Seite 85

Links

Copyright-Bestimmungen: siehe Über diese Seite