Icon-Programmierung in Pascal

ST Pascal Plus enthält bereits sehr viele AES- und VDI-Routinen, die die Programmierung einer komfortablen Benutzeroberfläche ermöglichen. Jedoch finden sich weder im Handbuch der Version 1.20 noch im neuen Handbuch der Version 2.0 Hinweise auf den Umgang mit Icons.

Nach etlichen fehlgeschlagenen Versuchen (teilweise wurden sie seitens des ST mit mehr oder weniger Bomben beantwortet), diese Icons auf den Bildschirm zu bringen, stellte ich fest: und es geht doch.

Das Beispielprogramm erwartet eine Resource-Datei ICONSMPL.RSC, die die Icons enthalten muß. Diese werden auf dem Bildschirm dargestellt, anschließend können sie mit der Maus selektiert oder verschoben werden. Doch zuerst zum Anlegen der Resource-Datei:

Die Resource-Datei beinhaltet nur einen Dialog mit dem Namen TDESK, dessen Umfassung groß genug sein sollte, um zwei Icons aufnehmen zu können, und der mit dem Muster des Hintergrundes ausgefüllt sein muß (ohne Rand), damit das korrekte Löschen der Icons gewährleistet ist. In diesen Dialog plaziert man zwei Icons (arbeitet man mit dem Kuma Resource Construction Set, können als Icons z.B. das Baumicon und das Dateiicon aus dem Resourcefile des RCS verwenden, um Arbeit zu sparen) mit den Namen ICON1 und ICON2. Die Namen der Objekte können natürlich auch anders gewählt werden, müssen dann allerdings auch im Programm entsprechend angepaßt werden. Bei beiden Objekte brauchen keine Objektflags gesetzt werden.

Aufbau des Programms

Die Zeilen 1-10 bedürfen wohl keiner großen Erklärung, es werden die Include-Dateien eingebunden und die Variablen deklariert. In Zeile 11 wird eine Variable tr vom Typ tree ptr deklariert, die verschiedene Informationen über einen Objektbaum und seine Kinder beinhaltet. Die für das Programm wesentlichen werden später noch erläutert.

Nach erfolgreichem Laden der Resource-Datei und nachdem die Zeigervariable tr durch die find_dialog-Routine auf den Objektbaum tdesk gesetzt wurde, zeichnet das Hauptprogramm zuerst durch zweimaligen Aufruf der Prozedur Icon_Draw die Icons mit dem Namen Icon1 und Icon2. Diese Prozedur setzt zuerst die x- und y-Koordinate des “Rahmens” (Index 0) auf den Ursprung, dann die des gewählten Objekts (item) auf die in xc und yc übergebenen Koordinaten (weil die Koordinaten eines Objektes relativ zum Elternobjekt angegeben werden). Da obj_draw neben den Koordinaten auch die Breite und Höhe als Parameter verlangt, werden diese vorher von Obj_Size ermittelt (wobei x und y die Werte von xc und yc annehmen). Die Variablen x,y,w,h werden als Referenzparameter übergeben. Danach wird die Prozedur m_contr aufgerufen, die als Kern für die Programmierung von Icons angesehen werden kann.

Die Funktion get_event ermittelt Keyboard- (nur zum Verlassen der Prozedur von Bedeutung) und Mousebutton-Events. Ergibt die Überprüfung in Zeile 62 einen Mousebutton-Event, erfolgt die Bearbeitung dieses Events. Der Laufvariablen i wird der Index des ersten Kindes des Rahmen zugewiesen, worauf in der folgenden Repeat-Schleife der Index des Icons ermittelt wird, auf das der Mauszeiger weist. Falls nicht auf ein Icon geklickt wurde, wird die Schleife verlassen, nachdem das letzte Kind überprüft wurde (allerdings wird hier nur die oberste Kinderebene berücksichtigt, mit Hilfe der Worktree-Routine kann man diese Prozedur für eigene Anwendungen erweitern), wobei fl=FALSE ist. Liefert jedoch obj_find einen Wert, der i entspricht, und ist das gefundene Object ein Icon, wird fl=TRUE gesetzt, worauf ic der Wert von i zugewiesen wird. Eigentlich könnte man die Schleife jetzt verlassen, jedoch sollte auch berücksichtigt werden, daß sich Icons überlappen können. Leider bietet GEM - soweit mir bekannt - keine Möglichkeit zu überprüfen, welches Icon an oberster Stelle liegt, da dies durch die verschiedene Reihenfolge der obj_draw-Aufrufe nicht von dem Objekt-Index der Icons abhängt. Man könnte vielleicht ein Array anlegen, in dem alle Icons mit Index aufge-führt sind und die in dieser festgelegten Reihenfolge gezeichnet werden.

Wenn nun ein Icon mit der linken Maustaste angeklickt worden ist (Überprüfung in Zeile 69), wird zunächst das selektierte Icon invers dargestellt. Dann wird mit graf_mkstate geprüft, ob die linke Maustaste noch gedrückt wird. Ist dies der Fall, wird die Prozedur icon_move aufgerufen, die das Verschieben eines Icons übernimmt: Mit der AES-Routine Dragbox werden die neuen Koordinaten des Icons ermittelt, das Icon wird an der ursprünglichen Position gelöscht, worauf man die übrigen neu zeichnen muß, da eventuell auch Teile eines anderen Icons gelöscht wurden, wenn es das zu verschiebende überlagert. Schließlich wird letztgenanntes an der neuen Position gezeichnet.

Wenn man viele Icons verwendet, kostet das Neuzeichnen aller Icons relativ viel Zeit, so daß sich das Programm dann vielleicht alle Icons merken sollte, die die obj_find-Routine liefert, um nur diese neu zu zeichnen.

Sollte ein Mausklick erfolgen, ohne daß der Mauszeiger auf einem Icon steht, werden alle Icons normal dargestellt (Zeile 111-118).

Man gelangt aus der Routine m_contr wieder ins Hauptprogramm, indem man die Space-Taste drückt. Don wartet das Programm erneut auf einen Tastendruck, worauf die Icons mit der icon_era-Routine gelöscht werden.

Diese Prozedur zeichnet an der Position der Icons einen Teil des Elternobjekts, so daß diese “überschrieben” werden. Deshalb ist es auch von Bedeutung, daß das Füllmuster der Umrandung das gleiche ist wie das des Desktops. Ich weiß nicht, ob dieses Verfahren das optimale ist, jedoch habe ich festgestellt, daß K-Resource anscheinend nach einer ähnlichen Methode arbeitet: Ändert man im ersten Formular des K-Resource-RSC-File das Füllmuster des Rahmens (des Elternobjekt der Icons) und speichert das RSC-File, wird der Bildschirm mit diesem Muster gefüllt, nachdem man den Resource-Editor erneut gestartet hat.

Zum Schluß wünsche ich noch viel Spaß beim Probieren und mögen die Bomben an Euch vorüber gehen!

program icon_sample; { (c) MAXON Computer GmbH }

{$I iconsmpl.i}
{$I gemconst.pas9

type
    {$I gemtype.pas}

var     c            : char;
        init,x,y,w,h : integer;
        tr           : tree_ptr;

{$I gemsubs}

{$P-}

procedure graf_mkstate(var mstate:integer);
var     int_in   : int_in_parms;
        int_out  : int_out_parms; 
        addr_in  : addr_in_parms;
        addr_out : addr_out_parms;
begin
    aes_call(79,int_in,int_out,addr_in,addr_out); 
    mstate:=int_out[3); 
end;

procedure icon_draw(var pt:tree_ptr; item,xc,yc:integer); 
var     x,y,w,h : integer; 
begin
    pt^[0].ob_x:=0; 
    pt^[0].ob_y:=0; 
    pt^[item].ob_x:=xc; 
    pt^[item].ob_y:=yc; 
    obj_size(pt,item,x,y,w,h); 
    obj_draw(pt,item,0,0,0,0,0); 
end;

procedure icon_era (var pt:tree_ptr;item:integer); 
var     x,y,w,h : integer;
        p1      : tree_ptr;
begin
    x:=0; y:=0;
    pt^[0].ob_x:=0; 
    pt^[0].ob_y:=0; 
    obj_size(pt,item,x,y,w,h); 
    pt^[0].ob_x:=pt^[item].ob_x; 
    pt^[0].ob_y:=pt^[item].ob_y; 
    obj_draw(pt,0,0,0,0,0,0); 
end;


procedure icon_move(ic:integer); 
var     xn,yn,x,y,w,h,i : integer;
begin
    obj_size(tr,ic,x,y,w,h);
    dragbox(0,0,639,399,x,y,w,h,xn,yn);
    icon_era(tr,ic);
    tr^[0].ob_x:=0;
    tr^[0].ob_y:=0;
    icon_draw(tr,ic,xn,yn);
    i:=tr^[0].ob_head;
    repeat
        if i<>ic then 
            begin
                obj_setstate(tr,i,$00,false); 
                obj_redraw(tr,i); 
            end;
        i:=tr^[i].ob_next; 
    until i=0; { Index der Wurzel }
end;

procedure m_contr;
var fl              : boolean;
    ev,mx,my,d,x,y,w,h,xn,yn,ic,i, 
        key,bstate  : integer; 
    msg             : message_buffer;
begin
    repeat
        ev:=get_event($01|$02,$01,$01,1,0,false,d,d,d,d,false,d,d,d,d, 
                      msg,key,bstate,d,mx,my,d); 
        if ev & $0200 then 
            begin
                i:=tr^[0].ob_head;
                ic:=-1;
                repeat
                    fl:=(i=obj_find(tr,i,0,mx,my)) and (tr^[i]. ob_type=G_Icon); 
                    if fl then 
                        begin
                            if obj_state(tr,i) & $01<>0 then fl:=not fl;
                                { Wenn gefundenes Objekt bereits selektiert, dann weitersuchen ) 
                            ic:=i; 
                        end;
                    i:=tr^[i].ob_next; 
                until i=0; { i=0: Wurzelobjekt} 
                if (ic<>-1) and (bstate & $01<>0) then 
                    begin
                        obj_setstate(tr,ic,$01,true); 
                        i:=tr^[0].ob_head; 
                        while i<>0 do 
                            begin
                                if i<>ic then obj_setstate(tr,i,$00,true);
                                i:=tr^[i].ob_next; 
                            end;
                        graf_mkstate(bstate); 
                        if bstate & $01 <>0 then icon_move(ic);
                    end
                else
                    begin
                        i:=tr^[0].ob_head; 
                        while i<>0 do 
                            begin
                                obj_setstate(tr,i,$00,true); 
                                i:=tr^[i].ob_next; 
                            end;
                    end;
            end;
    until key & $FF=32; 
end;

{$P=} 

begin
    init:=init_gem; if init<0 then halt; 
    if load_resource('iconsmpl.rsc') then 
        begin
            find_dialog(tdesk,tr); 
            icon_draw(tr,icon2,300,200); 
            icon_draw(tr,icon1,200,200); 
            obj_size(tr,icon1,x,y,w,h); 
            m_contr; 
            read(c);
            icon_era(tr,icon2); 
            icon_era(tr,icon1);
        end; 
    free_resource; 
    exit_gem; 
end.
(* Resource-Set indicies fuer ICONSMPL *) 

CONST
    tdesk   = 0;    (* Formular/Dialog *)
    rfill   = 0;    (* BOX in Baum TDESK *)
    icon1   = 1;    (* ICON in Baum TDESK *)
    icon2   = 2;    (* ICON in Baum TDESK *)

Oliver Krämer
Aus: ST-Computer 10 / 1989, Seite 80

Links

Copyright-Bestimmungen: siehe Über diese Seite