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.
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.
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
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.
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ß.
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.
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".
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.
Literatur:
Atari ST Profibuch, Sybex Verlag ST-Computer 8/87 S.20 ff
' ************************************
' * 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