Komfortables Dialog-Handling in GFA-BASIC 3.0

Die AES-Funktion FORM_DO hat ihre Licht- und Schattenseiten. Einerseits erspart sie dem Programmierer eine Menge Arbeit, übernimmt sie doch die komplette Verwaltung einer Dialogbox, andererseits aber bietet sie dem Anwender wenig Komfort bei der Bedienung. Außerdem wird wohl so mancher nach einem FORM_DO-Aufruf verzweifelt zum Reset-Taster gegriffen haben, weil durch ein vergessenes Exit-Objekt oder einen falsch übergebenen Objektindex der Rechner mal wieder im Halbleiter-Nirwana verschwunden war.

Die Problemstellung war klar. Eine eigene FORM_DO-Funktion mit völlig neuen Eigenschaften mußte her. Sie sollte einfach anstelle der gleichnamigen AES-Routine einzusetzen sein und zusätzliche Funktionen, vor allem zur komfortablen Positionierung des Cursors, besitzen. Außerdem sollten sämtliche Objekte einer Dialogbox alternativ auch über die Tastatur bedient werden können - eine nützliche Hilfe für fortgeschrittene Anwender eines Programms, die sich bei Pulldown-Menüs bereits durchzusetzen beginnt.

Das Ergebnis dieser Bemühungen sind die Extended FORM_DO-Funktion und der KEY RESOURCE EDITOR, mit dessen Hilfe man die Tastenbelegungen der einzelnen Objekte einer Resource erzeugen kann.

Extended FORM_DO

Kommen wir zunächst zur Beschreibung der neuen FORM_DO-Routine. Sie besitzt sämtliche Eigenschaften der normalen FORM_DO-Funktion. Zusätzlich jedoch sind einige weitere Steuercodes standardmäßig eingebaut (Bild 1). Außerdem läßt sich der Cursor jetzt mit der Maus zeichengenau in Textfelder setzen und landet nicht wie gewohnt immer am rechten Ende. Auch Dialogboxen ohne Exit-Objekt werden verwaltet.

Die wohl interessanteste Eigenschaft ist aber die Möglichkeit, sämtliche Objekte einer Dialogbox wahlweise auch über die Tastatur bedienen zu können. Das Prinzip ist eigentlich recht simpel: Einem Objekt einer Dialogbox, beispielsweise einem Radiobutton, wird eine bestimmte Taste, z.B. F1, zugeordnet. Bei Druck auf F1 verhält sich die Dialogbox genauso, als wäre der entsprechende Radiobutton angeklickt worden - er wird selektiert. Prinzipiell läßt sich dies für jedes beliebige Objekt realisieren. Eine Standardeinstellung wäre etwa die Belegung des Cancel/Abbruch-Buttons, den man in fast jeder Dialogbox findet, mit der Taste Undo. Eine solche, wenn auch die einzige, Tastaturbelegung ist ja bereits im AES implementiert, und zwar in Gestalt des Default-Objektes, das wahlweise mit der Maus oder der Return- bzw. Enter-Taste angewählt werden kann.

Wie funktioniert nun die Zuordnung einer Taste zu einem beliebigen Objekt? Dazu müßte man am besten den Code der Taste in der Objektstruktur des betreffenden Objekts innerhalb der Resource unterbringen, um unnötigen Speicherbedarf, etwa durch eine zusätzliche Datei, zu vermeiden. Betrachtet man die Objekt-Struktur näher, dann bleiben nur zwei Möglichkeiten, nämlich OB_STATE und OB_FLAGS, denn dies sind die einzigen Objektinformationen, die vom AES bitweise ausgewertet werden, und nur dort können wir den Tastaturcode "einpflanzen", ohne daß die interne Struktur der Resource oder das Objekt selbst verändert wird. Bei OB_STATE sind die Bits 6-13 unbelegt, bei OB_FLAGS die Bits 9-15. Laut Digital Research sind sie auch nicht reserviert, also gerade richtig für unsere Zwecke. Für den Code einer Taste benötigen wir acht Bits. Um die Routine möglichst flexibel zu halten, wird nicht der ASCII-, sondern der Scan-Code gespeichert und zusätzlich in drei Bits der Status der Umschalttasten Shift, Control und Alternate, wobei auf die Unterscheidung von rechter und linker Shift-Taste verzichtet wurde, um der völligen Verwirrung eines gestreßten Anwenders vorzubeugen. Die zusätzliche Berücksichtigung der Umschalttasten ist jedoch äußerst sinnvoll, vor allem bei Dialogboxen mit Texteingabefeldern, denn dort sind ja die meisten Tasten bereits für die Eingabe reserviert. Der Scan-Code der Taste wird in die freien Bits 8-15 von OB_STATE eingetragen, der zugehörige Status der Umschalttasten in die Bits 13-15 von OB_FLAGS (siehe Bild 2). Ich habe die beiden Werte mit Absicht an den "oberen Rand” gelegt, damit sie nicht mit möglichen Erweiterungen kollidieren, die ja beispielsweise vom Kuma RCS unterstützt werden, wo man die Bits 6 und 7 von OB_STATE und die Bits 10 und 11 von OB_FLAGS manipulieren kann.

# Die Standard-Steuertasten von EXTENDED FORM_DO
   
SHIFT CURSOR UP HOME Cursor in erstes Eingabefeld
SHIFT/CURSOR DOWN, SHIFT HOME Cursor in letztes Eingabefeld
SHIFT CURSOR LEFT Cursor an Anfang des Eingabefeldes
SHIFT/CURSOR RIGHT Cursor ans Ende des Eingabefeldes

Bild 1

Bild 2: Oie benötigten Tasten-Codes werden an unbenützten Stellen in ob_flags und ob_state eingetragen.
## Format von OBJC_EDIT

Original GEM-Dokumentation von DR:

OBJC_EDIT(ob_edtree,ob_edobject,ob_edchar,ob_edidx,ob_edkind,ob_newedidx)

Binding in den meisten (C-)Compilern:

OBJC_EDIT(ob_edtree,ob_edobject,ob_edchar,ob_edidx,ob_edkind)

Hier wird für die bisherige Cursorposition (ob_edidx) und die zurückgelieferte neue Position (ob_newedidx) dieselbe Variable verwendet.

Bild 3

Eine Resource nach dem oben beschriebenen Verfahren von Hand zu verändern, wäre viel zu umständlich. Daher habe ich den Key Resource Editor geschrieben, der es auf einfache und komfortable Weise ermöglicht, Tastenbelegungen für Resource-Objekte zu erzeugen. Eine derart veränderte Resource bleibt natürlich auch weiterhin lauffähig mit den üblichen AES-Routinen. Weitere Einzelheiten über den Key Resource Editor finden Sie weiter hinten.

Im folgenden möchte ich die Funktionsweise der neuen FORM_DO-Routine erklären und dabei auch auf die scheinbar wenig beachteten AES-Funktionen FORM_BUTTON, FORM_KEYBD und OBJC_EDIT eingehen. Grundsätzlich muß die Funktion genau das leisten, was auch die Original FORM_DO-Routine bietet, nämlich die Abfrage und Auswertung von Tastatur und Mausknopfeingaben. Steuertasten wie Cursor up/down, ESC etc. müssen richtig interpretiert, eingegebene Buchstaben und Ziffern in Textfelder eingetragen und angeklickte Objekte eventuell selektiert werden. Nach Anwählen eines Objekts mit Exit- oder Touchexit-Status muß der Dialog korrekt beendet werden. Dazu kommen nun noch die bereits geschilderten Sonderfunktionen. Bereits in der ST Computer 8/87 wurde eine modifizierte Routine von Tim Oren, dem Autor des Digital Research RCS, vorgestellt, die die geschilderten grundsätzlichen Aufgaben übernimmt. Genau das gleiche Prinzip, wenn auch in stark abgewandelter Form, benutzt auch die hier gezeigte FORM_DO-Funktion. Als Parameter werden ihr wie bei der Originalfunktion die Baumadresse und das Startobjekt, das als erstes ediert werden soll, übergeben. Mit WIND UPDATE(2) übernimmt die Applikation - das ist in diesem Fall unsere neue FORM_DO-Routine - die totale Kontrolle über die Maus. Das ist besonders deshalb wichtig, damit der Benutzer die Pull-Down-Menüs nicht anwählen kann. Dahinter finden wir schon den ersten entscheidenden Unterschied zur Originalroutine. Es wird geprüft, ob das übergebene Startobjekt tatsächlich edierbar ist. Wenn nicht, wird mittels der Suchfunktion search_ob_flag das erste edierbare Textfeld bestimmt. So kann es auch nicht passieren, daß eine versehentlich falsch übergebene Objektnummer den Cursor mitten in der Dialogbox erscheinen läßt und früher oder später zum Absturz führt. Die nun folgende WHILE...WEND-Schleife überwacht die Benutzereingaben über Maus und Tastatur. Zunächst wird überprüft, ob ein neues Textfeld für die nächste Eingabe bestimmt wurde. Das ist - unter der Voraussetzung, daß überhaupt Eingabefelder vorhanden sind - ganz zu Beginn der Fall und im folgenden immer dann, wenn der Cursor wegen eines Mausklicks oder einer Steuertaste in ein neues Feld bewegt werden muß.

Das Darstellen des Strichcursors übernimmt die Funktion OBJC_EDIT. Und genau diese Funktion hat wohl in der Vergangenheit immer wieder für Verwirrung gesorgt, denn die ursprüngliche DR-Dokumentation und die Bindings in verschiedenen Compilern (z.B. Megamax-C, DR-C) weichen voneinander ab. In GFA-Basic 3.0 ist diese Funktion löblicherweise nach der Originaldokumentation implementiert. Sollten Sie Extended FORM_DO auf eine andere Sprache umsetzen wollen, überprüfen Sie zunächst, in welchem Format OBJC_EDIT bei Ihnen eingebunden ist. Den Unterschied zeigt Bild 3. Nun zur Funktionsweise von OBJC_EDIT. Übergeben werden die Baumadresse, der Index des betreffenden Textobjekts, das eingegebene Zeichen als Wort (Hi-Byte: Scan-Code, Lo-Byte: ASCII-Code), die bisherige Position des Cursors, ein Flag zur Bestimmung der Funktion und als Rückgabewert die neue Position des Cursors. Wichtig ist vor allem der fünfte Parameter, nämlich das Flag ob_edkind. Dieses Flag kann die Werte 0-3 annehmen, wobei der Wert 0 (ED_START) keinerlei Funktion besitzt. Setzen wir für ob_edkind eine 1 (ED_INIT) ein, so wird aus der Textmaske und dem Text ein formatierter String berechnet und der Cursor eingeschaltet, einfach ausgedrückt: Der Cursor erscheint hinter dem letzten eingetragenen Zeichen in dem angegebenen Textobjekt. Der dritte und der vierte Parameter haben hier keine Bedeutung. Deshalb kann hier auch einfach eine 0 übergeben werden. Wichtig ist nur der zurückgelieferte Wert für die Variable pos&, welche nach dem Aufruf die Position des Cursors innerhalb des Eingabefeldes enthält.

# Parameter der Funktion process key

Wertparameter:

tree%       Baumadresse  
obj&        momentan ediertes Textobjekt (ed_obj& aus form_do)  
kstate&     Status der Umschalttasten (SHIFT, CTRL, ALT)  
ev_key&     Scan-Code und ASCII-Code der gedrückten Taste, 
            wird von EVNT_MULTI geliefert (GEM-Format)

VAR-Parameter:

nxt_obj&    Index des neuen aktuellen Objekts. Das ist entweder ein neues 
            Textobjekt oder ein Exit-Touchexitobjekt oder Null  
nxtchar&    Inhalt 0: Die gedrückte Taste wurde verwendet  
            Inhalt=ev_key&: Die gedrückte Taste muß weiterverarbeitet werden.
pos&        aktuelle Position des Cursors im Eingabefeld

Durch den nachfolgenden EVNT_MULTI-Aufruf wird auf einen Mausklick (Hier Tastendruck gewartet. War es ein Tastendruck, kommt die Funktion process key zum Einsatz. Sie ist ein Ersatz für die AES-Routine FORM_KEYBD. Die Aufgabe von FORM_KEYBD ist es. die Steuerlasten Cursor up/down herauszufiltern und, falls eine der beiden Tasten gedrückt wurde, den Index des neuen Textobjekts zurückzuIiefern. Außerdem ist sie dafür zuständig, bei Druck auf Return oder Enter zu überprüfen, ob ein Default-Objekt vorhanden ist, und gegebenenfalls die Beendigung des Dialogs zurückzumelden. Trotz intensiver Bemühungen ist es mir jedoch nicht gelungen. diese AES-Funktion zum Laufen zu bringen (an der Implementierung in GFA-Basic kann's nicht liegen, denn selbst die Parameterübergabe „zu Fuß“ durch Vorbesetzen der GEM-Arrays schaffte keine Abhilfe). Ich lasse mich natürlich gerne eines Besseren belehren. Allerdings ist das korrekte Funktionieren dieser Routine auch nicht so wichtig hier, denn durch die vielen neuen Steuertasten, die unser FORM_DO unterstützt, muß ohnehin eine eigene FORM_KEYBD-Funktion vorhanden sein, die alle standardmäßig vorhandenen Steuercodes korrekt interpretiert und zusätzlich was ja nicht vergessen werden darf die Tastenbelegungen der einzelnen Objekte überprüft.

Genau das tut die Funktion process key. Ich habe sie mit Absicht nicht form_keybd genannt, da die Parameterübergabe von der der normalen FORM_KEYBD-Routine abweicht. Die einzelnen Parameter und ihre Bedeutung sehen Sie in Bild 4. Entscheidend für uns ist vorerst nur, daß process key für die Variable ev_key& eine Null zurückliefert, wenn es sich bei der gedrückten Taste um eine Steuertaste gehandelt hat, ansonsten wird der Tastencode unverändert zurückgegeben. Zusätzlich bestimmt der in do_dial& enthaltene Rückgabewert, ob der Dialog beendet werden muß, d.h. ob ein Exit oder Touchexit-Objekt über die Tastatur angewählt wurde. Ist ev_key& nach dem Aufruf ungleich Null, dann wurde keine Steuertaste gedrückt, und wir müssen uns selbst darum kümmern, daß das eingegebene Zeichen in dem aktuellen Textobjekt verarbeitet wird. Das geschieht wiederum mit Hilfe von OBJC_EDIT, diesmal mit dem Flag 2. Dadurch wird uns eine Menge Arbeit abgenommen, denn wir müssen das eingegebene Zeichen beispielsweise nicht auf seine Zulässigkeit an dieser Stelle überprüfen. Das alles erledigt OBJC_EDlT.

Es ist übrigens nicht ganz richtig, wenn ich davon spreche, daß an OBJC_ED!T keine Steuertasten übergeben werden. Wenn Sie die Funktion process_key etwas näher betrachten, werden Sie feststellen, daß die Steuertasten Cursor links/ rechts, Backspace, Delete und Escape dort gar nicht behandelt werden. Das hat auch seine Richtigkeit, denn alle Steuertasten, die sich auf das aktuelle Textobjekt beziehen und den Cursor nicht in ein anderes Eingabefeld bewegen, werden auch von OBJC_EDIT verwaltet. Das ist natürlich wahrer Luxus, denn wollte man all diese Funktionen selbst berücksichtigen, würde der Aufwand erheblich ansteigen.

Gehen wir nun weiter im Listing. Wurde also eine Taste gedruckt, ist diese korrekt verarbeitet worden. Im Falle eines Mausklicks wird mittels OBJC_FIND überprüft, ob sich unter dem Mauszeiger ein Objekt befindet. Ist die Maus außerhalb der Dialogbox, wird für nxtobj& eine -1 zurückgeliefert, und wir quittieren das mit dem entsprechenden Klingelzeichen. Ansonsten muß bei Bedarf eine entsprechende Aktion, wie zum Beispiel das Selektieren eines Radiobuttons, ausgelöst werden. Glücklicherweise nimmt uns die Funktion FORM_BUTTON wiederum den größten Teil der Arbeit ab. Diese Funktion nämlich, die intern auch von FORM_DO benutzt wird, ermöglicht Mausknopfeingaben innerhalb einer Dialogbox. Übergeben werden Baumadresse, Index des angeklickten Objekts (hier ermittelt durch OBJC_FIND) und die Anzahl der Klicks. Genau wie bei process_key erhalten wir als Rückgabewert in do_dial& eine Null, falls ein Exit-/Touchexit-Objekt angeklickt wurde, und in nxt_obj& den Index des neuen aktuellen Objekts. Hier möchte ich nochmal einhaken, denn der Begriff "neues aktuelles Objekt” ist doch sehr schwammig. Ich muß zugeben, daß ich anfangs auch etwas Probleme mit dieser Formulierung hatte. Deshalb sei es hier nochmals klar und deutlich erklärt: nxt_obj& ist nach dem FORM_BUTTON-Aufruf nur dann ungleich Null, wenn entweder ein Exit- oder Touchexit-Objekt oder ein edierbares Textfeld angeklickt wurden. In diesen Fällen sind Reaktionen von unserer Seite nötig, nämlich die Beendigung des Dialogs unter Rückgabe des angeklickten Exit-Objekts bzw. die neue Positionierung des Cursors. Den Sinn der zusätzlichen Angabe der Klick-Anzahl finden wir bei den Touchexit-Objekten. Bei einem Doppelklick auf ein Touchexit-Objekt liefert FORM_BUTTON den entsprechenden Objektindex mit gesetztem Bit 15 zurück, genauso wie bei der Original-FORM_DO-Eunktion.

Nun wird es ein bißchen komplizierter, denn die neue FORM_DO-Routine soll ja auch die zeichengenaue Cursor-Positionierung mit Hilfe der Maus erlauben. Daher wird zunächst überprüft, ob es sich bei dem angeklickten Objekt um ein Texteingabefeld handelt. Wenn ja, dann werden zunächst die X-Koordinate des Textobjekts, die aktuelle Zeichenbreite und die Länge der Textmaske bestimmt. Die Textmaske bestimmt das Aussehen eines Textobjekts auf dem Bildschirm. Sie enthalt den statischen Teil des Eingabefeldes (also z.B die Bezeichnung) sowie für jedes edierbare Zeichen einen Unterstrich "_" als Platzhalter. Die Länge dieser Textmaske findet sich beim Offset 26 in der zugehörigen TEDINFO-Struktur, deren Adresse wir über OB_SPEC(tree%,nxt_obj&) erhalten. Nun wird abhängig von der Textausrichtung (te_just, TEDINFO Offset 16) die Position des Mauszeigers innerhalb der Textmaske bestimmt. Je nachdem, ob der Text linksbündig (0), rechtbündig (1) oder zentriert (2) ist, verändert sich natürlich die horizontale Position der Maus relativ zur Textmaske. Ich hoffe, Sie haben es bis hierhin verstanden. Wenn nicht, dann wird ein intensiver Blick ins Listing bestimmt helfen. Noch sind wir aber nicht am Ziel angelangt. In der Variablen mpos& steht jetzt zwar, über dem wievielten Zeichen der Textmaske sich der Mauszeiger befindet, wir wissen jedoch immer noch nicht, welches edierbare Zeichen der Maus am nächsten ist, also wo der Cursor letztendlich erscheinen muß.

Bild 5: Der Key Resource Editor nach dem Start
Bild 6: Tasten-Codes lassen sich den Buttons einfach zuordnen.

Das erledigt die nun folgende REPEAT ...UNTIL Schleife. Hier wird die Anzahl der vorkommenden Platzhalter bis zur Mausposition gezahlt. Die Variable p& schließlich enthält die neue aktuelle Cursorposition in dem angeklickten Textobjekt. Das scheint schon alles gewesen zu sein, jedoch gibt es da noch ein kleines Problem, nämlich: Wie positioniert man nun den Cursor beliebig innerhalb eines Eingabefeldes? Auf normalem Wege ist da nichts zu machen, denn der OBJC_EDIT-Aufruf zum Darstellen des Cursors (ED_INIT) läßt den Cursor grundsätzlich hinter dem letzten eingetragenen Zeichen erscheinen. Die Lösung dieses Problems findet sich in einem kleinen Trick:

Man gaukelt der OBJC_EDIT-Routine einfach vor, daß die Anzahl der eingetragenen Zeichen gerade bis zur gewünschten Cursor-Position reicht. Um das zu erreichen, ersetzt man das entsprechende Zeichen, nachdem man es sich vor her gemerkt hat, durch ein Nullbyte. Dann ruft man OBJC_EDIT auf, was zur Folge hat, daß der Cursor an der angestrebten Position erscheint. Schließlich muß man das entsprechende Zeichen wieder an seiner alten Position eintragen. Bevor man jedoch den Cursor an seiner neuen Position erscheinen läßt, muß dieser erst an seiner alten gelöscht werden. Dies erledigt wiederum OBJC_EDIT mit ob_edkind=3 (ED_END).

Wir sind nun fast am Ende von EXTENDED FORM_DO angelangt. Vor Abschluß der WHILE...WEND-Schleife wird noch für den Fall, daß der Dialog beendet werden muß oder ein neues Texteingabefeld angewählt wurde, der Cursor aus dem aktuellen Textobjekt entfernt. Zum Schluß wird die Mauskontrolle über WIND_UPDATE(2) wieder frei- und das Objekt, mit dem die Dialogbox verlassen wurde, zurückgegeben.

Noch ein paar Worte zur Funktion process_key. Bei Eingabe von Return oder Enter wird zunächst überprüft, ob ein Default-Objekt vorhanden ist. Ist dies nicht der Fall, wirken diese beiden Tasten genau wie Cursor down und bewegen den Cursor ins nächste Texteingabefeld. Eine Ausnahme jedoch bildet das letzte Eingabefeld. Befindet sich der Cursor im letzten Eingabefeld und existiert kein Default-Objekt. dann wird bei Druck auf Return/Enter geprüft, ob ein Exit-Objekt existiert. Wenn nicht, wird daraufhin der Dialog beendet. Das klappt natürlich auch bei Dialogboxen, die keine edierbaren Textobjekte besitzen. So gehören FORM_DO-Endlosschleifen aufgrund vergessener Exit-Objekte der Vergangenheit an. Wenn keine der im SELECT-Konstrukt aufgeführten Steuertasten gedruckt wurde, überprüft search_key_obj, ob einem Objekt der Dialogbox diese Taste bzw. Tastenkombination zugeordnet wurde. Wurde ein Objekt gefunden, wird mittels FORM_BUTTON die gleiche Aktion ausgelöst, als wäre das Objekt angeklickt worden.

Der Key Resource Editor

Soviel also zum Extended FORM_DO. Was wäre solch eine Erweiterung aber ohne die Möglichkeit, die notwendigen Tastaturbelegungen einfach, schnell und komfortabel in einer Resource zu erzeugen? Dazu habe ich den Key Resource Editor entwickelt. Er ermöglicht es auf einfache Weise. Resource-Files zu laden, diese zu edieren und wieder abzuspeichern. Der Key Resource Editor ist wegen seiner Listinglänge allerdings nur auf der Monatsdiskette zu finden. Wir bitten dafür um Verständnis. Ein großer Vorteil des Key Resource Editors ist. daß er Definitions-Dateien sowohl vom Digital Research RCS (Versionen 1 und 2) als auch vom Kuma-RCS lesen kann. Diese Fähigkeit ermöglicht es Ihnen, die einzelnen Objektbäume und Objekte namentlich anzusprechen, was eine große Arbeitserleichterung darstellt.

Nach dem Start des Programms erscheint die Auswahlbox (Bild 5). Von hier aus können Sie durch Anklicken von LOAD ein Resource File einladen. Das Programm lädt das Resource-File und - falls vorhanden - das zugehörige Definitionsfile. Daraufhin werden alle Objektbäume der Resource in dem Fenster TREES dargestellt. Angewählt werden können nur Formularobjekte, das heißt also Forms (Kuma) bzw. Dialogs, Panels (DR). Alle übrigen Strukturen wie Menüs (M), Alerts (A), Free Strings (S) und Free Images (I) sind in heller Schrift dargestellt. Die jeweiligen Großbuchstaben davor geben Auskunft über die Art der Struktur. Ein Häkchen vor einem Objektbaum bedeutet, daß in dieser Dialogbox Tastenbelegungen eingetragen sind. Mit den Randobjekten des Fensters können Sie bei mehr als acht Strukturen den gezeigten Ausschnitt verändern, genau wie bei der Fileselektorbox. Um neue Tastenbelegungen zu speichern oder bereits bestehende zu ändern, klicken Sie auf den entsprechenden Objektbaum und danach auf EDIT. Das gleiche erreichen Sie durch Doppelklick auf den Baumnamen. Die Dialogbox wird daraufhin gezeichnet. Nun klicken Sie das erste Objekt an, dem Sie eine Taste zuordnen wollen. Es wird dann selektiert, und an der gleichen Stelle erscheint eine kleine Infobox (Bild 6). Sie gibt Auskunft über den Namen des Objekts sowie die momentane Tastenbelegung. Drücken Sie nun die Taste, die Sie diesem Objekt zuordnen möchten. Sie erscheint daraufhin in der Infobox. Denken Sie daran, daß auch Tasten in Verbindung mit Shift, Control und Alternate sowie beliebige Kombinationen derselben erlaubt sind. Solange die Infobox auf dem Bildschirm steht, können Sie die Belegung des Objekts durch erneutes Drucken einer Taste beliebig oft verändern. Um die Tastenbelegung für das angewählte Objekt ganz zu löschen, klicken Sie auf den Button CLR links oben in der Intobox. Wenn Sie das Edieren des Objekts beenden wollen, klicken Sie einfach mit der Maus auf den Bereich außerhalb der Infobox.

Sie können jetzt weitere Objekte edieren oder sich auch nur deren momentane Belegung anschauen. Um das Edieren der Dialogbox zu beenden, drücken Sie entweder beide Maustasten gleichzeitig oder die linke Maustaste in Verbindung mit Shift, Control oder Alternate. Sie sehen nun wieder die Auswahlbox und können weitere Dialogboxen mit Tastenbelegungen versehen. Der RESET-Button in der Auswahlbox dient zum kompletten Löschen aller Tastenbelegungen eines Objektbaums. Dazu klicken Sie zunächst den entsprechenden Baumnamen an und danach RESET. Das Häkchen vor dem Namen verschwindet jetzt - die Dialogbox ist wieder “sauber".

# Definitions-Dateien der verschiedenen Resource Construction Sets

Digital Research RCS V1.4 (Extension .DEF)

Bytes 0,1 Anzahl der Objektnamen im Motorola-Format

danach in jeweils 16 Bytes die einzelnen Einträge:

BYTE INHALT
0,1 0
2 Baum-Index bei untergeordneten Objekten
3 Objekt-Index, Baum-Index,...
4 0 bei Bäumen, 1 bei Objekten
5 Baumtyp: 0: Unbekannt, 1: Free-Tree (-Panel), 2: Menübaum, 3: Dialogbaum, 4: Alert
6-13 Name des Baums/Objekts
14,15 0

Bei diesem RCS werden Free Strings/Free Images nicht verarbeitet

Digital Research RCS V2.1 (Extension .DFN)

Bytes 0.1 Anzahl der Objektnamen im Intel-Format (Lo-Byte/Hi Byte)

danach in jeweils 14 Bytes die einzelnen Einträge:

BYTE INHALT
0 Objekt-Index. Baum-Index, Free String-Index,...
1 Baum-Index bei untergeordneten Objekten
2 Baumtyp: 0: Unbekannt, 1 Panel. 2: Menübaum 3: Dialogbaum, 4: Alert
3 0 bei Bäumen, 1 bei Objekten
4-11 Name des Baums/Objekts
12,13 0
bei Free Strings: Byte 2: 1, Byte 3: 1
bei Free Images: Byte 2: 2, Byte 3: 1

Mit dem neuen Format lassen sich nun auch die Definitions-Dateien zwischen PC-GEM und Atari ST-GEM austauschen.

Kuma RCS V1.0 (Extension .RSD)

Jeweils 16 Bytes pro Eintrag. Im ersten Eintrag haben die Bytes 0 - 3 eine besondere Bedeutung:

   
Bytes 0,1 Anzahl der Objektnamen im Motorola-Format
Byte 2 0 (?)
Byte 3 Kennung für erzeugte Namensdatei:
Bit 0: C, Bit 1: Pascal, Bit 2: Modula, Bit 3: Fortran
BYTE INHALT
0-3 immer 0 bis auf ersten Eintrag (s.o.)
4 Baum Index bei untergeordneten Objekten
5 Objekt Index. Baum-Index, Free String-Index
6 0 bei Bäumen. 1 bei Objekten
7 Baumtyp: 2: Menübaum, 3: Dialogbaum, 4: Alert, 5: Free String, 6: Free Image
8 15

Wie man an dem Format der drei Dateien erkennen kann, werden bei allen Resource Construction Sets die Namen von Objekten, deren Index größer als 255 ist. nicht korrekt verwaltet

Bild 7

Um die Tastenbelegungen sämtlicher Bäume auf einmal zu löschen, klicken Sie auf den RESET-Button, ohne vorher einen bestimmten Baum auszuwählen. Es erscheint daraufhin die Sicherheitsabfrage RESET ALL TREES?, die Ihnen die Möglichkeit gibt Ihre Entscheidung zu revidieren. Sie können so sehr einfach eine komplette Resource von den Tastenbelegungen befreien. Sind Sie fertig mit dem Edieren der Resource, können Sie Ihr Werk über SAVE abspeichern. Wird die Resource unter gleichem Namen abgespeichert, so finden Sie die ursprüngliche Version nachher als Backup-File mit der Extension RSB.

Soviel zu der Bedienung des Key Resource Editors. Da das Programm ausführlich dokumentiert ist, möchte ich nicht auf alle Einzelheiten eingehen und nur einige Teile des Listings ansprechen. Bild 7 dokumentiert übersichtlich das Format der Definitionsdateien der verschiedenen Resource Construction Sets. Ich hatte bei der Entwicklung nur die Möglichkeit, die Formate der jeweils angegebenen Versionen zu testen. Sollten Sie ausgerechnet ein RCS besitzen, dessen Definitionsdateien vom Key Resource Editor nicht verarbeitet werden, müssen Sie versuchen. herauszufinden, inwieweit das Format von den hier dargestellten abweicht. Sollten alle Stricke reißen, bin ich gerne bereit zu helfen.

In dem Listing finden sich zwei Dinge, die ich gesondert hervorheben möchte. Das erste ist die Lösung eines Problems, das wohl weit verbreitet zu sein scheint, nämlich die Verwaltung von mehreren Resource-Files in einem Programm, ist doch allzu schnell die magische Grenze von 32 kByte überschritten. Prinzipiell ist es ja gar kein Problem, mehrere Resource-Files mittels RSRC_LOAD zu laden.

Speichert man beispielsweise nach dem Laden des ersten RSC-Files alle Baumadressen in Variablen, so benötigt man nach Laden des zweiten RSC Files den RSRC_GADDR-Aufruf für die Bäume der ersten Resource nicht mehr. Dieses Prinzip funktionierte auch hundertprozentig, wäre da nicht die Freigabe des Speichers bei Beendigung des Programms mit RSRC_FREE. Damit kann man zum Abschluß des Programms nur noch den Speicher für die zuletzt geladene Resource freigeben. Programmiert man in GFA-BASIC und möchte schließlich den mit RESERVE freigegebenen Speicher wieder an das BASIC zurückgeben, folgt unweigerlich ein “Fehler bei RESERVE". Abhilfe schafft hier eine kaum dokumentierte Adresse. Diese befindet sich im GEM-internen GLOBAL-Feld. Die Adresse des GLOBAL-Feldes steht im zweiten Langwort des AES-Parameter-Feldes. In GFA-BASIC erreicht man es über GB+4. In den Elementen 7 und 8 (Offset 14) des GLOBAL-Feldes findet man nach einem RSRC_LOAD die Adresse der Resource im Speicher und im Element 9 (Offset 18) die Länge der geladenen Resource. Auf diese Einträge greifen auch die AES-Funktionen zu. Der Rest ist dann recht einfach. Man muß nur nach dem Laden der ersten Resource deren Adresse und Länge zwischenspeichern, bevor man die zweite Resource lädt. Bei Beendigung des Programms wird mit RSRC_FREE zunächst der Speicher für die zweite Resource freigegeben. Daraufhin trägt man im GLOBAL-Feld Adresse und Länge der ersten Resource ein und ruft RSRC_FREE erneut auf. So wird der belegte Speicher wieder korrekt freigegeben. Prinzipiell läßt sich dieser Vorgang auch mit mehr als zwei RSC-Files realisieren. Weitere Einzelheiten entnehmen Sie bitte dem Listing. Die hier geschilderten Eigenschaften haben meines Wissens Gültigkeit für alle TOS-Versionen bis einschließlich Blitter-TOS, so daß man wohl davon ausgehen kann, daß es auch auf dem neuen TOS 1.4 und späteren Versionen funktioniert.

Bei näherem Betrachten des Listings wird Ihnen vielleicht auffallen, daß die zu bearbeitende Resource zweimal geladen wird. Das erste Mal mit RSRC_LOAD und das zweite Mal mittels BGET. Der Grund dafür ist folgender: Die Resource, wie sie von einem RCS erstellt wurde, liegt in einem adreß- und autlösungsunabhängigen Format vor. Sämtliche absoluten Adressen, Objektkoordinaten und -maße werden von der Funktion RSRC_LOAD nach dem Laden automatisch berechnet und eingetragen. Um die Resource abzuspeichern, müßte man sie erst wieder in ihr ursprüngliches Formal zurückverwandeln. Wenn der Benutzer nun nach dem Abspeichern aber weiter an der Resource arbeiten wollte, müßte sie wiederum umgewandelt oder gar ganz neu geladen werden. Das wäre zuviel Aufwand. Statt dessen wird die mit RSRC_LOAD geladene Resource ausschließlich benutzt, um die Dialogboxen auf dem Bildschirm zu zeigen, während die vorgenommenen Tastenbelegungen in das “Duplikat" eingetragen werden. Dieses Duplikat wird auch hinterher wieder abgespeichert. Dadurch erspart man sich einiges an Programmieraufwand, und an den maximal 32 kByte zusätzlich benötigtem Speicher soll es ja wohl nicht scheitern.

Abschließend noch ein paar Tips und Ratschläge. Man könnte die neue FORM_DO-Funktion dahingehend erweitern, daß sie bei Beendigung des Dialogs auch noch den Index des zuletzt edierten Textobjekts zurückliefert. So könnte der Cursor bei einem erneuten Aufruf wieder in dem zuletzt bearbeiteten Eingabefeld stehen. Das ist gerade bei Dialogboxen, die nach dem Anwählen eines Touchexit-Objekts auf dem Bildschirm stehen bleiben, sehr nützlich. Die Realisierung ist einfach. Dazu muß man lediglich die Variable startobj& durch ed_obj& ersetzen und in der Parameterliste der Funktion ed_obj& als VAR-Parameter deklarieren. Allerdings kann dann beim Aufruf für das Startobjekt keine Konstante mehr übergeben werden. Ich habe diese Erweiterung nicht schon von vornherein eingebaut, um Kompatibilität zur normalen FORM_DO-Routine zu gewährleisten. Denkbar wäre auch die zusätzliche Abfrage von Messages über EVNT_MULTI. damit der Benutzer auch während der Eingabe in eine Dialogbox die Möglichkeit hat, Menüeinträge anzuwählen. Der Sinn einer solchen Möglichkeit ist natürlich stark von der jeweiligen Anwendung abhängig.

Was die Gestaltung von Dialogboxen mit zusätzlichen Tastenbelegungen angeht, kann ich jedem nur raten, es nicht zu übertreiben. Immerhin soll diese Funktion eine Erleichterung für den Anwender darstellen. Die Belegung der einzelnen Objekte sollte nach bestimmten Prinzipien erfolgen und leicht einzuprägen sein. In Bild 8 sehen Sie ein kleines Beispiel einer auch für den Benutzer sichtbaren Tastenbelegung einzelner Objekte.

Bild 8: Beispiel für eine mit dem Key Resource Editor erstellte Dialogbox.

Literatur:
Atari ST Profibuch, Sybex Verlag ST-Computer 8/87 S.20 ff

key_rcs.zip

' ************************************
' *         EXTENDED FORM_DO         *
' *                                  *
' * I: 08.06.89      Tassilo Nitz    *
' * U: 09.06.89      Krablerstr. 24  *
' *                  4300 Essen 12   *
' * (c) MAXON Computer 1989          *
' ************************************
'
'
FUNCTION form_do(tree%,startobj&)
  '
  LOCAL do_dial&,ed_obj&,nxt_obj&,pos&,mpos&,p&,i&,buf|,charw&,mlen&
  LOCAL ox&,mes&,ev_mx&,ev_my&,ev_mb&,ev_ks&,ev_key&,ev_clicks&
  '
  ~WIND_UPDATE(3)    !BEG_MCTRL
  '
  IF NOT BTST(OB_FLAGS(tree%,startobj&),3)  !startobj& editable?
    nxt_obj&=@search_ob_flag(tree%,0,3,1)
  ELSE
    nxt_obj&=startobj&
  ENDIF
  ed_obj&=0      !Index des aktuellen Textobjekts
  do_dial&=1
  '
  WHILE do_dial&
    '
    IF ed_obj&<>nxt_obj& AND nxt_obj&<>0   !neues Textobjekt?
      ed_obj&=nxt_obj&
      nxt_obj&=0
      ~OBJC_EDIT(tree%,ed_obj&,0,0,1,pos&) !ja, Cursor darstellen
    ENDIF
    '
    mes&=EVNT_MULTI(&X11,2,1,1,0,0,0,0,0,0,0,0,0,0,0,0,ev_mx&,ev_my&,ev_mb&,ev_ks&,ev_key&,ev_clicks&)
    '
    IF BTST(mes&,0)            !Tastendruck
      '
      do_dial&=@process_key(tree%,ed_obj&,ev_ks&,ev_key&,nxt_obj&,ev_key&,pos&)
      '
      IF ev_key&                                       !keine Steuertaste
        ~OBJC_EDIT(tree%,ed_obj&,ev_key&,pos&,2,pos&)  !in Textobjekt einfügen
      ENDIF
      '
    ELSE                       !Maustaste
      nxt_obj&=OBJC_FIND(tree%,0,8,ev_mx&,ev_my&)  !Objekt unter Mauszeiger
      '
      IF nxt_obj&=-1   !Maus außerhalb der Dialogbox
        OUT 2,7
        nxt_obj&=0
      ELSE
        IF BTST(OB_FLAGS(tree%,nxt_obj&),3)       !Editable?
          ~OBJC_OFFSET(tree%,nxt_obj&,ox&,dummy&)   !X-Koord. Textobjekt
          ~GRAF_HANDLE(charw&,dummy&,dummy&,dummy&) !Breite eines Zeichens
          ' Länge der Textmaske (nullterminierter String, abschließendes
          mlen&=CARD{OB_SPEC(tree%,nxt_obj&)+26}-1  !Nullbyte zählt mit)
          SELECT CARD{OB_SPEC(tree%,nxt_obj&)+16}   !Textausrichtung
          CASE 0     !linksbündig
            mpos&=ROUND((ev_mx&-ox&)/charw&)
            ' rechtsbündig: nur der Vollständigkeit halber, werden vom AES
          CASE 1   !sowieso nicht korrekt verwaltet
            mpos&=ROUND((ev_mx&-ox&-OB_W(tree%,nxt_obj&))/charw&+mlen&)
          CASE 2     !zentriert
            mpos&=ROUND((ev_mx&-ox&-(OB_W(tree%,nxt_obj&)-mlen&*charw&) DIV 2)/charw&)
          ENDSELECT
          p&=0
          IF mpos&>0
            i&=0
            REPEAT    !Zeichenposition bestimmen
              IF BYTE{{OB_SPEC(tree%,nxt_obj&)+4}+i&}=95   !"_"
                INC p&
              ENDIF
              INC i&
            UNTIL i&=mpos& OR i&=mlen&
          ENDIF
          ~OBJC_EDIT(tree%,ed_obj&,0,pos&,3,pos&) !Positionierung des
          buf|=BYTE{{OB_SPEC(tree%,nxt_obj&)}+p&} !Cursors mit einem
          BYTE{{OB_SPEC(tree%,nxt_obj&)}+p&}=0    !kleinen Trick
          ~OBJC_EDIT(tree%,nxt_obj&,0,pos&,1,pos&)
          BYTE{{OB_SPEC(tree%,nxt_obj&)}+p&}=buf|
          ed_obj&=nxt_obj&                        !aktuelles Textobj neu setzen
        ENDIF
        do_dial&=FORM_BUTTON(tree%,nxt_obj&,ev_clicks&,nxt_obj&)
      ENDIF
      '
    ENDIF
    '
    IF do_dial&=0 OR (nxt_obj&<>0 AND nxt_obj&<>ed_obj&) !wenn neues Textobjekt
      ~OBJC_EDIT(tree%,ed_obj&,0,pos&,3,pos&)            !oder Ende des Dialogs
    ENDIF                                                !Cursor löschen
    '
  WEND
  '
  ~WIND_UPDATE(2)    !END_MCTRL
  RETURN nxt_obj&
  '
ENDFUNC
'
FUNCTION process_key(tree%,obj&,kstate&,ev_key&,VAR nxt_obj&,nxtchar&,pos&)
  LOCAL key_obj&
  '
  IF BTST(kstate&,0)
    kstate&=BSET(kstate&,1)
  ENDIF
  kstate&=SHR&(kstate&,1)
  '
  nxtchar&=0
  SELECT ev_key&
  CASE &H4D36   !SHIFT/Cursor right
    ~OBJC_EDIT(tree%,obj&,0,pos&,3,pos&)
    ~OBJC_EDIT(tree%,obj&,0,pos&,1,pos&)
    '
  CASE &H4B34   !SHIFT/Cursor left
    ~OBJC_EDIT(tree%,obj&,0,pos&,3,pos&)
    buf|=BYTE{{OB_SPEC(tree%,obj&)}}
    BYTE{{OB_SPEC(tree%,obj&)}}=0
    ~OBJC_EDIT(tree%,obj&,0,pos&,1,pos&)
    BYTE{{OB_SPEC(tree%,obj&)}}=buf|
    '
  CASE &H5000    !Cursor down
    nxt_obj&=@search_ob_flag(tree%,obj&,3,1)
    '
  CASE &H4800    !Cursor up
    nxt_obj&=@search_ob_flag(tree%,obj&,3,-1)
    '
  CASE &H5032,&H4737    !SHIFT/Cursor down  SHIFT/HOME
    REPEAT
      nxt_obj&=obj&
      obj&=@search_ob_flag(tree%,obj&,3,1)
    UNTIL obj&=nxt_obj&
    '
  CASE &H4838,&H4700    !SHIFT/Cursor up / HOME
    nxt_obj&=@search_ob_flag(tree%,0,3,1)
    '
  CASE &H1C0D,&H720D    !RETURN/ENTER
    nxt_obj&=@search_ob_flag(tree%,0,1,1)  !Default-Objekt vorhanden?
    IF nxt_obj&                            !ja, dann Mausklick simulieren
      RETURN FORM_BUTTON(tree%,nxt_obj&,1,nxt_obj&)
    ELSE       !kein Default-Objekt
      nxt_obj&=@search_ob_flag(tree%,obj&,3,1)   !nächstes Textobjekt suchen
      IF nxt_obj&=obj& AND @search_ob_flag(tree%,0,2,1)=0
        RETURN FALSE         !kein Exit-Obj und Cursor im letzten Textobjekt
      ENDIF
    ENDIF
    '
  DEFAULT   !keine Steuertaste => Objekt mit passender Tastenbelegung suchen
    key_obj&=@search_key_obj(tree%,kstate&,ev_key&)
    IF key_obj&                                     !Objekt gefunden
      RETURN FORM_BUTTON(tree%,key_obj&,1,nxt_obj&) !Mausklick simulieren
    ENDIF
    nxt_obj&=obj&
    nxtchar&=ev_key&
  ENDSELECT
  '
  RETURN TRUE
  '
ENDFUNC
FUNCTION search_key_obj(tree%,kstate&,ev_key&)
  LOCAL key|,obj&
  key|=SHR&(ev_key&,8)       !nur SCAN-Code
  '
  obj&=0
  REPEAT
    INC obj&
    IF key|=SHR&(OB_STATE(tree%,obj&),8) AND kstate&=SHR&(OB_FLAGS(tree%,obj&),13)
      RETURN obj&
    ENDIF
  UNTIL BTST(OB_FLAGS(tree%,obj&),5)
  '
  RETURN 0
ENDFUNC
FUNCTION search_ob_flag(tree%,startobj&,bitnr|,direction&)
  LOCAL obj&
  obj&=startobj&
  REPEAT
    ADD obj&,direction&
    IF BTST(OB_FLAGS(tree%,obj&),bitnr|)
      RETURN obj&
    ENDIF
  UNTIL (obj&<=0) OR BTST(OB_FLAGS(tree%,obj&),5)
  RETURN startobj&
ENDFUNC
'

FORM_DO.LST

' ********************************************
' * KEY RESOURCE EDITOR für EXTENDED FORM_DO *
' *                                          *
' * I: 09.06.89       Tassilo Nitz           *
' * U: 16.06.89       Krablerstr. 24         *
' *                   4300 Essen 12          *
' * (c) MAXON Computer 1989                  *
' ********************************************
'
'
init
main
'
> PROCEDURE init     !Vorbereitungen
  '
  KEYPAD 0       !NUMLOCK deaktivieren
  '
  DEFMOUSE 0     !Nur nötig, wenn das Programm
  SHOWM          !kompiliert werden soll
  '
  ' *** Platz für eigene RSC und edierte RSC ***
  RESERVE -40000
  '
  DIM c_shf&(2),m_tr&(7),tree_type|(50),tree_modify!(50)
  DIM tree_name$(50),ob_name$(50,256),trm$(6)
  ' *** RSC-Objekte definieren ***
  k_choice&=0
  c_clear&=1
  c_obname&=2
  c_shf&(0)=3
  c_shf&(1)=4
  c_shf&(2)=5
  c_key&=6
  main&=1
  m_file&=6
  m_trbox&=7
  m_tr&(0)=8
  m_tr&(1)=9
  m_tr&(2)=10
  m_tr&(3)=11
  m_tr&(4)=12
  m_tr&(5)=13
  m_tr&(6)=14
  m_tr&(7)=15
  m_scrlup&=16
  m_pbox&=17
  m_slider&=18
  m_load&=19
  m_save&=20
  m_edit&=21
  m_reset&=22
  m_scrldn&=23
  m_quit&=24
  ' *** Kürzel für Baum-Typen ***
  trm$(0)="U "  !UNKNOWN
  trm$(2)="M "  !MENU
  trm$(3)="  "  !DIALOG/PANEL/FREE-TREE
  trm$(4)="A "  !ALERT
  trm$(5)="S "  !FREE STRING
  trm$(6)="I "  !FREE IMAGE
  '
  pfad$="\"     !Zugriffspfad
  ' *** horiz. und vert. Auflösung bestimmen ***
  r&=XBIOS(4)
  hor_res&=320-320*(r&>0)-1
  vert_res&=200-200*(r&=2)-1
  ' *** Tastencode-Umwandlungstabellen ***
  keytbl%=XBIOS(16,L:-1,L:-1,L:-1)
  unshift_table%={keytbl%}
  shift_table%={keytbl%+4}
  ' *** Ausmaße eines Zeichens ***
  ~GRAF_HANDLE(charw&,charh&,dummy&,dummy&)
  ' *** RSC laden ***
  IF RSRC_LOAD("KEY_RCS.RSC")=0
    ALERT 1,"READ ERROR on RSC-File!",1,"END",dummy&
    ~RSRC_FREE()
    RESERVE
    END
  ENDIF
  ' *** Baumadressen holen ***
  ~RSRC_GADDR(0,k_choice&,k_choice_adr%)
  ~RSRC_GADDR(0,main&,main_adr%)
  ' *** Anfangsadresse und Länge der Resource ***
  global%={GB+4}  !Adresse des GEM-internen GLOBAL-Feldes
  prg_rsc_adr%={global%+14}  !in den Elementen 7 und 8 steht RSC-Adresse
  prg_rsc_len&=CARD{global%+18}  !Element 9 enthält Länge der Resource
  ' *** Index des ersten angezeigten Baums ***
  CLR topidx&
  ' *** Baumeinträge löschen ***
  tree%=main_adr%
  FOR i&=0 TO 7
    put_string(m_tr&(i&),"")
    del_state(m_tr&(i&),4)
  NEXT i&
  OB_Y(tree%,m_slider&)=0
  OB_H(tree%,m_slider&)=OB_H(tree%,m_pbox&)
  ' *** Auswahlbox darstellen ***
  CLS
  ~FORM_CENTER(tree%,fo_cx&,fo_cy&,fo_cw&,fo_ch&)
  ~OBJC_DRAW(tree%,0,8,fo_cx&,fo_cy&,fo_cw&,fo_ch&)
  '
RETURN
> PROCEDURE main
  LOCAL ex&,i&,w&,sy&,ypos&,dclick!
  REPEAT
    ex&=FORM_DO(tree%,0)
    dclick!=BTST(ex&,15)  !Doppelklick
    ex&=AND(ex&,&H7FFF)   !Bit 15 ausmaskieren
    '
    IF (ex&>=m_tr&(0) AND ex&<=m_tr&(7)) AND dclick!
      edit_tree(topidx&+ex&-m_tr&(0))   !Baum edieren
    ELSE IF ex&=m_edit&
      CLR i&
      DO UNTIL @selected(m_tr&(i&))
        INC i&
      LOOP WHILE i&<8
      IF i&<8
        edit_tree(topidx&+i&)   !Baum edieren
      ENDIF
      deselect(m_edit&,TRUE)
    ELSE IF ex&=m_reset&  !Tastenbelegungen löschen
      IF anz_trees&
        CLR i&    !ein Baum angewählt?
        DO UNTIL @selected(m_tr&(i&))
          INC i&
        LOOP WHILE i&<8
        IF i&<8   !ja, dann RESET nur für diesen Baum
          reset_tree(topidx&+i&)
          show_trees(topidx&,anz_struc&)
        ELSE      !ansonsten für komplette Resource
          ALERT 2,"Reset all trees?",2,"Yes|No",w&
          IF w&=1
            FOR i&=0 TO anz_trees&-1
              reset_tree(i&)
            NEXT i&
            show_trees(topidx&,anz_struc&)
          ENDIF
        ENDIF
      ENDIF
      deselect(m_reset&,TRUE)
    ELSE IF ex&=m_scrlup&   !Pfeil oben angeklickt
      IF topidx&>0
        DEC topidx&
        show_trees(topidx&,anz_struc&)
      ENDIF
    ELSE IF ex&=m_scrldn&   !Pfeil unten angeklickt
      IF topidx&+8<anz_struc&
        INC topidx&
        show_trees(topidx&,anz_struc&)
      ENDIF
    ELSE IF ex&=m_slider&   !Slider angeklickt
      IF anz_struc&>8
        ypos&=GRAF_SLIDEBOX(tree%,m_pbox&,m_slider&,1)
        topidx&=(ypos&+500/(anz_struc&-8))/1000*(anz_struc&-8)
        show_trees(topidx&,anz_struc&)
      ENDIF
    ELSE IF ex&=m_pbox&     !Parent von Slider angeklickt
      ~GRAF_MKSTATE(dummy&,ypos&,dummy&,dummy&)
      ~OBJC_OFFSET(tree%,m_slider&,dummy&,sy&)
      IF sy&<ypos&  !unterhalb vom Slider angeklickt
        topidx&=MIN(anz_struc&-8,topidx&+8)
      ELSE          !oberhalb
        topidx&=MAX(0,topidx&-8)
      ENDIF
      show_trees(topidx&,anz_struc&)
    ELSE IF ex&=m_load&     !RSC-File laden
      rsrc_laden
      deselect(m_load&,TRUE)
    ELSE IF ex&=m_save&     !RSC-File speichern
      IF rsc_load!
        rsrc_speichern
      ENDIF
      deselect(m_save&,TRUE)
    ELSE IF ex&=m_quit&     !PRG beenden
      ALERT 1,"QUIT ? ",2,"JA|NEIN",w&
      IF w&=2
        ex&=0
      ENDIF
      deselect(m_quit&,TRUE)
    ENDIF
  UNTIL ex&=m_quit&
  '
  IF rsc_load!    !wenn RSC geladen
    ~RSRC_FREE()    !Speicher freigeben
  ENDIF
  {global%+14}=prg_rsc_adr%   !Adresse und Länge der eigenen
  CARD{global%+18}=prg_rsc_len&  !RSC ins GLOBAL-Feld eintragen
  ~RSRC_FREE()                   !Speicher freigeben lassen
  RESERVE                        !GFA gets it back
RETURN
'
> PROCEDURE rsrc_speichern
  LOCAL backup$
  FILESELECT pfad$+"*.RSC",filename$,file$
  IF file$<>""
    filename_split(file$,pfad$,filename$,rawfile$)
    IF EXIST(file$)
      backup$=pfad$+rawfile$+".RSB"   !Backup-File
      IF EXIST(backup$)               !heißt "xxxxxx.RSB"
        KILL backup$
      ENDIF
      NAME file$ AS backup$
    ENDIF
    BSAVE file$,keyrsc_adr%,rsc_len&
    put_text(m_file&,filename$)       !neuen Filenamen
    ~OBJC_OFFSET(tree%,m_file&,x&,y&) !eintragen
    ~OBJC_DRAW(tree%,0,8,x&,y&,OB_W(tree%,m_file&),OB_H(tree%,m_file&))
  ENDIF
RETURN
> PROCEDURE filename_split(file$,VAR pfad$,filename$,rawfile$)
  LOCAL i&
  i&=RINSTR(file$,"\")       !'splittet' von FILESELECT
  pfad$=LEFT$(file$,i&)      !zurückgelieferte Datei in
  filename$=MID$(file$,i&+1) !Pfad und Dateinamen mit und
  i&=INSTR(filename$,".")    !ohne Extension
  IF i&
    rawfile$=LEFT$(filename$,i&-1)
  ELSE
    rawfile$=filename$
  ENDIF
RETURN
'
> PROCEDURE edit_tree(tidx&)            !Edieren eines Baums
  LOCAL n$,edtree%,keytree%,mx&,my&,but&,ks&
  LOCAL ox&,oy&,ow&,oh&,obj&
  ~RSRC_GADDR(0,tidx&,edtree%)  !Baumadresse der dargestellten Dial.box
  CLS
  tree%=edtree%
  keytree%=@get_keytree(tidx&)   !Baumadresse der edierten Dialogbox
  IF tree_name$(tidx&)<>""       !Name des Baums
    n$=tree_name$(tidx&)
  ELSE
    n$="[NONAME]"
  ENDIF
  PRINT AT(34,1);"TREE: ";n$
  ~FORM_CENTER(tree%,fo_cx&,fo_cy&,fo_cw&,fo_ch&)  !Box zeichnen
  ~OBJC_DRAW(tree%,0,8,fo_cx&,fo_cy&,fo_cw&,fo_ch&)
  '
  DO
    ~EVNT_BUTTON(1,1,1,mx&,my&,but&,ks&) !wartet auf Mausklick
    EXIT IF (but&=&X11) OR ks&    !Ausgang bei Druck auf beide Maustasten
    '                             !oder Mausklick mit SHIFT,CTRL oder ALT
    obj&=OBJC_FIND(tree%,0,8,mx&,my&)  !Obj unter Mauszeiger suchen
    '
    IF obj&>0     !Objekt gefunden (außer Baumobjekt 0)
      ~OBJC_OFFSET(tree%,obj&,ox&,oy&)  !Ausmaße des Objekts
      ow&=OB_W(tree%,obj&)              !berechnen
      oh&=OB_H(tree%,obj&)
      IF @selected(obj&)        !bereits selektiert?
        del_state(obj&,1)         !dann de-selektieren
      ELSE                      !ansonsten
        set_state(obj&,1)         !selektieren
      ENDIF
      ~OBJC_DRAW(tree%,0,8,ox&,oy&,ow&,oh&)  !Obj mit neuem STATE zeichnen
      ed_obj(tidx&,obj&,mx&,my&)             !und edieren
      tree%=edtree%                          !aktuelle Baumadresse neu setzen
      IF @selected(obj&)                     !und Obj wieder in altem
        del_state(obj&,1)                    !Zustand darstellen
      ELSE
        set_state(obj&,1)
      ENDIF
      ~OBJC_DRAW(tree%,0,8,ox&,oy&,ow&,oh&)
    ENDIF
    '
  LOOP
  '
  tree%=main_adr%    !aktuelle Baumadresse
  CLS
  ~FORM_CENTER(tree%,fo_cx&,fo_cy&,fo_cw&,fo_ch&)  !Auswahlbox zeichnen
  ~OBJC_DRAW(tree%,0,8,fo_cx&,fo_cy&,fo_cw&,fo_ch&)
  check_tree_modify(tidx&)    !sind im edierten Baum Tastenbel. gespeichert?
  show_trees(topidx&,anz_struc&)  !Ausschnitt neu darstellen
RETURN
> PROCEDURE ed_obj(tidx&,obj&,mx&,my&)  !Edieren eines Objekts
  LOCAL back$,drx&,dry&,drw&,drh&,maxx&,maxy&,key|,kstate|
  LOCAL mes&,kst&,t&,fund!,c.obj&
  tree%=k_choice_adr%   !Tastenauswahlbox aktuelle Baumadresse
  '
  drw&=OB_W(tree%,0)+4   !Koordinaten und Ausmaße der Box
  drh&=OB_H(tree%,0)+4   !berechnen
  maxx&=hor_res&-drw&
  maxy&=vert_res&-drh&
  drx&=MIN(mx&,maxx&)
  dry&=MIN(my&,maxy&)
  OB_X(tree%,0)=drx&+1
  OB_Y(tree%,0)=dry&+1
  '
  IF ob_name$(tidx&,obj&)=""   !Namen des Objekts in Box eintragen
    put_string(c_obname&,"[NONAME]")
  ELSE
    put_string(c_obname&,ob_name$(tidx&,obj&))
  ENDIF
  '
  GET drx&,dry&,drx&+drw&,dry&+drh&,back$  !Hintergrund retten
  '
  IF BTST(OB_FLAGS(keytree%,obj&),1)   !Default-Objekt?
    show_key(0,0)                      !wenn ja, dann nicht edierbar
    put_text(c_key&,"DEFAULT-OBJ")
    ~OBJC_DRAW(tree%,0,8,drx&,dry&,drw&,drh&)
    ~EVNT_BUTTON(1,1,1)
  ELSE
    '
    key|=SHR&(OB_STATE(keytree%,obj&),8)      !bisherige Tastenbelegung
    kstate|=SHR&(OB_FLAGS(keytree%,obj&),13)  !des Objekts
    '
    show_key(key|,kstate|)   !in Box eintragen
    '
    ~OBJC_DRAW(tree%,0,8,drx&,dry&,drw&,drh&)  !Box zeichnen
    '
    c.obj&=0
    '
    REPEAT
      ' auf Tastendruck oder Mausklick warten
      mes&=EVNT_MULTI(&X11,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,mx&,my&,dummy&,kst&,t&,dummy&)
      '
      IF BTST(mes&,0)     !Taste gedrückt
        '
        IF BTST(kst&,0)      !zwischen rechter und linker
          kst&=BSET(kst&,1)  !SHIFT-Taste wird nicht
        ENDIF                !unterschieden
        kstate|=AND(SHR&(kst&,1),&X111) !SHIFT/CONTRL/ALT
        key|=SHR&(t&,8)      !Scan-Code der gedrückten Taste
        '
        CLR o&
        REPEAT        !prüfen, ob Tastenbelegung bereits
          INC o&      !vorhanden ist
          fund!=(SHR&(OB_STATE(keytree%,o&),8)=key| AND SHR&(OB_FLAGS(keytree%,o&),13)=kstate| AND o&<>obj&)
          EXIT IF fund!
        UNTIL BTST(OB_FLAGS(keytree%,o&),5)
        '
        IF fund!
          ALERT 1,"Tastenzuordnung bereits|vorhanden!",1,"RETURN",dummy&
        ELSE IF key|=28 OR key|=114
          ALERT 1,"Kein RETURN/ENTER möglich!",1,"OK",dummy&
        ELSE    !alles ok
          ' neue Tastenbelegung in Objekt-Struktur eintragen
          OB_STATE(keytree%,obj&)=OR(AND(OB_STATE(keytree%,obj&),&HFF),SHL&(key|,8))
          OB_FLAGS(keytree%,obj&)=OR(AND(OB_FLAGS(keytree%,obj&),&H1FF),SHL&(kstate|,13))
          '
          show_key(key|,kstate|)   !Tastenbelegung anzeigen
          ~OBJC_OFFSET(tree%,0,x&,y&)
          ~OBJC_DRAW(tree%,0,1,x&+1,y&+1,OB_W(tree%,0)-2,OB_H(tree%,0)-2)
          '
        ENDIF
      ELSE   !Maustaste gedrückt
        c.obj&=OBJC_FIND(tree%,0,1,mx&,my&) !Objekt unter Mauszeiger
        IF c.obj&=c_clear&    !war es der CLR-Button?
          ' Tastenbelegung des Objekts löschen
          OB_STATE(keytree%,obj&)=AND(OB_STATE(keytree%,obj&),&HFF)
          OB_FLAGS(keytree%,obj&)=AND(OB_FLAGS(keytree%,obj&),&H1FF)
          show_key(0,0)   !Eintrag in Box löschen
          draw_obj(c_shf&(0))  !und Objekte neu zeichnen
          draw_obj(c_shf&(1))
          draw_obj(c_shf&(2))
          draw_obj(c_key&)
        ENDIF
      ENDIF
      '
    UNTIL c.obj&=-1   !bis außerhalb der Box geklickt wurde
  ENDIF
  '
  PUT drx&,dry&,back$  !Hintergrund wiederherstellen
  '
RETURN
> PROCEDURE show_key(k|,state|)         !Taste im Klartext darstellen
  LOCAL i&,k$
  '
  FOR i&=0 TO 2
    IF BTST(state|,i&)
      select(c_shf&(i&),FALSE)
    ELSE
      deselect(c_shf&(i&),FALSE)
    ENDIF
  NEXT i&
  '
  IF k|
    k$=""
    SELECT k|
    CASE 2 TO 13,16 TO 27,30 TO 41,43,44 TO 53,120 TO 131
      ADD k|,118*(k|>53)   !für ALT + 1,2,3,...
      IF BTST(state|,0)    !SHIFT
        k$=CHR$(BYTE{shift_table%+k|})   !ASCII-Code aus SHIFT-Tabelle
      ELSE
        k$=CHR$(BYTE{unshift_table%+k|}) !ASCII-Code aus UNSHIFT-Tabelle
      ENDIF
    CASE 1
      k$="ESCAPE"
    CASE 14
      k$="BACK"
    CASE 15
      k$="TAB"
    CASE 28
      k$="RETURN"
    CASE 57
      k$="SPACE"
    CASE 59 TO 68,84 TO 93    !Funktionstasten
      k$="F"+STR$(k|-58+25*(k|>68))
      ' Cursorblock
    CASE 71,119
      k$="HOME"
    CASE 72
      k$=""
    CASE 75,115
      k$=""
    CASE 77,116
      k$=""
    CASE 80
      k$=""
    CASE 82
      k$="INSERT"
    CASE 83
      k$="DELETE"
    CASE 97
      k$="UNDO"
    CASE 98
      k$="HELP"
      ' Ziffernblock
    CASE 99 TO 113,74,78
      k$=CHR$(BYTE{unshift_table%+k|})+" Zi"
    CASE 114
      k$="ENTER"
    ENDSELECT
    put_text(c_key&,"Taste: "+k$)
  ELSE
    put_text(c_key&,"")
  ENDIF
  '
RETURN
'
> PROCEDURE reset_tree(tidx&)           !Tastenbelegungen eines Baums löschen
  LOCAL obj&,adr%
  adr%=@get_keytree(tidx&)
  obj&=-1
  REPEAT
    INC obj&
    OB_STATE(adr%,obj&)=AND(OB_STATE(adr%,obj&),&HFF)
    OB_FLAGS(adr%,obj&)=AND(OB_FLAGS(adr%,obj&),&H1FF)
  UNTIL BTST(OB_FLAGS(adr%,obj&),5)   !bis LASTOB
  tree_modify!(tidx&)=FALSE   !Flag => Tree ist 'sauber'
RETURN
> PROCEDURE show_trees(idx&,anz&)       !Bäume namentlich ab idx& anzeigen
  LOCAL z&,n$,type|,x&,y&
  CLR z&
  '
  WHILE z&<8 AND z&+idx&<anz&
    type|=tree_type|(idx&+z&)
    IF tree_name$(idx&+z&)=""   !Name vorhanden?
      n$=trm$(type|)+"[NONAME]"   !nein, dann unbekannt
    ELSE
      n$=trm$(type|)+tree_name$(idx&+z&)  !sonst übernehmen
    ENDIF
    put_string(m_tr&(z&),n$)
    IF tree_type|(idx&+z&)<>3     !kein Dialog
      OB_STATE(tree%,m_tr&(z&))=8   !also DISABLED darstellen
      del_flag(m_tr&(z&),65)        !und nicht SELECTABLE/TOUCHEXIT
    ELSE
      IF tree_modify!(idx&+z&)    !Tastenbelegungen gespeichert?
        OB_STATE(tree%,m_tr&(z&))=4  !ja, dann CHECKED
      ELSE
        OB_STATE(tree%,m_tr&(z&))=0
      ENDIF
      set_flag(m_tr&(z&),65)      !SELECTABLE+TOUCHEXIT
    ENDIF
    INC z&
  WEND
  WHILE z&<8      !Rest des Fensters löschen
    OB_STATE(tree%,m_tr&(z&))=0
    del_flag(m_tr&(z&),65)
    put_string(m_tr&(z&),"")
    INC z&
  WEND
  ' Slider-Position und -Größe bestimmen
  IF anz&<=8
    OB_Y(tree%,m_slider&)=0
    OB_H(tree%,m_slider&)=OB_H(tree%,m_pbox&)
  ELSE
    OB_Y(tree%,m_slider&)=OB_H(tree%,m_pbox&)*idx&/anz&
    OB_H(tree%,m_slider&)=OB_H(tree%,m_pbox&)*8/anz&
  ENDIF
  ' Slider und Fensterausschnitt mit Baumnamen neuzeichnen
  ~OBJC_OFFSET(tree%,m_trbox&,x&,y&)
  ~OBJC_DRAW(tree%,m_trbox&,1,x&+2,y&,OB_W(tree%,m_trbox&)-4,OB_H(tree%,m_trbox&))
  ~OBJC_OFFSET(tree%,m_pbox&,x&,y&)
  ~OBJC_DRAW(tree%,m_pbox&,1,x&,y&,OB_W(tree%,m_pbox&),OB_H(tree%,m_pbox&)-1)
  '
RETURN
> PROCEDURE check_all_structs           !manuelle Typzuweisung
  LOCAL obj&,t&,adr%,i&
  ' zunächst alle Objektbäume überprüfen
  CLR t&
  WHILE t&<anz_trees&
    ~RSRC_GADDR(0,t&,adr%)
    obj&=-1
    REPEAT
      INC obj&
      EXIT IF OB_TYPE(adr%,obj&)=32     !G_TITLE => MENU-TREE
      EXIT IF AND(OB_STATE(adr%,obj&),&HFF00)  !Tastenbelegung
    UNTIL BTST(OB_FLAGS(adr%,obj&),5)   !bis LASTOB
    tree_modify!(t&)=AND(OB_STATE(adr%,obj&),&HFF00)
    IF OB_TYPE(adr%,obj&)=32
      tree_type|(t&)=2    !Menü
    ELSE
      tree_type|(t&)=3    !Dialog
    ENDIF
    INC t&
  WEND
  ' jetzt die FREE STRINGS
  CLR i&
  WHILE i&<anz_frestr&
    ~RSRC_GADDR(15,i&,adr%)
    x$=LEFT$(CHAR{{adr%}},3)
    IF x$="[0]" OR x$="[1]" OR x$="[2]" OR x$="[3]"  !auf ALERT prüfen
      tree_type|(t&+i&)=4   !ALERT
    ELSE
      tree_type|(t&+i&)=5   !sonstiger FREE STRING
    ENDIF
    INC i&
  WEND
  ' zum Schluß die FREE IMAGES
  WHILE i&<anz_freimg&
    tree_type|(t&+i&)=6
    INC i&
  WEND
  '
RETURN
> PROCEDURE check_tree_modify(tidx&)    !prüft auf Tastenbelegungen in einem Baum
  LOCAL obj&,adr%
  '
  adr%=@get_keytree(tidx&)
  obj&=-1
  REPEAT
    INC obj&
    EXIT IF AND(OB_STATE(adr%,obj&),&HFF00)
  UNTIL BTST(OB_FLAGS(adr%,obj&),5)   !bis LASTOB
  tree_modify!(tidx&)=AND(OB_STATE(adr%,obj&),&HFF00)
RETURN
> PROCEDURE rsrc_laden                  !Laden eines Resource-Files
  LOCAL laenge%,i&,x&,y&
  FILESELECT pfad$+"*.RSC",filename$,file$
  IF file$<>""
    IF EXIST(file$)
      '
      filename_split(file$,pfad$,filename$,rawfile$)
      '
      IF RIGHT$(filename$,4)=".RSC"
        IF rsc_load!     !war schon eine Resource geladen?
          ~RSRC_FREE()     !ja, dann Speicher freigeben
          ERASE resource|()  !Duplikat löschen
        ENDIF
        IF RSRC_LOAD(file$)=0  !laden
          ALERT 1,"RSC READ ERROR!",1,"RETURN",dummy&
          rsc_load!=FALSE
        ELSE
          rsc_load!=TRUE    !Flag für geladene RSC setzen
          rsc_adr%={global%+14}    !RSC-Adresse und Länge
          rsc_len&=CARD{global%+18} !stehen im GEM-internen GLOBAL-Feld
          anz_trees&=CARD{rsc_adr%+22}  !wichtige Informationen aus
          anz_frestr&=CARD{rsc_adr%+30} !RSC-Header: Anzahl der Objektbäume,
          anz_freimg&=CARD{rsc_adr%+32} !Anzahl FREE STRINGS/FREE IMAGES
          anz_struc&=anz_trees&+anz_frestr&+anz_freimg&  !Gesamtzahl
          '
          ERASE tree_type|(),tree_modify!(),tree_name$(),ob_name$()
          ~FRE(0)  !erzwungene Garbage-Collection
          ' neue Dimensionierung
          DIM tree_type|(anz_struc&-1),tree_modify!(anz_trees&-1)
          DIM tree_name$(anz_struc&-1),ob_name$(anz_trees&-1,255)
          '
          OPEN "I",#1,file$    !Duplikat laden => die Tastenbelegungen werden
          laenge%=LOF(#1)      !nur im Duplikat vorgenommen, das erspart das
          DIM resource|(laenge%)  !Konvertieren von Adressen und Koordinaten
          keyrsc_adr%=V:resource|(0)  !vor dem Abspeichern
          BGET #1,keyrsc_adr%,laenge%
          CLOSE #1
          keyrsc_ttab%=CARD{keyrsc_adr%+18}+keyrsc_adr%  !Baum-Adreßtabelle
          '
          IF @get_dfn_file(pfad$+rawfile$)  !Definitionsfile laden
            FOR i&=0 TO anz_trees&-1   !erfolgreich, dann alle Bäume auf
              check_tree_modify(i&)    !Tastenbelegungen prüfen
            NEXT i&
          ELSE                      !kein Definitionsfile vorhanden
            check_all_structs         !manuelle Typzuweisung
          ENDIF
          '
          topidx&=0                 !Index des ersten dargestellten Baums
          show_trees(topidx&,anz_struc&)  !Bäume namentlich darstellen
          '
          put_text(m_file&,filename$)  !Filenamen eintragen
          ~OBJC_OFFSET(tree%,m_file&,x&,y&)  !und darstellen
          ~OBJC_DRAW(tree%,0,8,x&,y&,OB_W(tree%,m_file&),OB_H(tree%,m_file&))
        ENDIF
      ELSE   !kein RSC-File
        ALERT 1,"NO RSC-FILE!",1,"CANCEL",dummy&
      ENDIF
    ELSE   !File nicht gefunden
      ALERT 1,"FILE NOT FOUND!",1,"RETURN",dummy&
    ENDIF
  ENDIF
RETURN
' get_keytree holt die Adresse eines zu edierenden Objektbaums
DEFFN get_keytree(idx&)={keyrsc_ttab%+4*idx&}+keyrsc_adr%
FUNCTION load_def_rsd(file$,dri!) !lädt DEF-Datei vom Kuma- oder DRI-RCS V1.x
  ' je nach Flag dri! wird entweder *.DEF vom DRI-RCS V1.x
  ' oder *.RSD vom Kuma-RCS geladen
  LOCAL adr%,type|,laenge%,anz&,i&,p%,offs&
  '
  OPEN "I",#1,file$
  '
  laenge%=LOF(#1)
  '
  anz&=CVI(INPUT$(2,#1))   !Anzahl der Datensätze im MOTOROLA-Format
  SEEK #1,0
  IF dri!     !beim DRI *.DEF-File gehören die ersten beiden
    SUB laenge%,2   !Bytes nicht zum ersten Datensatz
  ENDIF
  IF anz&<>laenge%/16    !ein Datensatz belegt 16 Bytes
    CLOSE #1
    ALERT 1,"BAD FILE FORMAT!",1,"RETURN",dummy&
    RETURN FALSE
  ELSE
    DIM defbuf|(laenge%+1)            !letztes Byte muß Nullbyte sein
    adr%=V:defbuf|(0)
    BGET #1,adr%,laenge%   !Definitionsdatei laden
    CLOSE #1
    '
    CLR i&
    REPEAT
      p%=ADD(adr%,MUL(i&,16))
      type|=BYTE{ADD(p%,7)}
      IF BYTE{ADD(p%,6)}=0
        SELECT type|
        CASE 0 TO 3    !Unknown, Free-Tree, Dialog- oder Menü-Baum
          IF type|=1     !Free-Tree
            type|=3        !konvertieren zu Dialog
          ENDIF
          offs&=0
        CASE 4,5       !Alert oder Free String
          offs&=anz_trees&
        DEFAULT
          offs&=ADD(anz_trees&,anz_frestr&)
        ENDSELECT
        tree_name$(ADD(BYTE{ADD(p%,5)},offs&))=CHAR{ADD(p%,8)}
        tree_type|(ADD(BYTE{ADD(p%,5)},offs&))=type|
      ELSE
        ob_name$(BYTE{ADD(p%,4)},BYTE{ADD(p%,5)})=CHAR{ADD(p%,8)}
      ENDIF
      '
      INC i&
    UNTIL i&=anz&
    '
    ERASE defbuf|()
    RETURN TRUE
  ENDIF
ENDFUNC
FUNCTION load_dfn(file$)   !lädt DEF-Datei (*.DFN) vom DRI-RCS V2.x
  LOCAL adr%,a%,type|,subtype|,laenge%,anz&,i&,p%,offs&
  '
  OPEN "I",#1,file$
  '
  laenge%=LOF(#1)-2
  '
  a%=CVI(INPUT$(2,#1))   !Anzahl Datensätze im INTEL-Format
  anz&=SHL&(a%,8)+SHR&(a%,8)  !so wird's PC-kompatibel
  IF anz&<>laenge%/14    !1 Datensatz belegt 14 Bytes
    CLOSE #1
    ALERT 1,"BAD FILE FORMAT!",1,"RETURN",dummy&
    RETURN FALSE
  ELSE
    DIM defbuf|(laenge%)
    adr%=V:defbuf|(0)
    BGET #1,adr%,laenge%
    CLOSE #1
    '
    CLR i&
    REPEAT
      p%=ADD(adr%,MUL(i&,14))
      type|=BYTE{ADD(p%,2)}
      subtype|=BYTE{ADD(p%,3)}
      IF type|<>0 OR subtype|=0
        IF subtype|                     !Free String oder Free Image
          IF type|=1     !Free String
            type|=5        !konvertieren
          ELSE           !Free Image
            type|=6        !konvertieren
          ENDIF
        ENDIF
        SELECT type|
        CASE 0 TO 3                 !Unknown/Panel-/Dialog- oder Menü-Baum
          IF type|=1   !Panel
            type|=3      !wird konvertiert zu Dialog (3)
          ENDIF
          offs&=0
        CASE 4,5                      !Alert
          offs&=anz_trees&
        DEFAULT
          offs&=ADD(anz_trees&,anz_frestr&)
        ENDSELECT
        tree_name$(ADD(BYTE{p%},offs&))=CHAR{ADD(p%,4)}
        tree_type|(ADD(BYTE{p%},offs&))=type|
      ELSE
        ob_name$(BYTE{SUCC(p%)},BYTE{p%})=CHAR{ADD(p%,4)}
      ENDIF
      '
      INC i&
    UNTIL i&=anz&
    '
    ERASE defbuf|()
    RETURN TRUE
  ENDIF
ENDFUNC
FUNCTION get_dfn_file(file$)  !DEF-File laden von Kuma oder DRI (Vers.1+2)
  IF EXIST(file$+".DFN")
    RETURN @load_dfn(file$+".DFN")
  ELSE IF EXIST(file$+".DEF")
    RETURN @load_def_rsd(file$+".DEF",TRUE)
  ELSE IF EXIST(file$+".RSD")
    RETURN @load_def_rsd(file$+".RSD",FALSE)
  ELSE
    RETURN FALSE
  ENDIF
ENDFUNC
' nützliche Prozeduren im Umgang mit Resourcen
> PROCEDURE draw_obj(obj&)
  LOCAL objdx&,objdy&
  ~OBJC_OFFSET(tree%,obj&,objdx&,objdy&)
  ~OBJC_DRAW(tree%,obj&,8,objdx&,objdy&,OB_W(tree%,obj&),OB_H(tree%,obj&))
RETURN
> PROCEDURE set_state(obj&,val&)
  OB_STATE(tree%,obj&)=OR(OB_STATE(tree%,obj&),val&)
RETURN
> PROCEDURE del_state(obj&,val&)
  OB_STATE(tree%,obj&)=AND(OB_STATE(tree%,obj&),NOT val&)
RETURN
> PROCEDURE select(obj&,redraw!)
  set_state(obj&,1)
  IF redraw!
    draw_obj(obj&)
  ENDIF
RETURN
> PROCEDURE deselect(obj&,redraw!)
  del_state(obj&,1)
  IF redraw!
    draw_obj(obj&)
  ENDIF
RETURN
DEFFN selected(obj&)=OB_STATE(tree%,obj&) AND 1
> PROCEDURE set_flag(obj&,val&)
  OB_FLAGS(tree%,obj&)=OR(OB_FLAGS(tree%,obj&),val&)
RETURN
> PROCEDURE del_flag(obj&,val&)
  OB_FLAGS(tree%,obj&)=AND(OB_FLAGS(tree%,obj&),NOT val&)
RETURN
> PROCEDURE put_string(obj&,str_txt$)
  CHAR{OB_SPEC(tree%,obj&)}=str_txt$
RETURN
DEFFN rsc_string$(obj&)=CHAR{OB_SPEC(tree%,obj&)}
> PROCEDURE put_text(obj&,txt$)  !G_TEXT schreiben
  CHAR{{OB_SPEC(tree%,obj&)}}=txt$
RETURN
DEFFN rsc_text$(obj&)=CHAR{{OB_SPEC(tree%,obj&)}}

KEY_RCS.LST


Tassilo Nitz
Aus: ST-Computer 11 / 1989, Seite 100

Links

Copyright-Bestimmungen: siehe Über diese Seite