Weg mit den Resource-Files: Resourcen mit Inline direkt in den GFA-Basic-Programmtext

Um Objektbäume (Dialog-Boxen etc.), die mit einem Resource-Constructionset erstellt und direkt in den Programmtext eingebunden wurden, benutzen zu können, reicht es (fast), rsrc_load() gegen die hier vorgestellte Funktion rsrc_conv() auszutauschen - alle anderen Objektfunktionen können unverändert weiter verwendet werden. Ausserdem: Objekte immer in der Bildschirmmitte? Nein danke!

Einen Wermutstropfen gleich vorweg: Die hier gebotene Information läßt sich - zumindest ohne Veränderungen (s.u.) - nur mit GFA-BASIC 3.x nutzen. Vielleicht dient sie aber dem einen oder anderen als Idee, um sie in eine ältere GFA-BASIC-Version oder auch OMIKRON.BASIC oder gar eine andere Hochsprache (z.B. Pascal) umzusetzen - nur Mut!

Seit Erscheinen der Version 3.0 wären selbstgebastelte Dialogboxen oder am Ende der völlige Verzicht darauf in GFA-BASIC-Programmen nun wirklich nicht mehr nötig, da zum einen alle Objektbefehle in der AES-Bibliothek enthalten sind und zum anderen das zur Erzeugung der Objektbäume nötige Werkzeug, nämlich ein Resource-Construction-Set (kurz: RCS), in Gestalt des „RCS2.PRG“ von Digital Research auf der GFA-BASIC-Diskette gleich mitgeliefert wird.

Lästig

GFA-BASIC-3.X stellt in bezug auf Resourcen eigentlich die „Eier legende Wollmilchsau“ dar. Wenn es doch nur nicht diese lästigen *.RSC-Files gäbe! Da läuft das Programm nicht, weil es das dazugehörige *.RSC-File nicht finden kann (das braucht Sie nicht zu wundem, Sie haben dann wohl vergessen, es zu kopieren, oder es steht im falschen Ordner). Und ist es doch noch fündig geworden (das sollte Sie jetzt aber wundem, weil unnötig viele Einträge im Directory stehen: zu jedem *.PRG-File ein * INF-, *.PIC- und *.RSC-File!), läuft es trotzdem nicht (mit der lapidaren Fehlermeldung: „Daten auf Disk x: defekt?!“). Oder in der Testphase: nach jedem RUN erstmal das *.RSC-File laden - und dann ein lehrbuchmäßiger Absturz, der die, durch das unzertrennbare Paar RESERVE - mem% / ~RSRC_LOAD( „rsc_name“) modifizierte, Speicherverwaltung total auf den Kopf stellt (aber mit etwas Übung erreicht man den RESET-Knopf doch). Zu guter Letzt begegnet Ihnen nach Wochen ein mühsam erstelltes Programm wieder, diesmal jedoch mit völlig verändertem „Outfit“, weil das irgend ein Scherzbold mal eben mit einem RCS überarbeitet hat (diese Möglichkeit läßt sich nie ganz vermeiden, aber erschweren).

Ich neige zu Übertreibungen? Dann schalten Sie Ihren Computer ruhig öfter ein: Sie werden diese und noch ganz andere Fallstricke entdecken!

Es geht auch anders!

Für Verbesserungen besteht also „Handlungsbedarf“. Eine Möglichkeit wäre das Erstellen der nötigen Objektstrukturen ganz ohne Hilfsmittel - alle Befehle dazu gibt es in handlicher Form in der AES-Bibliothek. Mancher, vor allem der Programmierer von Accessories, hat diese Wahnsinnstat schon begangen und ist nicht unbedingt glücklich dabei geworden. Besser wäre es schon, wenn sich das original Resource-File irgendwie in den Programmtext packen ließe...

Resource-Files werden für gewöhnlich mit der AES-Funktion 110 - RSRC_LOAD() -oberhalb von HIMEM in den zuvor mit RESERVE reservierten Speicher geladen. Der erste Schritt zum gewünschten Ziel ist es, RSRC_LOAD() durch INLINE zu ersetzen.

INLINE

Der INLINE-Befehl legt beliebige Daten innerhalb des Programmtextes ab (das können Bilder, Assembler-Programme oder eben auch Resource-Daten sein). Die vollständige Syntax lautet:

INLINE adresse%,anzahl

wobei adresse% eine Integer-Variable ist, in der INLINE die Adresse zurückgibt, an der die Daten im Speicher stehen, anzahl ist eine Konstante (keine Variable - leider!) und nennt in Bytes den Bereich, der im Programmtext für die Daten reserviert wird; anzahl sollte exakt der Länge des einzubindenden *.RSC-Files entsprechen: ist anzahl größer, so verschwenden Sie Speicherplatz, und ist anzahl kleiner als die File-Länge, geht Ihnen u.a. die lebenswichtige Objektbaumtabelle verloren, die den Schluß des *.RSC-Files bildet, und ohne die nichts läuft (s. Bilder 1 und 2).

Bild 1: Der Aufbau von *.RSC-Files

Erstellen Sie also wie gewohnt Ihr *.RSC-File. In Bild 3 sehen Sie eine sehr einfache Dialogbox, die die Grundlage für das Beispielprogramm - Listing 3 - bildet (Einsteiger in die Materie finden am Schluß des Artikels alle benötigten Angaben zur Handhabung des RCS).

Das *.RSC-File binden Sie durch

INLINE rsc_adr%,368

in Ihr Programm ein. Diesen Befehl finden Sie in Listing 3, Zeile 37. Die „368“ sollten Sie aus den o.g. Gründen allerdings tunlichst durch die tatsächliche Länge Ihres Files ersetzen, welche Sie entweder aus dem erweiterten Directory (also Directory als Text) bzw. mit der Fileselect-Box (wenn Sie eine erweiterte Version besitzen) erfahren. Wenn Sie in dieser Zeile die RETURN-Taste drücken, erfolgt der Syntax-Check durch den Editor, und im Programmtext werden (beispielsweise) 368 Null-Bytes untergebracht. Jetzt gehen Sie mit dem Cursor wieder in die INLINE-Zeile, drücken erst die HELP-Taste (worauf sich die obere Menüzeile des Editors verändern sollte), als nächstes die Taste „L“ (jetzt sollte eine Fileselect-Box erscheinen) und laden Ihr *.RSC-File (zeigt sich in der Fileselect-Box nicht freiwillig, da der voreingestellte Suchpfad mit der Extension „.INL“ endet). Danach befinden sich die Resource-Daten im Programmtext und werden auch mit gespeichert, wenn das Programm mit dem SAVE-Befehl gespeichert wird (nach dem Laden eines mit SAVE,A gespeicherten Programms durch MERGE sind statt der Daten jedoch wieder nur entsprechend viele Null-Bytes vorhanden!).

Hier noch ein Tip, wie Sie die Länge Ihres *.RSC-Files auch herausfinden können: Setzen Sie vorübergehend für anzahl den maximal möglichen Wert von 32700 ein. Die Fehlermeldung „File-Ende erreicht | EOF“ beim Laden (s.o.) kann ignoriert werden. Rufen Sie rsrc_conv() anschließend nicht wie in Listing 3, Zeile 44, gezeigt auf, sondern mit

PRINT FN rsrc_conv (rsc_adr%,TRUE)

Die Länge sollte nun auf dem Bildschirm stehen. Das Beispielprogramm meldet sich in diesem Fall übrigens mit der Fehlermeldung aus Zeile 46 und bricht die Programmausführung ab.

Wahrscheinlich haben Sie es schon gemerkt: INLINE reserviert den für die Daten gebrauchten Speicherplatz. D.h., den Befehl RESERVE -mem% können Sie im Zusammenhang mit Resourcen getrost vergessen. Das hat außerdem zur Folge, daß das Programm nicht mehr mit RSRC_FREE() und erneutem RESERVE zur Rückgabe des Speichers beendet zu werden braucht bzw. darf!

Etwas anderes haben Sie allerdings vielleicht auch schon gemerkt (z.B. nach beherztem - aber voreiligem - Programmstart): Das Ziel ist noch nicht erreicht.

Die Funktion RSRC_LOAD(), die ja nun grundsätzlich durch INLINE ersetzt wurde, ist ein multifunktionales Talent: sie lädt nicht nur Resource-Files in den Speicher, sondern rechnet auch noch diverse Zeiger und Koordinaten um und teilt dem AES den Beginn der Objektbaumtabelle mit. Jetzt müssen Sie sich eben selbst darum kümmern (das haben Sie nun davon!). Listing 1 zeigt die Lösung des Problems. Wenn Sie sich die Kommentare wegdenken, bleibt - wie in den anderen beiden Listings - nicht mehr viel übrig. Außerdem kommen einige Zeilen mehrfach vor bzw. sind sich sehr ähnlich, so daß sich bei der Eingabe der Funktion viel Arbeit mit den Tasten ^DEL und ^U sparen läßt.

Bild 2: Objekte im Detail

Für Neugierige

Um Listing 1 mit der neuen Funktion rsrc_conv() verstehen zu können, muß man wissen, daß das *.RSC-File, wenn es frisch aus dem RCS kommt, für das AES noch unbrauchbar ist.

Ein Resource-File besteht aus festen und variablen Bestandteilen (Bild 1). Immer vorhanden ist der 36 Bytes lange File-Header mit Informationen über die Anzahl verschiedener Objektstrukturen und Zeigern, die in den nachfolgenden Datenbereich zeigen. Weil diese Zeiger alle Word-Format haben, ist die Größe einer Resource-Datei übrigens auf 64 kBytes begrenzt. Jedes Objekt (außer FREE-STRINGs und FREE-IMAGEs einschließlich Alert-Boxen) ist im Objektfeld mit einem 24 Bytes umfassenden Eintrag vertreten. Und jedem Baum (also mehrere Objekte zu dem Gebilde zusammengefaßt, das dann auf dem Bildschirm erscheint) wird in der Objektbaumtabelle ein 4 Bytes langer Zeiger spendiert. Außerdem reichen den meisten Objekten die 24-Byte-Einträge nicht aus, so daß es einen Zeiger im Objektfeld gibt (OB SPEC), der dann auf eine weitere Struktur (z.B. die berühmten „TEDINFO“-Blöcke) weist, in der nochmal bis zu drei Zeiger auf die eigentliche Information im Resource-File zeigen (C-Text und Image-Blöcke). Bild 2 versucht dies zu verdeutlichen. Alle dort stark umrandeten Felder müssen von rsrc_conv() so angepaßt werden, daß das AES mit den Daten etwas anfangen kann (sehr ausführlich in [1] beschrieben).

Da sind zum einen die Koordinaten OB_X und OB_Y sowie zum anderen die Werte für Breite OB_W und Höhe OB_H der Objekte. Diese vier Werte werden vom RCS zeichenorientiert angelegt, um die Resourcen unabhängig von der Auflösung zu machen. Sie müssen in pixelorientierte Werte entsprechend der Bildauflösung umgerechnet werden. Dazu gibt’s im AES schon die Funktion RSRC_OBFIX (Zeile 67).

Für das Modifizieren der Zeiger existiert keine AES-Funktion. Ab Zeile 69 erledigt rsrc_conv() diese Aufgabe. Abhängig von OB_TYPE müssen neben OB_SPEC selbst bis zu drei oder aber weder OB_SPEC noch ein Folgezeiger umgerechnet werden. Alle Zeiger werden vom RCS relativ zum File-Anfang angelegt und müssen in absolute Speicheradressen umgerechnet werden (außer OB_NEXT, OB_HEAD und OB_TAIL). Das angewendete Verfahren ist recht einfach: Der Beginn des „rohen“ *.RSC-Files wird so behandelt, als läge er an der Speicheradresse null; INLINE legt die Daten natürlich an einer höheren Adresse ab - die daraus resultierende Differenz muß zu allen im *.RSC-File vorhandenen (absoluten) Adreßzeigern addiert werden.

Die Literaturflut zum Thema ist überwältigend. Stellvertretend seien [2], [3] (wie könnte es auch anders sein?!) und [4] genannt.

Der kleine Unterschied

Leider muß zwischen Compiler und Interpreter unterschieden werden: Das compilierte Programm wird direkt vom Desktop aus gestartet und kehrt nach Programmende auch dorthin wieder zurück (mit etwas Glück). D.h. bei jedem Programmstart werden sowohl das Programm selbst als auch die RSC-Daten immer neu von der (Hard-/RAM-)Disk geladen. Anders beim Interpreter: dort werden Programm und RSC-(INLINE-)Daten einmal geladen und können mittels RUN beliebig oft aus dem Speicher heraus gestartet werden. Die eben geschilderte Modifikation der Zeiger und Koordinaten findet dann bei jedem Programmstart mit RUN nochmal statt. Wenn aber die bereits geänderten Zeiger etc. erneut geändert werden, ist das Ergebnis nicht eben erfreulich (vielleicht äußert Ihr Computer ja wenigstens noch ein ganz kleines Lebenszeichen?!). Außerdem gibt es noch ein zweites Problem: OB_STATE, OB_FLAGS und der Inhalt edierbarer Objekte können sich während des Programmlaufs ändern. So beginnt ein Programm nach erneutem RUN nicht nur mit diesen geänderten Werten, sondern alle Änderungen werden beim Speichern des Programms mit SAVE auch noch auf die Diskette geschrieben. Deshalb werden die RSC-Daten, wenn das Programm im Interpreter läuft, in das Array rsc_bufl() kopiert (Listing 1, Zeilen 43 bis 48) und dem AES die Adresse der Kopie mitgeteilt. Das Kopieren kostet zwar Speicherplatz, ist aber, da nur im Interpreter erforderlich und der Compiler zum GFA-BASIC-Lieferumfang gehört, wohl vertretbar, zumal sich die nötige Unterscheidung - wie Sie nun sehen werden - elegant automatisieren läßt.

Ja, wo laufen wir denn?

Ob das Programm compiliert ist oder „interpretiert“ wird, läßt sich anhand des ersten Programm-Bytes feststellen. Das wiederum findet sich unmittelbar im Anschluß an die 256 Bytes lange BASEPAGE: Zumindest in der GFA-Version 3.0x beginnt der Interpreter mit dem Assembler-Befehl BRA.S, das entspricht dem Wert 60 (HEX) bzw. 96(Dez.), während Compilate mit dem LEA-Befehl beginnen, also 41 (HEX) bzw. 65(Dez.). Damit ist denn auch die Zeile 42 im Listing 3 erklärt.

Nachsendeantrag

Daß dem AES die neue Adresse der Objektbaumtabelle mitgeteilt wird, ist übrigens die Voraussetzung für die weitere und unveränderte Verwendbarkeit aller Objektfunktionen. Betrachten Sie z.B. die Zeile

~RSRC_GADDR(0, obj_ndx&, obj_adr%)

Mit dieser Funktion läßt sich jede Baumadresse bestimmen, wenn für Typ wie hier der Parameter 0 angegeben wird. obj_ndx& ist der Baumindex (oder die laufende Nummer) des Baumes, dessen Adresse Sie erfahren wollen und die in obj_adr% zurückgegeben wird. Diese Adresse übergeben Sie danach wiederum den entsprechenden Objektfunktionen als Referenz (vergl. z.B. Listing 3, Zeilen 52 und 54). Die eigentliche Adresse, an der die RSC-Daten im Speicher stehen (bzw. den Beginn der Objektbaumtabelle), erfahren Sie normalerweise gar nicht (wozu auch?), weil die dem AES von RSRC_LOAD() mitgeteilt wird. Jetzt erledigt das eben rsrc_conv(), und zwar mit den Befehlen in den Zeilen 47 bis 52. Hier werden die Words 5 und 6 (ap_ptree) des GEM-internen global-Feldes entsprechend belegt.

Einschränkung

Nichts hat nur Vorteile. Und so gibt es auch hier Nachteile: INLINE kann nur höchstens 32700 Bytes aufnehmen. Also können Resource-Files, die größer sind, nicht wie hier beschrieben in den Programmtext übernommen werden. Ferner gibt es Probleme, wenn Sie „Panel“-Objekte in verschiedenen Auflösungen benutzen wollen. Normalerweise werden in diesem Fall getrennte Resourcen für jede Auflösung angelegt. Nun sind die Resource-Daten aber fester Programmbestandteil. Also mehrere verschiedene Programmversionen?!?

Zugabe

Listing 2 zeigt zwei universell verwendbare Prozeduren, mit denen sich Objektbäume ausgeben und auch wieder entfernen lassen. Hier sind die Funktionen zusammengefaßt, die vor allem beim Einsatz von Objekten zusammen mit (GFA-)Me-nüs und Fenstern gebraucht werden. Das Restaurieren von Fenstern erledigt redraw_tree allerdings nicht; es wird lediglich eine entsprechende Mitteilung in den Message-Puffer geschrieben (Zeilen 79/80). Eigentlich gehört ein Window-Redraw in die Zeile 87 von Listing 3 - im Beispiel tut’s allerdings auch CLS. Die meisten Objektbäume besitzen einen Abbruch- und einen OK-Button. Wird nun ein Baum aufgerufen, der zuvor schon einmal durch Anklicken von z.B. „OK“ verlassen wurde, erscheint der entsprechende Button selektiert, d.h. schwarz. draw_tree() können deshalb zwei Indizes von Objekten übergeben werden, die das AES dann mit Normal-Status zeichnet (Zeilen 34 bis 42). Soll dies nicht geschehen, oder fehlen solche Objekte, dann übergeben Sie -1 statt der Indizes. Weitere Information zur Handhabung von Objekten, Windows (einschließlich Redraw) etc. findet sich u.a. in [5].

Immer in der Mitte?

„Handelsübliche“ Dialogboxen erscheinen immer brav in der Bildschirmmitte, was nicht nur langweilig ist, sondern auch unpraktisch, da oft unnötig lange Wege mit der Maus erforderlich werden. Wie Dialogboxen, z.B. ähnlich sog. Pop-Up-Menüs, immer an der momentanen Cursor-Position erscheinen (unter besonderer Berücksichtigung der Bildschirmgrenzen), sehen Sie in der draw_tree()-Routine (Listing 2, Zeilen 47 bis 60): Nachdem Sie FORM_CENTER() aufgerufen haben (dann sind die Variablen für Breite und Höhe der Box schon mal bedient), ändern Sie die Variablen obj_x& und obj_y& entsprechend - die sind für FORM_DIAL() und das Clipping bei OBJC_DRAW() zuständig - und die Koordinaten OB_X()/OB_Y() des Mutterobjektes (Index 0) bzw. ‘Wurzelobjektes’ (schließlich handelt es sich hier ja um Bäume). Statt MOUSEX/MOUSEY können Sie natürlich auch andere Werte einsetzen, so daß die Dialogboxen an von Ihnen definierten Stellen erscheinen. Außerdem könnten Sie jeweils den ersten Parameter in den beiden MAX()-Funktionen (Zeilen 54: 5 bzw. linker Rand und 57: 20 bzw. oberer Rand) sowie x_res& (=rechter Rand) und y_res& (=unterer Rand) durch Fensterkoordinaten (-grenzen) ersetzen bzw. ergänzen, damit sich Dialogboxen nur innerhalb von Fenstern bewegen (können). Der einzuhaltende Rand ist 3 Pixel größer als das eigentliche Objekt, womit sich das Ende der Zeilen 54 und 57 bis 59 erklärt (-3 bzw. +3). Und der Clipping-Bereich wiederum ist zwei Pixel größer als das Objekt, weshalb in der 54. Zeile 5 (3+2) und in der 57. Zeile 20 (18+2) eingesetzt sind.

Warum nur GFA 3.x?

Die in den Listings 1 bis 3 gezeigten Programme sind für den Befehlssatz der Version 3.x optimiert. Der INLINE-Befehl ist in fast keiner anderen Sprache vorhanden. Abhilfe schafft hier nur die Verwendung von READ/DATA. Das kostet eine Menge Speicherplatz, man kann sich dabei aber das Puffern der Daten sparen (Listing 1: Zeilen 34 bis 36 und 43 bis 48). Die AES-Bibliothek ist natürlich auch nicht überall vorhanden. Diese Bibliothek läßt sich durch intensiven Einsatz der Befehle (L/D)POKE und GEMSYS mit GFA-BASIC V2.x und anderen Sprachen nachbilden. Ältere Literatur zum Thema GFA-BASIC V2.x erklärt dies in der Regel recht gut, wie z.B. [4].

Die Konstruktion

var1&=WORD{var2%} 

entspricht

var1&=DPEEK(var2%) 

und

{var1%}={var2%}+var3& 

ist nichts anderes als

LPOKE var1%,LPEEK(var2%)+var3&

Da in älteren GFA-BASIC-Versionen mehrzeilige Funktionen wie Listing 1 nicht möglich sind, machen Sie daraus einfach eine Prozedur. Sollten Sie die zurückgegebene File-Länge tatsächlich brauchen, schreiben Sie sie eben in eine globale Variable. rsrc_conv() ist überhaupt nur aus Gründen der Kompatibilität zu den anderen Resource-Funktionen des GFA-BASICs 3.x als solche ausgeführt.

Die Variablentypen 1- und 2-Byte-Integer mit dem Postfix „|“ bzw. „&“ sind im GFA-BASIC 2.x ebenfalls nicht bekannt. Sie können jedoch leicht durch 4-Byte-Integer-Variablen ersetzt werden. Lediglich bei den Felddimensionen wird nur noch ein Viertel der Feldgröße benötigt. Also:

DIM rsc_buf%(INT((l.rs%+3)/4))
Bild 3: Die Dialog-Box des Beispielprogramms

Die Tilde '~' vor Funktionen ersetzt eine (Dummy-) Variable bzw. VOID.

Die Fallunterscheidung SELECT var1& / CASE var2& / ... / ENDSELECT muß in anderen Sprachen gegen IF var1&=var2& THEN / ... / (ELSE IF...)/ENDIF o.ä. Konstruktionen getauscht werden.

Der Aufruf von Unterprogrammen erfordert in den meisten anderen Sprachen ein GOSUB oder @ vor dem Prozedurnamen. Schließlich müßte ~FORM_ALERT() u.U. noch dem gewöhnlichen Alert-Befehl weichen.

Für Eilige

Das Reservieren des benötigten Speicherplatzes und das Laden des *.RSC-Files zu Programmbeginn mittels

RESERVE -mem% !Speicherplatz f. RSC 
~RSRC_LOAD("rsc_name")

entfällt genauso wie das Freigeben des Speichers beim Beenden des Programms mit

~RSRC_FREE()
RESERVE

RSRC_LOAD und RSRC_FREE werden überhaupt nicht mehr gebraucht und sollten im Programm nicht Vorkommen. RESERVE wird im Zusammenhang mit den Objekten auch überflüssig (da der für die Objekte benötigte Speicher vom INLINE-Befehl belegt wird), kann aber jetzt umso leichter für andere Zwecke eingesetzt werden, bei denen eine eigene Speicherverwaltung benutzt wird, ohne Konflikte mit den Objektdaten befürchten zu müssen.

Bauen Sie statt der o.g. vier Befehle bzw. Funktionen die neue Funktion rsrc_conv() (Listing 1) in Ihr Programm ein. Übernehmen Sie die Resource-Daten mit INLINE, und rufen Sie am Anfang Ihres Programmes einmal rsrc_conv() auf, w ie es im Beispiel-Listing 3 in den Zeilen 36 bis 48 gezeigt und oben beschrieben ist. Weitere Änderungen gegenüber der normalen Verwendung von *.RSC-Files sind nicht erforderlich.

Für Einsteiger

Sollten Sie bisher keine Erfahrung im Umgang mit dem Re-source-Construction-Set besitzen, wissen Sie gar nicht, was Ihnen da entgangen ist und erfahren es hier nun, soweit dies für das Erstellen der in Bild 3 gezeigten Dialogbox erforderlich ist. Dieser Abschnitt ist nötig, da (wohl aus Copyright-Gründen) eine Beschreibung des RCS2-Programms im GFA-Handbuch fehlt. Ehe Sie weiterlesen, noch eine Warnung: Das Erstellen von Objekten und die Arbeit damit in fertigen Programmen kann süchtig machen. Deshalb: Nur in kleinen Dosen und nicht über längeren Zeitraum anwenden!

Nach dem Start des RCS gehen Sie mit dem Maus-Cursor auf das Dialog-Symbol, drük-ken die linke Maustaste und bewegen mit weiterhin gedrückter Taste das Symbol nach oben irgendwo in das Edit-Fenster, wo Sie dann die Taste loslassen (dieser Vorgang hört auf den schönen neudeutschen Namen Dragged). Jetzt werden Sie per Dialogbox (!) aufgefordert, dem Baum einen Namen zu geben. Danach sollten Sie das Dialog-Symbol mit dem von Ihnen eingegebenen Namen im Edit-Fenster sehen. Durch Doppelklick auf das Symbol wird es geöffnet und füllt das ganze Edit-Fenster aus. Außerdem ändert sich der Inhalt der Part-Box, also der Bereich, aus dem das Dialog-Symbol stammt.

Die Dialogbox ist erheblich größer als die im Bild 3. Sie läßt sich in der Größe verändern, wenn Sie sie aktivieren, d.h. einmal anklicken (der Maus-Cursor befindet sich dabei innerhalb „Ihrer“ Dialogbox) und mit dem Maus-Cursor das schwarze Feld, das nun rechts unten erscheint, wie beim „Draggen“ beschrieben, bewegen.

Entsprechend Bild 3 „draggen“ Sie nun ein Objekt vom Typ BOXTEXT in die Dialogbox - es läßt sich in 8-Pixel-Schritten positionieren (das merken Sie aber erst nach dem Loslassen der Taste. Hätten Sie statt eines Dialog- ein Panel-Objekt gewählt, ließen sich die Objekte in Einerschritten setzen). Öffnen Sie dieses Objekt ebenfalls durch Doppelklick und geben Sie den Text ein; einen Namen braucht es nicht. Nach dem Schließen der Edit-Box, in der der Text eingegeben wurde, können Sie das Boxtext-Objekt (wie jedes andere Objekt auch) innerhalb des „Mutterobjektes“, der Dialogbox, bewegen (draggen) und auch in der Größe verändern.

Wenn ein Objekt aktiviert ist (also nach einmaligem Anklicken), lassen sich - abhängig vom Objekttyp - die meisten Attribute aus der linken Tool-Box erreichen. So können Sie, wie in Bild 3, den Rahmen des Objektes verstärken und mit einem Schatten unterlegen.

Es fehlen im Beispiel nur noch die Buttons. Die drei Buttons zur Auswahl des Bildtyps sind sog. Radio-Buttons, von denen sich immer nur ein Button gleichzeitig innerhalb des übergeordneten Objekts (hier die Dialogbox selbst) aktivieren läßt.

Alle Objekte, auf die vom Programm reagiert werden soll, müssen (in diesem Beispiel hier - es geht auch anders...) je ein gesetztes Selectable- und Exit-Flag und einen Namen haben. Das Anklicken der drei Auswahl-Buttons führt im laufenden Programm nicht zum Beenden des Dialoges, weshalb diese Buttons aus Boxtext-Objekten gebildet wurden und so einen schmalen Rand haben. Pro Objektbaum darf außerdem ein Default-Objekt definiert sein - also das Objekt, das auch mit der RETURN-Taste angesprochen werden kann. Üblich ist hier der „OK“-Button.

Jetzt sollten Sie nur noch die Objekte sortieren. Dazu wird die ganze Dialogbox aktiviert (einmal angeklickt). Der Menütitel „Hierarchy“ bietet mit „Sort Children“ die Möglichkeit, die Indizes der Objekte in bestimmter Reihenfolge zu sortieren (sonst werden die Indizes in der Reihenfolge vergeben, in der die Objekte erstellt wurden). Nehmen Sie die zweite Möglichkeit von links („1 2 3“ untereinander), dann haben Sie die gleiche Reihenfolge wie im Beispiel. Das Abspeichern Ihres *.RSC-Files dürfen Sie natürlich auch nicht vergessen. Dazu müssen Sie das Objekt mittels des Schließfeldes (oben links im Fenster) schließen und wählen dann „Save As...“ unter dem Titel „File“.

Bevor Sie die Diskette aus dem Laufwerk nehmen, sollten Sie noch folgendes beachten: Beenden Sie die Arbeit mit dem RCS2-Programm unbedingt auf dem offiziellen Weg mit „Quit“, weil wegen eines Fehlers^) im Programm das .DFN-File erst dann geschlossen wird (sonst hat’s nur 0 Bytes Länge, und Sie machen ein ebensolches Gesicht). Sie brauchen dieses File nämlich, wenn Sie Ihr .RSC-File später nochmal bearbeiten wollen (Laden mit „File“/“Open...“!). Und noch etwas: Das Namen-File (.LST) mit den Objektnamen und den Indizes dazu (Zeilen 20 bis 25 in Listing 3) wird nur dann geschrieben, wenn unter dem Menütitel „Global“ und dem Menüpunkt „Output...“ der Button „GFA-BASIC(.LST)“ aktiv ist.

Nun bleibt mir nur noch, Ihnen (endlich) viel Spaß und Erfolg bei der Arbeit mit Ihren vollwertigen GEM-Programmen ohne *.RSC-Files zu wünschen!

[1] Höhn, Stefan: „Resource-Formate“, ST Computer 7/8 1990

[2] Litzkendorf, Uwe: „Das große GFA-BASIC-3.5-Buch“, DATA BECKER GmbH, 1. Aufl. 1989

[3] Jankowski/Reschke/Rabich: „ATARI ST Profibuch“, SYBEX-Verlag, 7. Aufl. 1989

[4] Ostrowski, Frank: „GFA-BASIC“, GFA Systemtechnik GmbH, Juni 1987

[5] Muus, J. und Besenthal, W.: „ATARI-ST-Programmierpraxis GFA-BASIC 3.0“, Markt&Technik Verlag AG, 1989

' ****************** LISTING 1 ******************
' *    RSC-Pointer & -Koordinaten angleichen    *
' *    --------------------------------------   *
' *    Sprache:  GFA-BASIC V3 x                 *
' *    Autor:    Volker Goreth. 4150 Krefeld    *
' *    Datum:    09.12.90, geaend 03 02.91      *
' *    --------------------------------------   *
' *       (c) 1991 by MAXON Computer GmbH       *
' ***********************************************

FUNCTION rsrc_conv(l.ra%,l.buf!)
    ' <— 1 rsS !Laenge des RSC-Datenbereichs
    ' !oder 0 bei Fehler 
    ' —> l.ra% !RSC-INLINE-Adresse
    ' —> l.buf! !RSC-Daten puffern
    '
    LOCAL l.pa%         !Puffer-Adresse
    LOCAL l.o&,l.t&     !Laufvar.
    LOCAL l.ob&         !Beginn des Objekt-Feldes
    LOCAL l.no&         !Anzahl OBJECTS
    LOCAL l.nt&         !Anzahl TREES
    LOCAL l.rs&         !Laenge des RSC-Datenbereichs
    LOCAL l.napt%       !Neue TREE-TABLE-Adr.
    LOCAL l.gb%         !Adr. des AES-Global-Feldes
    LOCAL l.of%         !Objekt-Feld
    LOCAL l.ns&         !Anzahl der freien Strings
    LOCAL l.ni&         !Anzahl der freien Images
    LOCAL l.adr%,1,i&   !Hilfsvar., Laufvar
    '
    l.rs&=WORD{l.ra%+&H22}
    '
    ' Pruefen, ob rsc_buf|() nicht schon 
    ' dimensioniert ist (-> FEHLER + ABBRUCH'):
    IF l.buf! AND DIM?(rsc_buf|())>0 
        l.rs&=0 
    ENDIF
    '
    IF l.rs&>0          !Nur, wenn INLINE nicht leer
        '               !und rsc_buf|() noch nicht
        '               !dimensioniert worden ist
        '
        ' RSC-Daten ggf. puffern (nur Interpreter):
        IF l.buf!
            DIM rsc_buf|(l.rs&)         !RSC-Puffer
            l.pa%={*rsc_buf|()}+4       !Puffer-Adresse
            BMOVE l.ra%,l.pa%,l.rs&     !RSC kopieren
            l.ra%=l.pa%                 !Neue Adresse
        ENDIF
        '
        ' Neue Adresse der TREE-Tabelle 
        l.napt%=l.ra%+WORD{l.ra%+&H12}
        ' Adr des AES-Global-Feldes 
        l.gb%={GB+4}
        ' AES neue Tabellen-Adr zuweisen 
        {l.gb%+10}=l.napt%
        ' Bisheriger Start des Objekt-Feldes 
        l.ob&=WORD{l.ra%+2}
        ' Neue Startadr. des Objekt-Feldes:
        l.of%=l.ra%+l.ob&
        ' Anzahl der Objekte im File -1: 
        l.no&=WORD{l.ra%+&H14}-1 
        ' Anzahl der Trees im File -1: 
        l.nt&=WORD{l.ra%+&H16}-1
        '
        FOR l.o&=0 TO l.no& !Alle Objekte 
            ' Koordinaten umrechnen:
            ~RSRC_OBFIX(l.of%,l.o&)
            '
            SELECT OB_TYPE(l.of%.l.o&)
                '
                ' Zeiger auf Struktur mit 3 Zeigern 
                ' - TEDINFO bzw. ICONBLK
                ' (TEXT, BOXTEXT, FTEXT, FBOXTEXT, ICON)
            CASE &H15,&H16,&H1D,&H1E,&H1F 
                OB_SPEC(l.of%,l.o&)= OB_SPEC(l.of%,l.o&)+l.ra%
                {OB_SPEC(l.of%,l.o&) } = {OB_SPEC(l.of%,l.o&)}+l.ra%
                {OB_SPEC(l.of%,l.o&)+4}= {OB_SPEC(l.of%,l.o&)+4}+l.ra%
                {OB_SPEC(l.of%,1 o&)+8}= {OB_SPEC(l.of%,l.o&)+8}+l.ra%
                '
                ‘ Zeiger auf Struktur mit 2 Zeigern 
                ' - APPLBLK
                ' (PROGDEF):
            CASE &H18
                OB_SPEC(l.of%,l.o&)= OB_SPEC(l.of%,l.o&)+l.ra%
                {OB_SPEC(l.of%,l.o&)}={OB_SPEC(l.of%,l.o&) }+l.ra% 
                {OB_SPEC(l.of%,l.o&)+4}={OB_SPEC(l.of%,l.o&)+4}+l.ra%
                '
                ’ Zeiger auf Struktur mit 1 Zeiger 
                ' - BITBLK
                ' (IMAGE):
            CASE &H17
                OB_SPEC (l.of%,l.o&) = OB_SPEC(l.of%, l.o&)+l.ra%
                {OB_SPEC(l.of%,l.o&)}= {OB_SPEC(l.of%,l.o&)}+l.ra%
                '
                ' Zeiger auf Datenstruktur 
                ' - C-Text
                ' (BUTTON,STRING,TITLE):
            CASE &H1A,&H1C,&H20 
                OB_SPEC(l.of%,l.o&)= OB_SPEC(l.of%,l.o&)+l.ra%
            ENDSELECT 
        NEXT l.o&
        '
        ' ap_ptree-Tabelle aktualisieren 
        FOR l.t&=0 TO l.nt&     !Alle Baeume
            {l.napt%+(4*l.t&)}={l.napt%+(4*l.t&)}+l.ra% 
        NEXT l.t&
        '
        ' FREE_STRINGs-Tabelle aktualisieren: 
        l.ns&=WORD{l.ra%+&H1E}-1    !Anzahl -1
        IF l.ns&>-1
            l.adr%=l.ra%+WORD{l.ra%+SHA}
            FOR l.i&=0 TO l.ns&
                {l.adr%+(4*l.i&)}={l.adr%+(4*l.i&)}+l.ra% 
            NEXT l.i& 
        ENDIF
        '
        ' FREE_IMAGEs-Tabelle aktualisieren:
        l.ni&=WORD{l.ra%+&H20}-1    !Anzahl -1
        IF l.ni&>-1
            l.adr%=l.ra%+WORD{l.ra%+&H10}
            FOR l.i&=0 TO l.ni&
                {l.adr%+(4*l.i&)}={l.adr%+(4*l.i&)}+l.ra% 
                ' Pointer in BITBLK relozieren 
                {(l.adr%+(4*l.i&))}={(l.adr%+(4*l.i&)}}+l.ra%
            NEXT l.i& 
        ENDIF
        '
    ENDIF
    '
    RETURN l.rs&
ENDFUNC

Listing 1: Pointer angleichen (statt „RSRC_LOAD“)

' ****************** LISTING 2 ******************
' *    Baeume ausgeben und wieder entfernen     *
' *    ------------------------------------     *
' *    Sprache:   GFA-BASIC V3 x                *
' *    Autor:     Volker Goreth, 4150 Krefeld   *
' *    Datum:     22.12 90                      *
' *    ------------------------------------     *
' *       (c) 1991 by MAXON Computer GmbH       *
' ***********************************************
'
' Objektbaum auf Bildschirm ausgeben:
PROCEDURE draw_tree(l.tno&,l.depth&, l.can&,l.ok&, l.c!)
    '     —> l.tno&           !Baum-Nr.
    '     —> l.depth&         !Anzahl der Ebenen
    '     —> l.can&,l.ok&     !Buttons in Normal-
    '                         ! darstellung 
    '     —> l.c!             !Baum zentriert
    ' GLOBAL obj_adr%         !Baum-Adresse
    '        obj_x&,obj_y&    !Objekt-X- u. Y-Koord. 
    '        obj_w&,obj_h&    !Objekt-Breite u. -Hoehe 
    '        x_res&,y_res&    !X- und Y-Aufloesung 
    '                         ! (in Pixel)
    '
    '  !!! Wenn Objektbaeume nicht zentriert !!!
    '  !!! ausgegeben werden,   m u s s   in !!!
    '  !!! x_res& und y_res& die Bildschirm- !!!
    '  !!!    Aufloesung angegeben werden    !!!
    '  !!!  (siehe Listing 3: Beispielprg.)  !!!
    '
    ~WIND_UPDATE(1)         !Menues sperren
    ' Objektadresse von Baum-Nr. l.tno& holen 
    ~RSRC_GADDR(0,l.tno&,obj_adr%)
    '
    ' Abbruch-Feld in Normaldarstellung 
    '  (-1, wenn nicht vorhanden):
    IF l.can&>-1
        OB_STATE(obj_adr%,l.can&)=0 
    ENDIF
    ' OK-Feld in Normaldarstellung:
    IF l.ok&>-1
        OB_STATE(obj_adr%,l.ok&)=0 
    ENDIF
    '
    ' Den Koordinaten- und Groessen-Variablen 
    ' Werte fuer Objektbaumdarstellung 
    ' in Bildschirmmitte zuweisen 
    ~FORM_CENTER(obj_adr%,obj_x&,obj_y&,obj_w&,obj_h&)
    '
    ' Koordinaten ggf korrigieren, wenn der Baum 
    ' nicht zentriert, sondern an Mausposition 
    ' ausgegeben werden soll (l.c!=FALSE):
    IF NOT l.c!
        ' Rand links, rechts und unten: mind 3 Pixel 
        obj_x&=MAX(5,MIN(MOUSEX-(obj_w&\2),x_res&-obj_w&-3))
        ' bzw. mind. 20 Pixel unter Bildschirm-
        ' Oberkante (=Platz fuer Menue-Leiste) 
        obj_y&=MAX(20,MIN(MOUSEY-(obj_h&\2),y_res&-obj_h&-3))
        OB_X(obj_adr%,0)=obj_x&+3 
        OB_Y(obj_adr%,0)=obj_y&+3 
    ENDIF
    '
    ' Bildschirmbereich reservieren:
    ~FORM_DIAL(0,0,0,0,0,obj_x&,obj_y&,obj_w&,obj_h&)
    ' Sich ausdehnendes Rechteck zeichnen:
    ~FORM_DIAL(1,MOUSEX,MOUSEY,16,16,obj_x&,obj_y&,obj_w&,obj_h&)
    '
    ' Objektbaum zeichnen
    ~OBJC_DRAW(obj_adr%,0,1,depth&,obj_x&,obj_y&,obj_w&,obj_h&)
    '
    ~WIND_UPDATE(0)             !Menues wieder freigeben
RETURN
'
' Objektbaum vom Bildschirm entfernen:
PROCEDURE redraw_tree
    ~WIND_UPDATE(1)             !Menues sperren
    ' "Schrumpfendes" Rechteck zeichnen:
    ~FORM_DIAL(2,MOUSEX,MOUSEY,16,16,obj_x&,obj_y&,obj_w&,obj_h&)
    ' Reservierten Bildschirmbereich freigeben: 
    ~FORM_DIAL(3,0,0,0,0.obj_x&,obj_y&,obj_w&,obj_h&)
    ON MENU                     !(wg redraw_msg)
    ~WIND_UPDATE(0)             !Menues wieder freigeben
RETURN

Listing 2: Universelle Draw-/Redraw-Routinen

' ****************** LISTING 3 ********************** 
' *                 Beispielprogramm                *
' *     ----------------------------------------    *
' *     Sprache:    GFA-BASIC V3.x                  *
' *     Autor:      Volker Goreth, 4150 Krefeld     *
' *     Datum:      22.12.90                        *
' *     ----------------------------------------    *
' *         (c) 1991 by MAXON Computer GmbH         *
' ***************************************************
'
'   Zeigt Bilder im DEGAS- und Screen-Format 
'        - allerdings nur in HIRES.
'
'  Damit's auch laeuft LISTING 1 & 2 ans Ende!
'
' Objekt-Indizes. Abhaengig von der Erstellungs-/ 
' bzw. Sortier-Reihenfolge andere Werte moeglich
' Deshalb hier am besten Ihr original Namen-
' (*.LST-)File von RCS2 einfuegen
LET dialog&=0           !RSC_TREE
LET pi3&=2              !Obj in #0
LET pic&=3              !Obj in #0
LET img&=4              !Obj in #0
LET cancel&=5           !Obj in #0
LET ok&=6               !Obj in #0
'
x_res&=WORK_OUT(0)+1    !X-Aufloesung
y_res&=WORK_OUT(1)+1    !Y-Aufloesung
' (siehe Listing 2 Procedure draw_tree!)
'
s_res&=XBIOS(4)         !Bildschirm-Aufloesung
DIM pic|(32066)         !Bild-Puffer
pic_adr%={*pic|()}+4    !Adresse dazu
scrn_adr%=XBIOS(3)      !Bildschirm-Adresse
'
' DAS RESOURCE-FILE EINBINDEN 
INLINE rsc_adr%,368
' >>> Die Konstante 'Anzahl' - hier 368 - ist <<< 
' >>>   abhaengig von der RSC-File-Laenge!!!  <<<
'
' Interpreter oder Compiler? 
interpreter!= (BYTE{BASEPAGE+256}=96)
' RSC-Pointer anpassen:
rsc!=FN rsrc_conv(rsc_adr%,interpreter!)
IF NOT rsc!             !Hat's nicht geklappt?
    ~FORM_ALERT(1,"[3][Fehler bei der RSC- | Daten-Umwandlung!][na sowas!]")
    END
ENDIF
'
' "*.PI3"=Default
'  - Baumadresse der Dialogbox "dialog&" holen
~RSRC_GADDR(0,dialogs,obj_adr%)
'  - "*.PI3"-Button selectieren
OB_STATE(obj_adr%,pi3&)=1 
'  - File-Extender:
ext$="PI3"
'
' Das compilierte Programm mag's so lieber: 
CLS
DEFMOUSE 0
'
'             ***** Hauptprogramm: *****
'       (Laeuft, bis "Abbruch" angeklickt wird)
DO
    ' Baum "dialog&" ausgeben:
    draw_tree(dialog&,3,cancel&,ok&,FALSE)
    '
    ' Maus und <RETURN>-Taste ueberwachen:
    REPEAT
        bno&=FORM_DO(obj_adr%,0)    !Button-Nr.
        '
        ' "* PI3", "*.PIC" oder "*.IMG" angeklickt 
        SELECT bno& 
        CASE pi3& 
            ext$="PI3"
        CASE pic& 
            ext$="PIC"
        CASE img& 
            ext$=”IMG"
        ENDSELECT
        '
        ' ... bis Abbruch- oder OK-Button angeklickt
        ' oder <RETURN>-Taste gedrueckt wird 
    UNTIL bno&=cancel& OR bno&=ok&
    '
    redraw_tree     !Weg mit dem Baum
    CLS             !.. und der grauen Flaeche,
    '               ! die redraw hinterlaesst
    '
    EXIT IF bno&=cancel& !"Abbruch" angeklickt
    '
    ' sonst Aktion gemaess Inhalt von ext$: 
    SELECT ext$
    CASE "PI3"
        pic_load(34,ext$)
    CASE "PIC"
        pic_load(0,ext$)
    CASE "IMG"
        ~FORM_ALERT(1,"[1][Ohne Routine zum Entpacken | von *.IMG-Bildern geht's|nun mal nicht'][ naja... ]")
    ENDSELECT
    '
LOOP
'
'           ***** Unterprogramm(e): *****
'             (Nur des Beispiels wegen)
PROCEDURE pic_load(l.disp&,l.ext$)
    '     —> l.disp&    !zu ueberspringende Bytes
    '     —> l.ext$     !File-Extender (PI3/PIC/IMG)
    ' GLOBAL s_res&     !Bildschirm-Aufloesung
    '        pic_adr%   !Bild-Puffer-Adresse 
    '        scrn_adr%  !Video-Ram-Adresse 
    LOCAL l.file$       !File-Name des Bildes
    '
    IF s_res&=2         !HIRES - oder etwa nicht?
        ' Sie haben die Wahl:
        FILESELECT "*."+l.ext$,”",l.file$
        IF EXIST(l.file$)
            ' Erst das Bild in den Puffer laden:
            BLOAD l.file$,pic_adr%
            ' ... und dann in den Bildschirm kopieren:
            BMOVE pic_adr%+1,disp&,scrn_adr%,32000 
            ' Auf (Maus-)Tastendruck warten 
            REPEAT
            UNTIL MOUSEK OR INKEY$<>""
            REPEAT
            UNTIL MOUSEK=0 
        ENDIF 
    ELSE
        ~FORM_ALERT(1,"[1][Dieses Beispielprogramm| ladt Bilder nur in HIRES! ] [schon gut]") 
    ENDIF 
RETURN
'
'       !!! Hierhin gehoeren LISTING 1 und 2 !!!

Listing 3: Beispielprogramm zu den Listings 1 und 2


Volker Goreth
Aus: ST-Computer 09 / 1991, Seite 89

Links

Copyright-Bestimmungen: siehe Über diese Seite