ST-Ecke: Bäumchen wechsle dich

In diesem Monat wollen wir uns wieder einmal mit der Objektstruktur des AES beschäftigen. Dabei wird besonders das Element ob_type unter die Lupe genommen, denn mit ihm kann man die sogenannten ’extended ob_types’ realisieren. Außerdem soll auf eine Eigenschaft des HIDETREE-Flags eingegangen werden.

Wie Sie sicherlich wissen, enthält die Objektstruktur der AES-Objekte verschiedene Einträge. So ist dort die Koordinate oder Breite eines Objektes vorhanden (siehe Bild 1). Ein weiterer Eintrag ist das OB_TYPE-Element, das den Objekttyp wie zum Beispiel G_TEXT, G_ICON oder ähnliches definiert. Nun ist folgende Tatsache interessant und nach einigem Nachforschen auch gesichert: Der Eintrag ob_type ist zwei Bytes lang, von denen aber nur das untere Byte vom AES (zum Beispiel in objc_draw()) benutzt wird - nicht nur das: AES blendet dieses obere Byte bei seiner Arbeit sogar aus, so daß Werte an dieser Stelle nicht stören. Dies hat einen riesigen Vorteil für uns, denn wir können dadurch in diesem Byte Zusatzinformationen unterbringen, ohne daß wir Bedenken haben müssen, daß dieses die 'Gedanken’ des AES durcheinanderbringen könnten.

Eine Eigenheit des HIDETREE-Flags

Ich möchte nun einige Beispiele für die Nutzung dieses freien Bytes anführen: Ich nehme an, daß Ihnen die Bedeutung des HIDETREE-Flags bekannt ist, das dazu dient einige Objekte beim Zeichnen einer Box auszublenden. Angenommen Sie wollen aufgrund einer Aktion des Benutzers dafür sorgen, daß ein paar Ihrer Objekte beim Zeichnen nicht mehr mitgezeichnet werden. Dies können BUTTONs, BOXen oder auch TEXTe sein. Nun ist beim Verschwindenlassen von editierbaren Texten folgendes zu beachten: Wird das HIDETREE-Flag gesetzt und das EDITABLE-Flag bleibt, so wird man schnell feststellen, daß der Text zwar nicht mehr gezeichnet wird, aber der Cursor immer noch an dieser Stelle erscheint - zu allem Unglück kann man diesen Text auch noch editieren, nur daß der eingegebene Text unsichtbar ist (Passwort-Freaks hergehört, denn diesen Effekt kann man sich bei einer PASS-WORT-Eingabe auch gezielt zunutze machen). Die einzige Lösung ist also, daß beim Setzen des HIDETREE-Flags in einem editierbaren Objekt das EDITABLE-Flag gelöscht werden muß, was aber wieder einen Nachteil mit sich bringt. Wollen wir alle Objekte wieder erscheinen lassen, müssen wir wissen, welche Objekte einmal EDI-TABLE waren. Soll dieser Vorgang in einer Schleife ablaufen, bietet sich nun folgende Vorgehensweise an: Lassen wir in der Schleife das Objekt, das editierbar ist, dadurch verschwinden, daß wir das HIDETREE-Flag Flag setzen und das EDITABLE-Flag löschen, dann führen wir noch folgenden Vorgang durch: Wir setzen ein (beliebiges) Bit im oberen Bit des ob_types des editierbaren Objektes. Sollen nun später die Objekte wieder sichtbar gemacht werden, so überprüft man zunächst ob es sich um einen Text handelt und ist dies der Fall überprüft man zusätzlich, ob das oben von Ihnen benutzte Bit gesetzt ist. Dies sagt Ihnen, daß dieses Objekt ehemals editierbar war. Daraufhin setzen Sie wieder das EDITABLE-Flag im Objekt und löschen das Bit im oberen Byte des ob_type. Sie sehen, daß diese äußerst elegante Methode sehr viel Arbeit sparen kann, da man sich nicht merken muß, welches Objekt EDITABLE war, sondern dies einfach in der Objektstruktur zusätzlich vermerkt hat.

/* Objektstruktur */

typedef struct object 
{
        int             ob_next; /* Zeiger zum nächsten Objekt */
        int             ob_head; /* Kopf des Kindes */
        int             ob_tail; /* Ende des Kindes */
        unsigned int ob_type;   /* Typ des Objektes */
        unsigned int ob_flags;  /* Flags */
        unsigned int ob_state;  /* Status des Objektes */
        char            *ob_spec; /* objektspezifischer Eintrag */
        int             ob_x;   /* X-Koordinate */
        int             ob_y;   /* Y-Koordinate */
        int             ob_width;  /* Breite des Objektes */
        int             ob_height; /* Höhe des Objektes */
} OBJECT;

Bild 1: Die Objektstruktur des ST

Um die Verfahrensweise mit einer zusätzlichen Information zu verdeutlichen, schauen Sie sich bitte Listing 1 an. Zunächst werden alle Objekte, die editierbar sind, mit einer weiteren Information in dem ob_type-Element versehen. Diese Information bringen wir im ersten Bit des oberen Bytes von OB_TYPE unter und nennen es WAS_EDIT, also 'war editierbar’.

Wenn Sie nun beim Verstecken von Boxen auch das EDITABLE-Flag ausblenden, um ein Erscheinen des Cursors zu vermeiden, so wird das WAS_EDIT-Flag nicht gelöscht. Wenn die Box später wieder eingeblendet wird, so schauen wir bei jedem Objekt in ob_type nach, ob das WAS_EDIT-Flag gesetzt ist und rekonstruieren daraufhin das EDITABLE-Flag. In dem Programmbeispiel ist dies ganz gut zu erkennen. Noch eine Anmerkung, die Sie auch im Quellcode entdecken: Natürlich wird man beim Verstecken eines Baumteils nicht alle EDIT-Felder ’ausschalten’, wie dies in unserem Programmbeispiel geschieht. Vielmehr sollten nur die Textfelder innerhalb der verschwundenen Box ausgeschaltet werden. Dazu ist aber eine Routine erforderlich,d ie sich innerhalb des Baumes hin und her bewegt. Eine solche Routine habe ich aufgrund der Übersichtlichkeit in diesem Programm weggelassen, werde sie aber zu gegebenem Zeitpunkt nachliefern. Dann wollen wir auch näher auf den Algorithmus eingehen.

Bild 2: Die Boxen des Listings

Eine runde Taste oder eine abgerundete Box

Ein weiterer Anwendungsfall, von dem der eigentliche Name EXTENDED OB_TYPE - erweiterter Objekttyp - abstammt, ist der folgende: In früheren Ausgaben der ST-Computer wurde schon auf den Objekttyp G_USERDEF oder, was das gleiche ist, G_PROGDEF hingewiesen. Findet objc_draw() einen Eintrag mit diesem Objekttyp, so wird der ob_spec-Eintrag als Adresse einer Routine auf gefaßt, in die es dann einspringt. Auf diese Weise könnte man zum Beispiel eine Melodie erklingen lassen, sobald eine Dialogbox gezeichnet würde. Ein viel interessanterer Fall ist aber eine neue Objektart zu kreieren. Wie wäre es zum Beispiel mit einer Box mit abgerundeten Ecken oder vielleicht ein runder Button - Macintosh läßt grüßen -? Das Prinzip der Realisierung ist recht einfach: Das unter Byte von ob_type enthält den Eintrag G_USERDEF und ob_spec die Adresse der Routine die unsere neuen Objekte zeichnet und verwaltet. Wird nun unsere Routine angesprochen, so überprüft diese den oberen Eintrag von ob_type, in den wir unsere neuen Objekttypen - natürlich in Zahlen kodiert - hineingeschrieben haben. Beachten Sie aber bitte, daß in ihrer USERDEF-Routine keine Einsprünge in das AES vorhanden sind, da das AES nicht ’reentrant’ ist, was bedeutet, daß das AES nicht zweifach benutzt werden darf und da Ihre USERDEF-Routine von AES aufgerufen wurde, kann diese AES nicht wiederum aufrufen. Eine weitere Einschränkung ist, daß der Stack des AES nicht allzu groß ist, so daß Sie die Menge Ihrer Variablen stark einschränken sollten.

Trotzdem bietet G_USERDEF im Zusammenhang mit dem leeren Byte in ob_type eine interessante Erweiterung der Objektstruktur.

Es gibt noch viele Möglichkeiten, das leere Byte von ob_type auszunutzen, zum Beispiel wäre es denkbar, in einer gewissen Weise dies in einer eigenen Form_do-Routine (siehe ST-ECKE) zu verwerten - der Kreativität sind kaum Grenzen gesetzt.

Zum Schluß noch ein Wort in eigener Sache: Wir freuen uns über jeden Brief, der nach einem Hilfeschrei in Sachen Programmierung aussieht und dem wir helfen können. Deshalb bitten wir Sie uns nach wie vor Ihre Probleme mitzuteilen. Wir haben allerdings die Erfahrung gemacht, daß bestimmte Probleme sehr speziell sind und sogar teilweise recht einfach in einem persönlichen Gespräch zu klären wären. Deshalb möchte ich Sie bitten, beim Schreiben eines Briefes Ihre TELEFONNUMMER mit anzugeben. Dadurch können wir Ihnen schnell und einfach helfen. Jetzt wünsche ich weiterhin kreatives Programmieren - bis zum nächsten Monat.

Ihr Stefan Höhn

#include <obdefs.h>
#include <osbind.h>
#include "hide.h"

#define WAS_EDIT 0x0100 /* erstes Bit im oberen Byte -> für ob_type */

/* Die Definitionen OB_FLAGS und OB_TYPE sind wegen der besseren 
   Übersicht im Listing erstellt worden */

long pt_chr(); /* Deklaration für alle Routinen */

    /* Dies sind die Strings, die in der Box vorkommen */

char #strings[] = {
"Beispielbox zu HIDETREE",
"Verstecken",
"---------",
"Edit1: ________",
"XXXXXXXX",
"Edit2: ________",
"XXXXXXXX",
"Edit3: ________",
"XXXXXXXX",
"Button 1",
"Ausgang"};

    /* Dies ist die Struktur der editierbaren Objekte */

TEDINFO ted[] = {
strings[2], strings[31, strings[4], 3, 6, 0, 0x1180, 0x0, -1, 9,16, 
strings[5]. strings[6], strings[7], 3, 6, 0, 0x1180, 0x0, -1, 9,16, 
strings[8], strings[9], strings[10], 3, 6, 0, 0x1180, 0x0, -1, 9,16};

        /* Der Objektbaum */

OBJECT tree[] = {
-1, 1,  8, G_BOX,    NONE,   OUTLINED, 0x21100L,    0,0, 424,206,
2, -1, -1, G,STRING, NONE,     NORMAL, strings[0],  104,16, 184,16,
3, -1, -1, G,BUTTON, 0x5,      NORMAL, strings[1],  104,176, 88,16,
8,  4,  7, G_BOX,    NONE,     NORMAL, 0xFF1101L,    64,48, 272, 100,
5, -1, -1, G_FTEXT,  EDITABLE, NORMAL, &ted[0],      72, 16, 120,16,
6, -1, -1, G_FTEXT,  EDITABLE, NORMAL, &ted[1],      72, 32, 120,16,
7, -1, -1, G_FTEXT,  EDITABLE, NORMAL, &ted[2],      72,48, 120,16,
3, -1, -1, G_BUTTON, 0x5,      NORMAL, strings[11], 104,80, 72,16,
0, -1, -1, G_BUTTON, 0x25,     NORMAL, strings[12], 208,176,72,16);

main()
{
    int exit_obj,i;
    int versteckt; /* Flag: Zeigt an, ob Box sichtbar */

    appl_init(); /* Applikation initialisieren */

/*  Im folgenden werden alle editierbaren Objekte mit einem
    zusätzlichen Flag versehen, an dem man, selbst nach dem Löschen des 
    EDITABLE-Flags, erkennen kann, daß sie früher einmal editierbar waren.

    Anmerkung: Die Schleife ist eine einfach uerständliche. aber
               unelegante Lösung, da man die Länge des Baums kennen 
               muß. Aufwendiger, aber besser, wäre eine Routine die 
               sich vom Anfang bis zum letzten Objekt im Baum (!) 
               durcharbeiten würde. Eine solche Routine wird in eine 
               der nächsten ST-Ecken ueröffentlicht. */

    for (i = 0; i<9: i++)
    {
        if (tree[i].ob_flags & EDITABLE) 
            tree[i].ob_type |= WAS_EDIT;
    }

    box_draw(tree,320,200,20,20); /* Zeichnen der Dialogbox */

    while (exit_obj!=MEXIT) /* Bis die Taste Ausgang gedrückt ist */
    {
        exit_obj=form_do(tree,0); /* Dialog */
        tree[exit_obj].ob_state &= ~SELECTED; /* Deselektieren der Taste */ 
        objc_update(tree,exit_obj,MAX_DEPTH); /* Neuzeichnen */

        if (exit_obj==HIDE) /* Wenn Taste HIDE gedrückt */
        {
            if (!versteckt) /* Box ist sichtbar */
            {

/* Die folgende Schleife, die alle Editable-Flags löscht ist auch nicht 
   die eleganteste Version, da sie alle (!) Textfelder bearbeitet. 
   Besser wäre eine Bearbeitung nur innerhalb der Box. */

                for (i=0; i<9; i++)
                    if (tree[i].ob_flags & EDITABLE) 
                        tree[i].ob_flags &= ~EDITABLE;

                hide_obj(tree,MBOX);    /* Box ausblenden */
                versteckt=1;            /* merken, daß Box versteckt ist */
                strcpy(pt_chr(tree,HIDE),"Einblenden"); /*Text in Taste ändern */
            }
            else /* Box ist versteckt */
            {
                /* Die folgende Schleife setzt das EDITABLE-Flag aufgrund des Merkers WAS_EDIT in ob_type.
                Für diese Schleife gilt das Gleiche wie das oben Erwähnte, */

                for (i=0; i<9; i++)
                    if (tree[i].ob_type & WAS_EDIT) 
                        tree[i].ob_flags |= EDITABLE; 
                unhide_obj(tree,MBOX); /* Mache Box sichtbar */
                versteckt=0; /* merke dir das */
                strcpy(pt_chr(tree,HIDE),"Verstecken"); /* Text ändern */
            }
        }
        objc_update(tree,BOX,MAX_DEPTH); /* Zeichne ganze Box neu */
    }
    box_undraw(tree,320,200,20,20); /* Box auf dem Bildschirm löschen */
    appl_exit(); /* Applikation beenden */
}

/*********************************************************/
/* Diese Routine berechnet die Adresse eines Textstrings */
/* in STRINGS oder in BUTTONs. also Objekten die keine   */
/* TEDINFO besitzen.                                     */
/*********************************************************/

long pt_chr(baumob_ind) 
long baum; 
int ob_ind:
{
    return((long)(((OBJECT*)(baum+24*ob_ind))->ob_spec));
}

/*********************************************************/
/* Diese Routine setzt das HIDETREE-Flag in einem        */
/* bestimmten Objekt und löscht bei einem Textfeld       */
/* das EDITABLE-Flag.                                    */
/*********************************************************/

hide_obj(baum, ob_ind) 
long baum; 
int ob_ind;
{
    OBJECT* ob;
    
    ob= (OBJECT*) (baum+24*ob_ind); /* Adresse des Objekts errechnen */
    ob->ob_flags |= HIDETREE;       /* HIDETREE-Flag setzen */

    if (ob->ob_flags & EDITABLE)    /* falls Objekt editierbar, */
        ob->ob_flags &= ~EDITABLE;  /* EDITABLE-Flags ausblenden */
}

/*********************************************************/
/* Diese Routine löscht das HIDETREE-Flag und            */
/* rekonstruiert das EDITABLE-Flag aufgrund des          */
/* verwendeten WAS_EDIT-Flags in ob_type                 */
/*********************************************************/
unhide_obj(baum, ob_ind) 
long baum; 
int ob_ind;
{
    OBJECT* ob;

    ob= (OBJECT*) (baum*24*ob_ind); /* Adresse des Objekts errechnen */
    ob->ob_flags &= ~HIDETREE;      /* HIDETREE-Flag löschen */
    if (ob->ob_type & WAS_EDIT)     /* falls Objekt früher editierbar.*/
        ob->ob_flags |= EDITABLE;   /* Objekt wieder editierbar machen */

}

Listing 1: Ein Beispiel zur Verarbeitung von HIDETREE



Aus: ST-Computer 12 / 1987, Seite 78

Links

Copyright-Bestimmungen: siehe Über diese Seite