Trennungsschmerzen: Tear-Off-Menüs unter GEM

Pull-down-Menüs, Pop-up-Menüs, Drop-down-Menüs, Systemmenüs, Submenüs, hierarchische Menüs, Menüzeilen, Fenstermenüs, kontextsensitive Menüs, Menüs ä la Carte und jetzt auch noch Tear-Off-Menüs - wo soll das nur hinführen?

Obwohl man diese Frage wahrscheinlich zu Recht stellen kann, soll hier die für unseren geliebten ATARI neue Menüvariante vorgestellt werden. Man höre und staune, sie hat sogar bei dem überall abgekupferten und allseits geliebten Windows noch keinen Einzug gehalten. Der einzige Rechner, der bisher über die sogenannten Tear-Off-Menüs verfügt - und das auch noch nicht so lange - ist wieder einmal der Apple Macintosh.

Tränen und trennen

Die genannten Menüs haben dabei jedoch nichts mit irgendwelchen Tränen zu tun, die mancher Anwender möglicherweise vergießt, weil der ATARI-Markt in letzter Zeit so stark geschrumpft ist, sondern vielmehr handelt es sich dabei um „Abreiß“-Menüs. Solche Menüs werden einfach in der Menüzeile angeklickt, „abgerissen“ und auf dem Bildschirm an beliebiger Stelle neu plaziert, wo sie dann wie ein Fenster jederzeit zur Verfügung stehen. Die Originalmenüs stehen währenddessen aber natürlich auch weiterhin noch an ihrem Originalplatz, nämlich der Menüzeile, zur Verfügung. Dies hat auch den Effekt, daß jedes Menü nahezu beliebig oft abgerissen werden kann; lediglich die Anzahl der vom GEM zur Verfügung gestellten Fenster stellt eine Obergrenze dar.

Sinnvoll sind solche „Abreiß“- oder eben „Tear-Off“-Menüs, wenn man mit einem größeren Bildschirm arbeitet und nicht jedes Mal die Maus mühsam an den oberen Bildschirmrand zur Menüzeile bewegen möchte. Da ist es dann schon wesentlich einfacher, häufig benötigte Menüs einfach von der Menüzeile abzureißen und in der Nähe des Arbeitsbereichs zu plazieren, so daß die benötigten Befehle nicht erst in der Menüzeile gesucht werden müssen, sondern auf einen Klick zur Verfügung stehen.

Um solche Tear-Off-Menüs in GEM-Programmen zu implementieren, ist gar nicht mal ein solch großer Aufwand nötig, wie man jetzt vielleicht vermuten könnte. Tatsächlich hält sich der benötigte Aufwand sogar in gewissen Grenzen, wie auch an dem nachfolgenden Demoprogramm zu sehen ist. Der größte Teil der Arbeit entfällt dabei auf das „Abreißen“ und Öffnen der Menüs in einem Fenster, die restliche Verwaltungsarbeit ist dagegen vergleichsweise gering.

Trennungskosten

Um nun das Abreißen der Menüs möglich zu machen, ist ein kleiner (legaler) Eingriff ins Betriebssystem notwendig, der auf allen getesteten Betriebssystemversionen auch problemlos funktioniert. Lediglich in der allerneuesten (bzw. alten) MultiTOS-Version mit der AES-Versions-Nummer 4.1 gibt es kleine Schwierigkeiten. Hier können die Menüs zwar auch immer noch abgerissen werden, allerdings ist die Bedienung hier leicht unterschiedlich. Dazu jedoch später mehr. Zu testen ist das nachfolgend beschriebene Verfahren allerdings noch unter MagiC, das mir nicht zur Verfügung stand.

Die hier vorgestellte Methode ist also -obwohl „legal“ - nicht so ganz hundertprozentig. Sie soll jedoch auch nur einen Denkanstoß geben, Tear-Off-Menüs auf eine bessere Art und Weise zu implementieren, damit auch zukünftige Betriebssystemversionen (falls es ja doch noch welche geben sollte) damit vollständig zurechtkommen. Wer eine entsprechende Lösung findet, darf sich gerne bei mir melden.

Der Objektbaum der im Demoprogramm verwendeten Menüzeile. Die Verknüpfungen innerhalb des „About“-Menüs (BOX 6) werden aus Platzgründen nicht dargestellt.

Getrennte Welten

Auf dem Macintosh funktionieren Tear-Off-Menüs folgendermaßen: Nach Aufklappen eines Menüs kann man bei weiterhin gedrückter Maustaste einen Menüeintrag dadurch auswählen, daß man die Maustaste über dem entsprechenden Eintrag losläßt. Um ein Menü abreißen zu können, kann man also nicht einfach die Taste gedrückt halten und das Menü verschieben, denn der Rechner kann beide Aktionen natürlich nicht voneinander unterscheiden. Deshalb muß man den Mauszeiger beim Macintosh über das Menü hinausziehen, wodurch es dann von der Menüzeile abgelöst wird und auf dem Bildschirm positioniert werden kann. Diese Vorgehens weise erscheint zwar nicht unbedingt ganz logisch, läßt sich auf dem Mac infolge der Menüauswahl mit gedrückter Maustaste aber wohl nicht anders lösen.

Der Aufbau der Menüzeile als „Realbild“

Auf dem ATARI dagegen kann man die gewohnte Vorgehensweise verwenden, da hier ein Menüeintrag mit einem normalen Mausklick ausgewählt wird: Man klickt ein ausgeklapptes Menü an und verschiebt es bei gedrückt gehaltener Maustaste. Bei Loslassen der Taste wird das Menü dann an der ausgewählten Position geöffnet und in einem Fenster dargestellt.

Das Problem bei der Sache ist jedoch, daß die Menübehandlung vollständig vom Betriebssystem abgehandelt wird und man eigentlich keine Möglichkeit hat, in sie einzugreifen. Um die oben geschilderte Vorgehens weise realisieren zu können, müßte man nämlich bei Anklicken eines Menüeintrags feststellen können, wie lange die Maustaste gedrückt gehalten wird; dauert der Klick länger als gewöhnlich, ist das Menü „abzureißen“. Leider läßt sich das auf dem ATARI in dieser Form nicht verwirklichen, da das angewählte Menü sofort nach dem Klick eingeklappt wird, egal wie lange die Maustaste gedrückt gehalten wird. Außerdem verbleibt das Betriebssystem anschließend solange in der Menübehandlung, bis die Maustaste wieder losgelassen worden ist.

Abtrennung

Aus diesem Grund hat sich nach einigen Versuchen die folgende Vorgehensweise als am sinnvollsten und einfachsten zu realisieren herausgestellt: Statt der linken Maustaste wird zum Abreißen eines Menüs die rechte Taste verwendet, die vom Betriebssystem (bis AES-Version 4.1) völlig ignoriert wird. Mit dieser Taste läßt sich nach einem kleinen Eingriff in den Maus-Button-Interrupt das oben beschriebene Verfahren verwirklichen. Dazu wird im Interrupt überprüft, ob die rechte Maustaste in einem ausgeklappten Menü betätigt wurde. Falls dies der Fall ist, wird ein normaler Mausklick mit der linken Taste simuliert, um das AES dazu zu bewegen, das Menü wieder einzuklappen. Danach muß im Programm nur noch die vom AES gesendete MN_SELECTED-Meldung abgefangen werden. Anschließend kann man dann auf gewohnte Weise einen verschiebbaren Rahmen zeichnen und an der Endposition ein Fenster mit dem Menüinhalt öffnen; dadurch ist es auch möglich, ein Menü beliebig oft von der Menüzeile abzureißen.

Wie oben schon angedeutet, funktioniert die beschriebene Methode jedoch nur bis AES-Version 4.1 auf diese Art und Weise. Ab der genannten Version wird offensichtlich auch die rechte Maustaste innerhalb der Menübehandlung ausgewertet - warum auch immer. Jedenfalls hat das den Effekt, daß auch nach dem Schließen eines Menüs das Betriebssystem solange in der Menübehandlung verbleibt, wie die rechte Maustaste gedrückt bleibt. Demzufolge läßt sich obiges Verfahren leider so nicht vollständig anwenden und die Bedienung der Tear-Off-Menüs muß hier konsequenterweise geändert werden. Deshalb ist ab AES-Version 4.1 das abzureißende Menü mit der rechten Maustaste ganz normal anzuklicken (und die Maustaste anschließend wieder loszulassen). Dann wird der bereits oben erwähnte Rahmen gezeichnet und kann mit einem erneuten Mausklick (linke oder rechte Taste, ganz nach Geschmack) positioniert werden.

Natürlich ließe sich dieses Verfahren auch für alle anderen AES-Versionen so implementieren, um eine konsequente und AES-unabhängige Bedienung zu gewährleisten. Aber wer weiß, ob in der nächsten AES-Version nicht alles schon wieder ganz anders ist... Jedenfalls sind im Beispiel-Programm abhängig von der AES-Version beide Möglichkeiten enthalten, um die Vorgehens weise für jedes Verfahren zu demonstrieren. So kann sich jeder Anwender seine eigene Meinung dazu bilden; meines Erachtens ist die erste beschriebene Methode jedoch logischer und die Handhabung intuitiver.

Umwandlung und Umrechnung eines Menüs aus der Menüzeile in ein Tear-Off-Menü
Das Demoprogramm mit einer Vielzahl abgerissener Menüs

Trennungsaufwand

Wenn man nun Tear-Off-Menüs in eigenen Programmen einsetzen möchte, so sollte man sich bewußt sein, daß dies bei der Programmierung etwas mehr an Aufwand als üblich bedeutet. Da jedes Menü theoretisch beliebig oft von der Menüzeile abgerissen werden kann, müssen bei Änderung eines Menüeintrags, egal ob es sich dabei um den Menütext oder irgendwelche Flags handelt, diese in allen offenen Tear-Off-Menüs ebenfalls geändert und neu gezeichnet werden! Weiterhin muß natürlich beim Anklicken eines Menüeintrags in einem Tear-Off-Menü die entsprechende Funktion ausgeführt werden, d.h., man hat plötzlich nicht nur mehr MN_SELECTED-Meldungen auszuwerten, sondern muß sich auch noch um jeden Klick in jedes Tear-Off-Menü kümmern!

Ein weiterer Punkt ist schlußendlich noch das Abreißen des „About“-Menüs, also des Menüs, das ganz links als erstes in der Menüzeile steht. Falls man das Abreißen dieses Menüs zuläßt, wird man nämlich ganz schnell feststellen, daß es keinerlei Möglichkeit gibt, im abgerissenen Tear-Off-Menü etwaig aufgeführte Accessories oder in MultiTOS aufgelistete andere Programme aufzurufen. Aus diesem Grund darf bei Abreißen des „About“-Menüs einzig und allein der erste Eintrag über das eigene Programm abgerissen werden können. Dieser Punkt wurde in den vorliegenden Routinen jedoch bereits berücksichtigt, so daß man sich darum nicht mehr zu kümmern braucht.

Trennungsmerkmale

Außerdem tritt durch die Benutzung durch Tear-Off-Menüs ein weiteres Problem auf, dessen man sich vielleicht zunächst gar nicht bewußt ist, das man aber bereits bei der Programmplanung berücksichtigen muß. Immer dann, wenn in einem Programm mit mehreren Fenstern gearbeitet wird, muß man bei Ausführung einer Funktion wissen, auf welches Fenster bzw. welchen Fensterinhalt sie angewendet werden soll und ob die Funktion auf dieses Fenster überhaupt anwendbar ist. Meistens ist so etwas ganz einfach festzustellen, da solch eine Funktion in den meisten Fällen immer auf das gerade aktive (Arbeits-)Fenster angewendet wird.

Wenn man jedoch mit Tear-Off-Menüs arbeitet, ist in dem Moment, in dem man einen Tear-Off-Eintrag anklickt, das Tear-Off-Menü normalerweise das aktive Fenster! Deshalb muß man in solchen Fällen ständig mitprotokollieren, was das zuletzt aktive Bearbeitungsfenster war oder sich je nach Bedarf eine andere Methode dazu einfallen lassen. Eine Möglichkeit, um dieses Problem so weit wie möglich zu umgehen, ist die im Demoprogramm bereits vorgenommene Unterdrückung der WM_TOPPED-Meldung für Tear-Off-Menüs. Dadurch können sie auch im Hintergrund bedient werden, und das Arbeitsfenster bleibt aktiv. Leider funktioniert dies jedoch erst ab der AES-Version 3.31, so daß man für frühere AES-Versionen doch wieder nach Ausweichmöglichkeiten suchen muß, wenn man kompatibel bleiben will.

Nach der Untersuchung der Problematik im Zusammenhang mit Tear-Off-Menüs jetzt zu den Details der Implementierung. An dieser Stelle muß direkt gesagt werden, daß aus Platzgründen nicht alle Listings im Heft abgedruckt werden können. Hier sind deshalb nur die wichtigsten und zum Verständnis notwendigen Funktionen zu finden. Der komplette Programmcode und das lauffähige Demoprogramm können jedoch der Mega-Disk zum Heft entnommen werden.

Hauptsache getrennt

In der Hauptroutine des Programms (Datei „TEAROFF.C“), die wie üblich main() heißt, sind nachfolgende Dinge zu beachten. Zunächst muß am Anfang des Programms mit der VDI-Funktion vex_butv() die neue Maus-Button-Behandlung installiert werden. Entsprechend muß sie dann am Ende des Programms mit der gleichen Funktion auch wieder entfernt werden.

Weiterhin ist die evnt_multi()-Schleife nicht ganz unwichtig. Um Tear-Off-Menüs bearbeiten zu können, werden die Flags MU_MESAG, MU_BUTTON und MU_TIMER benötigt; sie müssen also auf jeden Fall immer gesetzt sein.

Der Timer wird dazu benötigt, in regelmäßigen Abständen die globale Variable tear_menu abzufragen. Falls im Interrupt ein Tear-Off-Menü angeklickt wurde, wird hier der Objektindex des angeklickten Menüs hinterlegt. Anschließend muß dann der oben bereits erwähnte Rahmen gezeichnet, das Menü auf dem Bildschirm plaziert und als Fenster geöffnet werden.

Die Messages müssen aus zwei Gründen abgefragt werden. Zum einen müssen natürlich gegebenenfalls abgerissene Tear-Off-Menüs gezeichnet oder restauriert werden. Zum anderen muß die beim Ab-reißen eines Menüs vom AES erzeugte MN_SELECTED-Meldung abgefangen werden; in diesem Fall darf nur der Menütitel zurückgesetzt werden.

Schließlich müssen natürlich auch noch die Mausklicks ausgewertet werden, damit die abgerissenen und jetzt in Fenstern stehenden Tear-Off-Menüs benutzt werden können.

Wertstofftrennung

Die zu installierende Maus-Button-Routine ist der Einfachheit halber zum Teil in Assembler programmiert und in der Datei „MOUSEVEC.S“ und Listing 1 enthalten. Hier wird überprüft, ob die rechte Maustaste innerhalb eines ausgeklappten Menüs erfolgt. Falls ja, wird ein Mausklick mit der linken Maustaste simuliert, um das Betriebssystem dazu zu veranlassen, das Menü zu schließen. Danach wird dann ganz normal mit einem Druck der rechten Maustaste die Funktion beendet.

Die Überprüfung, ob der Mausklick in einem aufgeklappten Menü erfolgt ist, geschieht in der C-Routine maus_in_menu() in der Datei „TEAROFF.C“ bzw. Listing 2. Um die Funktionsweise dieser Funktion verstehen zu können, muß man sich über den Aufbau einer Menüzeile unter GEM im klaren sein. Der Objektbaum einer Menüzeile ist nämlich zweigeteilt (siehe Abbildung 1). Abbildung 2 zeigt den Aufbau derselben Menüzeile, die auch im Demoprogramm verwendet wird, noch einmal in „Realdarstellung“.

Im linken Teil des Objektbaums werden die Menütitel verwaltet, im rechten Teil die dazugehörigen Menüs. Ein offenes Menü kann nur daran erkannt werden, daß der Menütitel selektiert ist. Deshalb muß im linken Teilbaum nach solch einem Titel gesucht und anschließend das zugehörige Menü im rechten Teilbaum ermittelt werden. Nachdem so das offene Menü ermittelt wurde, müssen dessen Koordinaten berechnet werden, um festzustellen, ob der Mausklick auch tatsächlich innerhalb des Menüs ausgeführt wurde. Für die Ermittlung der Mauskoordinaten werden dabei die Line-A-Variablen benutzt. Ist der angeklickte Menüeintrag nicht abgeschaltet („Disabled“), so muß dies noch vermerkt werden, damit später im Programm die vom AES versandte MN_SELECTED-Meldung nicht fälschlicherweise ausgewertet wird.

Arbeitstrennung

Die nächste wichtige Routine ist die Funktion tear_off() (Datei „TEAROFF.C“ bzw. Listing 3), in der das Menü abgerissen und als Fenster geöffnet wird. Hier wird zunächst entsprechend des angeklickten Menüs ein Rechteck auf den Bildschirm gezeichnet und anschließend solange verschoben, bis es mit der Maus durch Tastendruck bzw. Loslassen der Taste (je nach AES-Version, s.o.) plaziert wird. Leider kann dazu nicht die AES-Funktion graf_dragbox() benutzt werden, da sie lediglich die linke Maustaste aus wertet. Hinzu kommt noch, daß, wie oben beschrieben, je nach AES-Version ein anderes Verfahren eingesetzt werden muß. Aus diesen Gründen muß das Verschieben des Rahmens also selbst programmiert werden. Dabei handelt es sich jedoch um ein konventionelles Verfahren, weshalb hier nicht weiter darauf eingegangen werden soll; lediglich die zwei AES-Versionsabfragen sind zu beachten.

Nachdem jetzt das Rechteck mit der Maus plaziert wurde, wird eine Kopie des entsprechenden Teils des Menüobjektbaums angelegt und die einzelnen Objekte dem neuen Baum entsprechend auch neu verkettet. Abbildung 3 zeigt die Vorgehensweise einer solchen Umwandlung. Das „About“-Menü wird gegebenenfalls so verkleinert, daß nur der erste Eintrag angewählt werden kann. Anschließend werden dann nur noch ein Fenster mit der entsprechenden Größe geöffnet und die notwendigen Verwaltungsinformationen gespeichert.

Damit wären jetzt auch schon die zentralen Funktionen zur Tear-Off-Behand-lung abgehandelt. Die restlichen, hier nicht besprochenen Funktionen sollten eigentlich leicht verständlich sein und weiter keine Probleme bereiten, zumal sie sowieso nur für das Demoprogramm benötigt werden.

Trennungsgründe

Das Programm selbst wurde mit Pure C 1.1 und dem Pure Assembler entwickelt, sollte aber auch leicht mit anderen Compilern übersetzt werden können. Das fertig compilierte Programm, das inklusive des kompletten Quelltextes auch auf der Diskette zum Heft enthalten ist, öffnet nach dem Start lediglich eine Menüzeile, in der die Menüs mit dem beschriebenen Verfahren beliebig oft abgerissen werden können. Eine Obergrenze stellt nur die vom AES zur Verfügung gestellte Fensteranzahl dar. Abbildung 3 zeigt das Programm mit einer Anzahl abgerissener Menüs. Bleibt nur noch zu hoffen, daß Tear-Off-Menüs demnächst in vielen Applikationen zu finden sind.


; Neue Maus-Button-Routine:
; Feststellen, ob der Mausklick in einem 
; Menü erfolgte.
; (c)1995 by MAXON-Computer 
; Autor: Uwe Hax

_m_newvec:
        cmp         #2,d0           ;rechte Taste?
        beq         _m_next         ;ja
        move.l      _m_oldvec,a1    ;nein
        jsr         (a1)
        rts

_m_next:
        movem.l     d0-a6,-(sp)
        jsr         mouse_in_menu   ;Maus in Menü?
        movem.l     (sp)+,d0-a6

        move.l      _m_oldvec,a1    ;Original-Routine noch aufrufen
        tst         tear_menu       ;Ein Menü an Mausposition? 
        bmi         _m_end          ;nein, fertig

        move        #1,d0
        jsr         (a1)            ;ja, Button Down senden

        move        #0,d0
        jsr         (a1)            ;Button Up
        move        #2,d0           ;Right Button Down

_m_end:
        jsr         (a1)            ;das Menü schliessen

        rts 

Listing 1


/*-----------------------------------------------*/
/* Feststellen, ob sich der Mauszeiger in        */
/* einem Menü befindet.                          */
/* (c)1995 by MAXON-Computer                     */
/* Autor: Uwe Hax                                */
/*-----------------------------------------------*/

VOID mouse_in_menu (VOID)
{
    WORD title, parent, menubox, item;
    WORD xbox, ybox;
    GRECT box, itembox;
    WORD first;

    /*
     * Indizes und Koordinaten ermitteln.
     */

    title = menu[ROOT].ob_head;
    title = parent = menu[title].ob_head;
    first = title = menu[title].ob_head;

    xbox = menu[ROOT].ob_x; 
    ybox = menu[ROOT].ob_y; 
    menubox = menu[ROOT].ob_tail;

    xbox += menu[menubox].ob_x; 
    ybox += menu[menubox].ob_y; 
    menubox = menu[menubox].objiead;

    /*
     * Schleife über alle Menütitel, ob ein Titel
     * selektiert, d.h. das Menü ausgeklappt ist.
     */

    while (title != parent)
    {
        if (menu[title].ob_state & SELECTED)
        {
            /*
             * Koordinaten des ausgeklappten Menüs
             * berechnen.
             */

            box.g_x = xbox + menu[menubox].ob_x; 
            box.g_y = ybox + menu[menubox].ob_y; 
            box.g_w = menu[menubox].ob_width;

            if (title == first)
            {
                box.g_h = menu[menu[menubox].ob_head].ob_height;
            }
            else
                box.g_h = menu[menubox].ob_height;

            if (rc_inside(&box, Vdiesc->cur_x, Vdiesc->cur_y))
            {
                tear_menu = menubox; 
                tear_title = title;

                /*
                 * Feststellen, ob der angeklickte Menü-
                 * Eintrag disabled ist.
                 * Wenn nein, muß die erzeugte
                 * MN_SELECTED-Meldung später abgefangen
                 * werden.
                 */

                item = menu[menubox].ob_head; 
                do 
                {
                    itembox.g_x =box.g_x + menu[item].ob_x; 
                    itembox.g_y =box.g_y + menu[item].ob_y; 
                    itembox.g_w = menu[item].ob_width; 
                    itembox.g_h = menu[item].ob_height;

                    if (rc_inside(&itembox, Vdiesc->cur_x, Vdiesc->cur_y)) 
                        if (!(menu[item].ob_state & DISABLED)) 
                        {
                            tear_msg = TRUE; 
                            break;
                        } /* if */ 
                        item = menu[item].ob_next;
                }
                while (item != menubox); 
                return;
            } /* if */
        } /* if */
        title = menu[title].ob_next; 
        menubox = menu[menubox].ob_next; 
    } /* while */
} /* mouse_in_menu */

Listing 2


/*-----------------------------------------------*/
/* Menü in Tear-Off-Menü umwandeln.              */
/* (c)1995 by MAXON-Computer                     */
/* Autor: Uwe Hax                                */
/*-----------------------------------------------*/

VOID tear_off (VOID)
{
    GRECT size, desk;
    WORD mx, my, mstate, mbutton, dummy;
    WORD pxyarray[4], deskarray[4];
    WORD diffx, diffy;
    WORD events, msg_buff[8];
    WORD count, next, i, whandle, found;
    OBJECT *tree;
    BYTE title[20];
    WORD first, condition, clicks;

    /*
     * Sicherheitsabfrage 
     */

    if ((tear_menu == NIL) || (tear_title == NIL)) 
        return;

    /*
     * Falls der Menütitel invertiert ist,
     * wiederherstellen.
     */

    if (menu[tear_title].ob_state & SELECTED) 
        menu_tnormal(menu, tear_title, TRUE);

    /*
     * Index des ersten Menütitels ermitteln.
     */

    first = menu[ROOT].ob_head; 
    first = menu[first].ob_head; 
    first = menu[first].ob_head;

    /*
     * Größe des zu zeichnenden Rechtecks
     * ermitteln.
     */

    objc_offset(menu, tearjnenu, &size.g_x, &size.g_y); 
    size.g_w = menu[tear_menu].ob_width; 
    if (first == tear_title)
    {
        size.g_h = menu[menu[tear_menu].ob_head].ob_height;
    }
    else
        size.g_h = menu[tear_menu].ob_height;

    /*
     * Rechteck zeichnen und verschieben.
     * graf_dragbox() ist nicht einsetzbar, da
     * diese Funktion nur mit der linken
     * Maustaste arbeitet.
     */

    wind_get(DESKTOP, WF_WORKXYWH, &desk.g_x, 
             &desk.g_y, &desk.g_w, &desk.g_h);

    wind_update(BEG_MCTRL);

    graf_mkstate(&mx, &my, &mstate, &dummy);
    pxyarray[0] = size.g_x;
    pxyarray[1] = size.g_y;
    pxyarray[2] = size.g_x + size.g_w - 1;
    pxyarray[3] = size.g_y + size.g_h - 1;

    diffx = mx - size.g_x; 
    diffy = my - size.g_y;

    /*
     * Je nach AES-Version andere Bedienung. 
     */

    if (aes_version < 0x410)
    {
        condition = mstate & RIGHT_BUTTON; 
        clicks = 1;
    }
    else
    {
        condition = !mstate; 
        clicks = 256 I 1;
    } /* else */

    if (condition)
    {
        /*
         * ersten Rahmen zeichnen 
         */
    
        deskarray[0] = desk.g_x; 
        deskarray[1] = desk.g_y; 
        deskarray[2] = desk.g_x + desk.g_w - 1; 
        deskarray[3] = desk.g_y + desk.g_h - 1;

        vs_clip(vdi_handle, TRUE, deskarray);

        vswr_mode(vdi_handle, MD_XOR); 
        vsf_interior(vdi_handle, FIS_HOLLOW); 
        vsf_style(vdi_handle, 0); 
        vsf_color(vdi_handle, BLACK); 
        vsf_perimeter(vdi_handle, TRUE);

        v_hide_c(vdi_handle); 
        v_bar(vdi_handle, pxyarray); 
        v_show_c(vdi_handle, TRUE);

        do
        {
            events = evnt_multi(MU_MI | MU_BUTTON, 
                        clicks,
                        LEFT_BUTTON | RIGHT_BUTTON,
                        0, 1, mx, my, 1, 1, 0,0,0,0,0, 
                        msg_buff, 0,0, &mx, &my, 
                        &mbutton, &dummy, &dummy,
                        &dummy);

            /*
             * Sonderbehandlung ab AES 4.1:
             * Loslassen der Maustaste ignorieren (wird
             * hier noch aus der Menübehandlung
             * gemeldet)
             */

            if (events & MU_BUTTON)
                if ((aes_version >= 0x410) && (mbutton == 0)) 
                    events &= ~MU_BUTTON;

            /*
             * Bei Mausbewegung neuen Rahmen zeichnen. 
             */

            if (events & MU_M1)
            {
                v_hide_c(vdi_handle); 
                v_bar(vdi_handle, pxyarray); 
                v_show_c(vdi_handle, TRUE);

                pxyarray[0] = rax - diffx; 
                pxyarray[1] = my - diffy; 
                pxyarray[2] = pxyarraytO] + size.g_w; 
                pxyarray[3] = pxyarray[1] + size.g_h;

                v_hide_c(vdi_handle); 
                v_bar(vdi_handle, pxyarray); 
                v_show_c(vdi_handle, TRUE);
            ) /* if */
        }
        while (!(events & MU_BUTTON));

        /*
         * letzten Rahmen löschen 
         */

        v_hide_c(vdi_handle); 
        v_bar(vdi_handle, pxyarray); 
        v_show_c (vdi_handle, TRUE);
    } /* if */

    wind_update(END_MCTRL);

    /*
     * Neuen Objektbaum erstellen.
     * 1. Anzahl der Objekte ermitteln.
     */

    count = objc_childs(menu, tearjnenu) + 1; 
    tree = malloc((LONG)count * sizeof(OBJECT)); 
    if (tree == NULL)
    {
        form_alert(1, NO_MEMORY); 
        return;
    } /* if */

    /*
     * Objekte kopieren 
     */

    tree[ROOT] = menu[tear_menu]; 
    tree[ROOT].ob_next = NIL; 
    tree[ROOT].ob_head = 1; 
    tree[ROOT].ob_x = 0; 
    tree[ROOT].ob_y = 0;

    next = menu[tear_menu].ob_head; 
    i = 1; 
    do {
        /*
         * Objekt kopieren 
         */

        tree[i] = menu[next];

        /*
         * Verkettung anpassen
         * Kind-Objekte werden hier nicht mit
         * übernommen, da in Menüzeilen normaler-
         * weise keine verschachtelten Objekte
         * vorhanden sind.
         */

        tree[i].ob_head = NIL; 
        tree[i].ob_tail = NIL; 
        tree[i].ob_next = i + 1;

        i++;
        next = menu[next].ob_next;
    }
    while ((next != tear_menu) &&
          (first != tear_title));

    i--;
    tree[i].ob_next = ROOT; 
    tree[ROOT].ob_tail = i;

    /*
     * Sonderbehandlung für Desk-Menü 
     */

    if (first == tear_title) 
        tree[ROOT].ob_height = tree[tree[ROOT].ob_head].ob_height;

    /*
     * Fenstergröße berechnen und Fenster erzeugen. 
     */

    wind_calc(WC_BORDER, NAME | MOVER | CLOSER, 
        pxyarray[0], pxyarray[1], size.g_w, 
        size.g_h, &size.g_x, &size.g_y,
        &size.g_w, &size.g_h);

    if (size.g_y < desk.g_y) 
        size.g_y = desk.g_y;

    whandle = wind_create(CLOSER | NAME | MOVER, 
        size.g_x, size.g_y, size.g_w, 
        size,g_h);

    if (whandle <= 0)
    {
        free(tree);
        form_alert(1, NO_WINDOW); 
        tear_menu = NIL; 
        tear_title = NIL; 
        return;
    } /* if */

    /*
     * Fenstertitel festlegen.
     */

    title[0] = EOS;
    if (menu[tear_title].ob_spec.free_string[0]!= ' ') 
        strcpy(title, " "); 
    strcat(title, menu[tear_title].ob_spec.free_string); 
    if (title[strlen(title) - 1] != ' ') 
        strcat(title, " ");

    tear_menu = NIL; 
    tear_title = NIL;

    /*
     * VerwaltungsInformationen anlegen.
     */

    found = FALSE;
    for (i = 0; i < MAX_TEAROFF; i++) 
        if (tearoff[i].whandle <= 0)
        {
            tearoff[i].whandle = whandle; 
            tearoff[i].tree = tree; 
            strcpy(tearoff[i].title, title);

            wind_set(whandle, WF_NAME, tearoff[i].title); 
            wind_set(whandle, WF_BEVENT, 1, 0, 0, 0);

            found = TRUE; 
            break;
        } /* if */

    if (!found)
    {
        form_alert(1, NO_WINDOW); 
        wind_delete(whandle); 
        free(tree); 
        return;
    } /* if */

    /*
     * Fenster öffnen.
     */

    wind_open(whandle, size.g_x, size.g_y, size.g_w, size.g_h); 
    wind_get (whandle, WF_WORKXYWH, &tree[ROOT].ob_x, &tree[ROOT].ob_y, &dummy, &dummy);
} /* tear_off */

Listing 3


Uwe Hax
Aus: ST-Computer 03 / 1995, Seite 75

Links

Copyright-Bestimmungen: siehe Über diese Seite