GFA-Programmierung mit faceVALUE (Teil 4)

Im letzten Kursteil haben wir den Schritt zu den Userfenstern gewagt. Ein einfacher Textviewer diente als Beispiel zur Demonstration der Grafikausgabe. In diesem Kursteil wollen wir zunächst dem Viewer das MultiTOS Dragdrop-Protokoll beibringen. Anschließend werden Routinen zum Markieren von Textzeilen vorgestellt. Die markierten Zeilen sollen in ein anderes Fenster gezogen und dort gesammelt werden können.

Im folgenden Abschnitt geht es also um ein Verfahren, welches das Arbeiten mit komfortablen Benutzeroberflächen prägt. Mit 'Drag & Drop' (kurz: D&D) wird allgemein der Vorgang bezeichnet, ein Objekt mit der Maus anzupacken, über den Bildschirm zu bewegen, um es dann an einer anderen Stelle fallenzulassen (Abbildung 1). In den meisten Fällen möchte man damit eine Aktion auslösen, manchmal auch nur das Objekt verschieben. Seit mit MultiTOS nicht mehr nur eine Applikation zur gleichen Zeit am werkeln ist, hat ATARI ein spezielles D&D-Protokoll entwickelt. Das Protokoll soll es dem Benutzer in erster Linie ermöglichen, Objekte per D&D aus einer Applikation auch in eine andere einzufügen. Hierzu ist es nötig, dass sowohl die Anwendung, die das Objekt anbietet (der Sender), als auch die Anwendung, die das Objekt erhalten soll (der Empfänger) das Protokoll versteht. In faceVALUE wurde D&D zur einen Hälfte implementiert. Man kann in seinem Programm ohne große Vorkenntnisse Objekten empfangen.

Die Übergabe der Daten läuft in mehreren Schritten ab, weshalb oft von einer D&D-Sitzung gesprochen wird. Nachdem der Benutzer ein Objekt auf einem Ihrer Fenster fallengelassen hat, meldet sich der Sender bei der faceVALUE-Engine. Diese ruft dann die Funktion user_dragdrop_init auf und übergibt Informationen über das Zielfenster, die Koordinaten, an welchen das Objekt eingefügt werden soll, und den Status der Tastaturumschalttasten. Anhand dieser Angaben teilt man der Engine durch einen entsprechenden Returnwert mit, ob man die Daten empfangen möchte. Entscheidet man sich dazu, die Daten anzunehmen, füllt man vor dem Rücksprung aus user_dragdrop_init die Variable datatypes$ (VAR-Parameter) mit Kennungen für die Datentypen, die man zu empfangen in der Lage ist. Bei den Kennung handelt es sich in der Regel um Dateiextender mit vorangestelltem Punkt. Ein Beispiel: Sie möchten ASCII-Texte und Pixelgrafiken empfangen. Setzen Sie dann datatypes$ etwa auf

LET datatypes$=".TXT.ASC.IMG"

Eine Liste der gebräuchlichen Typen finden Sie in Tabelle 1. Im zweiten Schritt der D&D-Sitzung teilt der Sender der Engine mit, ob er die gewünschten Daten senden kann. Wenn ja, ruft die Engine die Funktion user_dragdrop_accept auf. Vom Sender hat sie außerdem Informationen erhalten, für welchen Datentyp er sich entschieden hat, wie die Daten heißen (ein Dateiname z.Bsp.) und wie groß (in Bytes) die Datenmenge ist. Hier teilt man der Engine abermals durch einen geeigneten Returnwert mit, ob sie den Empfang der Daten einleiten soll. Eine Zusammenfassung der gültigen Returnwerte finden Sie in Tabelle 2.

Last but not least müssen die Daten dann noch wirklich empfangen werden. Das geschieht in user_dragdrop_loaddata. Dort sind die Daten unter Verwendung der Funktion fread() zu lesen. Das Lesen darf in mehreren Schritten geschehen, insgesamt müssen aber wirklich so viele Bytes gelesen werden, wie der Sender angekündigt hat. Nach dem Lesen muss man noch zum Schließen des Datenkanals die Funktion fclose() aufrufen. In Listing 5 des letzen Kursteils fanden Sie bereits die Routine fread_to_array(), die das Lesen von Textdateien in ein Stringarray demonstriert. Das enttäuschend kleine Listing 1 enthält alle notwendigen Ergänzungen, um unseren Textviewer D&D-fähig zu machen.

Hat es Klick gemacht?

Bislang wurden die Userfenster nur zur Anzeige von Daten genutzt. In vielen Fällen soll der Benutzer aber mit den Daten arbeiten können. Es sollen bestimmte Aktionen ausgeführt werden, wenn er in das Fenster klickt oder eine Taste drückt. Im nächsten Schritt ermöglichen wir dem Benutzer, einzelne oder auch mehrere Textzeilen im Dateiviewer-Fenster zu selektieren.

Zunächsteinmal ist es die Engine, die vom Betriebssystem über Mausklicks informiert wird. Soweit möglich reagiert sie dann entsprechend darauf (Buttons selektieren, Popups öffnen...). Die Engine weiß aber mit Mausklicks in Userfenster nichts anzufangen. Sie überlässt daher, wie im letzen Kursteil bereits angesprochen, diese Arbeit dem Programmierer und springt in einem solchen Fall in die Prozedur user_mouse. Diese user-Prozedur ist der Platz, an dem wir eingreifen dürfen. Alle zum Behandeln des Mausklicks wichtigen Informationen werden uns dabei von der Engine übergeben: Fensterhandle handle&, Fensterindex index&, Userhandle userhandle&, Mauskoordinaten mx& und my& beim Klick, gedrückte Maustaste mb& , Anzahl der Mausklicks mc& (Einfach- oder Doppelklick) und der Status der Tastaturumschalttasten ks& (Shift, Control und Alternate). mb& und ks& sind binärcodiert. Ist Bit 0 in mb& gesetzt, wurde die linke Maustaste gedrückt, ist Bit 1 gesetzt, die rechte. ks& kann wie folgt entschlüsselt werden:

LET shift!=AND(ks&,&x11)  (rechts/links-Shift)
LET control!=BTST(ks&,2)
LET alternate!=BTST(ks&,3)

Um user_mouse nicht unnötig aufzublasen, schreibt man sich gerne eine eigene Prozedur zu jedem Userfenstertyp, die alle Mausklicks behandeln kann. Dann genügt es, in user_mouse für jedes Fenster nur einen Eintrag vorzunehmen (s. Listing 2).

Zeile selektiere Dich

Um zu kennzeichnen, welche Textzeilen des Viewers gerade selektiert sind, benutzen wir das Boolfeld selected!(). Im letzten Kursteil wurde dieses Feld immer nur als Leiche mitgeschleppt. Diesesmal soll es zum Zuge kommen. Das behandeln des Mausklicks erledigt die Prozedur aus Listing 3. Hier kommt endlich wieder Pionierarbeit auf den Programmierer zu. Werfen wir also einen Blick dorthinein. Zunächst wird die Zeile line& errechnet, in die geklickt wurde. Dann wird 150 Millisekunden darauf gewartet, dass der Benutzer die Maustaste nach dem Klick wieder loslässt. Sollte er sie nach diesen 0,15 Sekunden immer noch gedrückt haben, möchte er in der Regel das angewählte Objekt verschieben. Dieser Fall wird in der Variable dd! vorgemerkt.

Das Selektieren der Zeilen soll wie in einem normalen GEM- Fenster von statten gehen: Drückt der Benutzer nicht Shift, werden zuerst alle bislang selektierten Zeilen deselektiert und die neu angewählte selektiert. Das Deselektieren aller Zeilen übernimmt der nächste IF-ENDIF-Konstrukt. Im nächsten Schritt wird auf Doppelklick geprüft. Ein Doppelklick soll die betroffene Zeile zunächst immer selektieren. Die Funktion textlist_click gibt in diesem Falle zudem statt -1 die Nummer dieser Zeile zurück. Dann können Sie in user_mouse den Rückgabewert auswerten und darauf reagieren. Für das pure Selektieren von Zeilen in unserem Viewer ist das allerdings nicht von Interesse.

Der vorletzte Schritt endlich selektiert oder deselektiert die angewählte Zeile. Zum Neuzeichen wird übrigens wie bereits weiter oben die Prozedur textlist_redraw() genutzt. Sie macht nichts weiter, als die Bildschirmkoordinaten der neuzuzeichnenden Zeile zu errechnen und diese dann von der Engine neuzeichnen zu lassen. Sie sehen, dass sie als Programmierer dazu recht wenig Aufwand treiben müssen. Last but not least kümmert sich textlist_click dann noch wie angesprochen um das Verschieben von Textzeilen in andere Fenster (Drag&Drop). Hier übrigens nicht zu verwechseln mit dem zu Anfang erklärten MultiTOS-Dragdrop-Protokoll. textlist_click stellt eine mit der Maus verschiebbare Box dar (GRAF_DRAGBOX). Sobald der Benutzer die Maus wieder loslässt, werden das Zielfenster und die Zielkoordinaten ermittelt. Bei einem Treffer in ein faceVALUE-Fenster wird user_textlist_dragdrop angesprungen. Dies ist keine user- Prozedur von faceVALUE, sondern eine ganz normale, von uns angelegte Routine. Sie hat aber einen ähnlichen Zweck wie die faceVALUE-Userprozeduren. Sie dient als "Callback" (engl. Rückruf). Der Sinn liegt darin, dass viele vom Inhalt verschiedene Userfenster ein- und dieselbe Prozedur zum Behandeln von Mausklicks nutzen können: textlist_click. Dort, wo aber größere Unterschiede in der Behandlung zu erwarten sind (Doppelklicks, Drag&Drop etc.) sollte man aus dieser Prozedur hinausgehen. In kommenden faceVALUE-Versionen werden solche Callback-Routinen von wachsender Bedeutung sein. Testen sie den neuen Viewer ruhig aus. Im jetzigen Beispiel wird natürlich die Drag&Drop-Funktion noch keine Aktion nach sich führen. Deshalb erweitern wir das Programm abermals.

Toolstrips, Toolbars und Menuwindows

In den vergangenen Ausgaben dieses Kurses wurden eine Mischform der User- und Dialogfenster angesprochen. Bekannt sind sie ihnen aus vielen anderen Programmen: Fenster mit einem bedienbaren Dialogstreifen (Toolstrip) am oberen Rand werden oft z.Bsp. in Textverarbeitungen genutzt, um den Textstil, die Fontgröße oder das Absatzformat einzustellen. In Grafik- und DTP-Programmen findet man meist Dialogbalken (Toolbars) am linken Fensterrand zum Auswählen des Zeichenwerkzeugs.

Wenn sie einen Toolstrip- oder Toolbar-Dialog im RCS erstellen möchten, achten sie darauf, dass hierfür -genau wie für modale und nonmodale Dialoge- in der Datei TREES.RSC vorgefertigte Leerdialoge bereit liegen. Für Fenstermenüs gibt es keinen besonderen Baumtyp. Fenstermenüs werden im RCS auf die gleiche Weise erstellt wie Hauptmenüs.

Es steht also wieder etwas RCS-Arbeit an. Das in der Einleitung erwähnte Testprogramm benötigt die Bäume, die in Tabelle 3 aufgeführt sind. Abbildung 2 zeigt den Toolstrip- Baum. Zum Einstieg habe ich bewußt vorerst nur normale Shortcut-Buttons verwendet, Sie können sich aber mit Außnahme von editierbaren Objekten aller anderen faceVALUE bekannten Objekttypen bedienen.

Vor dem Erstellen des Programmgerüsts müssen im faceVALUE- Hauptprogramm für die Mischformen der Userfenster, genauso wie für normale Userfenster, eigene Fenstertypen erstellt werden. Einziger Unterschied: Im 'Dialog'-Popup wird der Typ des für das Fenster zu benutzenden Dialoges eingestellt. Erstellen Sie die Fenstertypen "textwindow" und "toolwindow" entsprechend der Tabelle 4. Binden Sie zu den in der letzten Ausgabe angegebenen Extra-Routinen noch die Routine 'fileselector' mit ein.

Die neuen Dialogformen sind eigentlich gar keine. Sie werden im erstellten Programmgerüst genauso angesprochen wie normale Dialoge. Alle Objekte erhalten ganz normale Einträge in die user-Routinen. Die Fenster werden aber eben nicht mit win_open_dialog, sondern wie Userfenster, mit win_open geöffnet. An dieser Stelle kommt nun der im letzten Kursteil nicht angesprochene Parameter tree& ins Spiel. Übergeben Sie an dieser Stelle die Nummer des Tool- oder Menübaumes, der in dem Misch-Userfenster erscheinen soll - und fertig. Listing 4 enthält alle notwendigen Eintragungen in die User- Routinen. Die meisten Dinge (Lade, Sichern, Drucken usw.) sind Ihnen bekannt. Zu diesem Listing möchte ich daher auch nicht viel Worte verlieren.

Die Toolbox

Zum Abschluß des faceVALUE-Kurses komme ich wieder auf die normalen Dialoge zurück. Eine Toolbox ist nämlich ein solcher einfacher Dialog. Allerdings ist die Toolbox auch dann bedienbar, wenn sie im Hintergrund liegt. Eine Toolbox wird gerne zur Auswahl von Zeichenwerkzeugen und ähnlichem benutzt. Bei diesen Fenstertypen wäre es sehr lästig, wenn man sie zum Auswählen einer Option erst toppen müsste. faceVALUE beherrscht (wie könnte es anders sein) auch diesen Dialogtyp. Geben Sie beim Öffnen des Dialoges mit win_open_dialog beim Parameter type& eine 3 an, und schon wird der Dialog hintergrundbedienbar.

Ende?

Hiermit sind wir nun am Ende des faceVALUE-Kurses angelangt. Es war mir sicherlich nicht möglich, Ihnen alle Möglichkeiten von faceVALUE vorzuführen. Viele Dinge blieben unerwähnt, von der Klemmbrett-Benutzung und die Selectric-Unterstützung bis hin zu Offscreen-Bitmaps. Eine neue faceVALUE-Version bahnt sich bereits seit längerem an und wird derzeit ausgiebig getestet. Ich verspreche mit Sicherheit nicht zu viel, wenn ich sage, dass das Programm mit den neuen Features der kommenden Version sich selbst in den Schatten zu stellen vermag. Hoffen wir also auf ein baldiges Erscheinen.


Holger Herzog
Aus: ST-Computer 05 / 1997, Seite 39

Links

Copyright-Bestimmungen: siehe Über diese Seite