Windows unter GEM, Teil 4: Das “Redrawing” des Fenster

Willkommen zum 4. Teil unserer Folge. Diesmal werden wir uns um das wichtige “Redrawing”, d.h. um das Neuzeichnen der Fenster kümmern. Darunter versteht man ein Wiederherstellen des alten Fensterinhaltes, wenn dieser von einer Dialogbox oder einem anderen Fenster überlagert wurde und nun wieder offen zutage liegt.

Zum Glück wird unsere Arbeit wieder vom AES (Application Environment System des GEM) vereinfacht. Wir brauchen bloß zu warten, bis wir mittels evnt_mesag() [evnt_multi() kann auch verwendet werden] in buffer[0] den Wert von WM_REDRAW erhalten und führen dann das Redrawing durch (vgl. die angepaßten Zeilen in handle_window():

case WM REDRA : do_redraw(buffer); break;

Und wie weiter? Die Funktion do_redraw() erledigt für uns das Drum und Dran beim Redrawing. Wir übergeben ihr den von evnt_mesag() gelieferten Buffer und lassen sie dann arbeiten:

Nachdem sie mittels v_hide_c(handle) die Maus ausgeschaltet hat, die beim Neuzeichnen nur stören würde, und danach mit wind_update(BEG_UPDATE) dem AES mitgeteilt hat. daß sie nun die Kontrolle übernimmt (mit anderen Worten kein Menü mehr heruntergeklappt werden soll und auch alle anderen Benutzeraktionen ignoriert werden sollen), beginnt die eigentliche Routine. AES übermittelt uns in buffer[4] bis buffer[7] die Koordinaten des zerstörten Bildschirmbereichs. Das genügt aber nicht, denn wir möchten wissen, welche Bereiche eines Fensters neu gezeichnet werden müssen (Desktop-Bereiche zeichnet GEM selbständig neu). Dazu brauchen wir die sogenannte Rechtecksliste dieses Fensters. Darin ist der sichtbare Arbeitsbereich in verschiedene Rechtecke aufgeteilt (Bild 1 verdeutlicht den Sachverhalt). Ist das Fenster ganz sichtbar, besteht die Liste natürlich nur aus einem Rechteck (vgl. Fenster 2 in Bild 1), bei teilweise verdeckten Fenstern dagegen aus mehreren (vgl. Fenster 1 in Bild 1).

Bild 1: Rechteckslisten von zwei Fenstern

Die Koordinaten des ersten Rechtecks erhalten wir durch

wind_get(buffer[3],WF_FIRSTXYWH,...),

die nächsten durch wiederholten Gebrauch von

wind_get(buffer[3],WF_NEXTXYWH,...).

Wenn Breite und Höhe der erhaltenen Koordinaten gleich Null sind, sind keine weiteren Rechtecke vorhanden. Bild 2 zeigt nun, was passiert, wenn z.B. eine Dialogbox einen Teil des Bildschirms verdeckt hat und der darunterliegende (schraffierte) Bereich neu gezeichnet werden muß. do_redraw() nimmt sich in einer while{...}do-Schleife alle vorhandenen Rechtecke vor und untersucht sie auf Überschneidungen mit dem zerstörten Bildschirmbereich. Dazu werden in rc_intersect() die Koordinaten beider Bereiche verglichen und bei einer Überlappung die Koordinaten des Schnittbereichs berechnet und TRUE zurückgegeben. Ist dies der Fall, d.h. muß ein Bereich neu gezeichnet werden, clippt do_redraw() mit vs_clip() die Bildschirmausgabe, d.h. es werden nur Ausgaben in den angegebenen Bereich erlaubt. Darauf löscht sie dieses Rechteck. Jetzt wird es Zeit, die eigentliche Draw-Routine für das Fenster aufzurufen (Genaueres siehe weiter unten).

Nach Abschluß dieser Arbeit, also nachdem alle beschädigten Bereiche eines Fensters neu gezeichnet worden sind, wird das Clipping wieder ausgeschaltet (sonst sind keine normalen Ausgaben mehr möglich) und mit

wind_update(END_UPDATE)

dem AES die Kontrolle wieder übergeben sowie die Maus wieder angeschaltet

(v_show_c(handle,TRUE)),

bis eine neue REDRAW-Message vor der Tür steht... (AES schickt für jedes betroffene Fenster eine eigene Meldung, die bearbeitet werden muß).

Bild 2: Neuzeichnenderbereich eines Bildschirms

Strukturen mit Pointer auf void-Funktionen...

Keine Angst, das klingt zwar relativ kompliziert und sieht in C auch so aus, ist es aber eigentlich nicht. Weiter oben wurde erwähnt, daß nun die eigentliche Draw-Routine für das Fenster aufgerufen wird. Wenn man ins Listing sieht, heißt das dann

(*windows[buffer[3]].w_redraw)(),

was für die meisten wohl gleichbedeutend mit Chinesisch sein dürfte, nämlich unverständlich.

Hier nun die Erklärung: Um die Funktion do_redraw() möglichst autonom und transparent zu gestalten, übergeben wir open_window() (die dazu nötigen Änderungen an der alten Version zeigt Listing 1 auf) einen Pointer auf eine void-Funktion (eine Funktion ohne Rückgabewert), die die jeweilige Bildschirmausgabe erledigt. Listing 2 dürfte dies anschaulich demonstrieren. Open_window() weist diesen Pointer dann dem neu geschaffenen Element w_redraw von struct wind_data zu. Somit kann über dieses globale Array (windows[]) jede Funktion auf die wesentlichen Daten eines existierenden Fensters zugreifen. Wie dies eben do_redraw mittels

(*windows[buffer[3]].w_redraw)()

vollzieht. (Das stellt eine Möglichkeit dar, eine Funktion über einen Pointer auf sie aufzurufen. Genaueres erfahren Sie in Ihrer C-Bibel!)

Anmerkung: Der Methode, derer do_redraw() sich bedient (Ermitteln der Rechteckslisten bei einer REDRAW-Meldung mit anschließendem Neuzeichnen dieser), sollten alle GEM-Applikationen folgen, auch wenn sie nur ein Fenster öffnen, denn es kann ja z.B. durch ein Accessoire überdeckt werden. Außerdem kommt hierbei eines der grundlegenden Konzepte von GEM, nämlich das Message Prinzip, voll zur Geltung - eines der am meisten mißverstandenen Konzepte, das an sich flexible Programmgestaltung und ein gewisses Multitasking erlaubt (siehe Accessories).

Wie geht’s weiter?

In dieser Folge haben wir zum ersten Mal etwas in unsere Fenster ausgegeben (vgl. Listing 2). Sie können (und sollten) damit selbstverständlich nach Belieben experimentieren - es braucht ja kein Text zu sein...

Nun bleiben uns noch die Slider (Schiebebalken) der Fenster, die bei größeren Datenmengen, die ein Fenster nicht auf einmal darstellen kann, zum Einsatz kommen. Wir werden uns also in der nächsten Folge mit Begriffen wie “Dokumentengröße", “Sliderposition", “Arbeitsbereichsgröße" u.a. auseinandersetzen müssen...

        /* Aenderungen zur Window-Bibliothek */ 
       /* Andreas Loetscher 1988            */
      /* Compiler : Lattice C 3.04         */
     /*************************************/


  /* erweiterte Struktur wind_data */

struct wind_data /* Struktur, die */
{ /* Wissenswertes ueber unsere Fenster enthaelt */ 
    char  name[80]; /* Fenstername */
    GRECT max;      /* Maximalgroesse */
    WORD  elements; /* Bestandteile des Fensters */ 
    WORD  align;    /* Faktor zur hör. Ausrichtung */ 
    WORD  snap;     /* Fenster snappen (TRUE/FALSE)*/
    WORD  full;     /* Full-Flag (TRUE/FALSE) */ 
    void  (*w_redraw)(); /* Pointer auf Redraw-Funk. */
};

/* erweiterte open_window-Deklaration */

WORD open_window(w_name,redraw,was,algn,snp, 
                 x1,y1,w1,h1,
                 mx,my,mw,mh)
    char    *w_name;     /* Ptr auf Namensstring */
    void    (*redraw)(); /* Ptr auf Redraw-Funkt.*/
    ...



/* ueberprueft, ob sich p1 und p2 ueberlappen: */

rc_intersect(p1,p2)
short p1[],p2[];
{
    short tw = _min(p1[0] + p1[2], p2[0] + p2[2]);
    short th = _min(p1[1] + p1[3], p2[1] + p2[3]);
    short tx = _max(p1[0], p2[0]);
    short ty = _max(p1[l], p2[1]);

    p2[0] = tx;
    p2[1] = ty;
    p2[2] = tw - tx;
    p2[3] = th - ty;

    return ((tw > tx) && (th > ty));
}

void /* fuehrt den Redraw eines Fensters aus */
do_redraw(buffer)
    WORD buffer[8];
{
    GRECT   p;
    short   work[4];

    v_hide_c(handle); 
    wind_update(BEG_UPDATE); 
    wind_get(buffer[3],WF_FIRSTXYWH,&p.g_x,&p.g_y,&p.g_w,&p.g_h); 
    while(p.g_w>0 && p.g_h>0)
    {
        work[0] = buffer[4]; work[1] = buffer[5]; 
        work[2] = buffer[6]; work[3] = buffer[7]; 
        if(rc_intersect(&p,&work))
        {
            work[2] += work[0]-1; 
            work[3] += work[1]-1; 
            vs_clip(handle,TRUE,work); 
            vsf_color(handle,0); 
            v_bar(handle,work);
            (*windows[buffer[3]].w_redraw)();
        }
        wind_get(buffer[3],WF_NEXTXYWH,&p.g_x,&p.g_y,&p.g_w,&p.g_h);
    }
    vs_clip(handle,FALSE,work); 
    wind_update(END_UPDATE); 
    v_show_c(handle,TRUE);
}

/* angepasstes case in handle_window() */

    case WM_REDRAW : do_redraw(buffer);
                     break;

        /* Demonstrationsprogramm zum Snappen */
       /* von Windows                        */
      /* Andreas Loetscher 1988             */
     /* Compiler : Lattice-C 3.04          */
    /**************************************/

#include <a:\headers\portab.h>
#include <c:\listing1.h>

extern WORD handle;

void 
hallo()
{
        char    txt[40];
        WORD    p1,p2,p3,p4;
        sprintf(txt,"Hello World");
        wind_get(1,WF_WORKXYWH,&p1,&p2,&p3,&p4);
        v_justified(handle,p1+5,p2+20,txt,200,0,0);
}

void 
hallo2()
{
        char    txt[40];
        WORD    p1,p2,p3,p4;
        sprintf(txt,"Hello Universe"); wind_get(2,WF_WORKXYWH,&p1,&p2,&p3,&p4); 
        p1 = align(p1+10,8);
        v_justified (handle,p1,p2+20,txt,200,0,0);
}

void 
main()
{
    WORD buffer[8], w_handle[2];

    gem_init(); 
    graf_mouse(0,0);

    w_handle[0] = open_window("Demofenster", 
                        hallo,
                        NAME+CLOSER+FULLER+
                        MOVER+SIZER,
                        0,TRUE,
                        40,30,300,300,
                        0,19,600,350); 
    w_handle[1] = open_window("zweites Demofenster",
                        hallo2,
                        NAME+CLOSER+MOVER+
                        FULLER+SIZER,
                        8,TRUE,
                        50,50,200,100,
                        7,30,500,250);

    do
    {
        evnt_mesag(buffer); 
        handle_window(buffer);
    }while(buffer[0] != WM_CLOSED);

    if(buffer[3]==1) wind_close(w_handle[1]); 
    else             wind_close(w_handle[0]);
    wind_delete(w_handle[1]); 
    wind_delete(w_handle[0]); 
    gem_exit();
}

Andreas Lötscher
Aus: ST-Computer 09 / 1989, Seite 115

Links

Copyright-Bestimmungen: siehe Über diese Seite