Mit diesem Kursteil dringen wir diesmal in die Praxis ein und werden ein Beispiel programmieren. An Hand des kleinen Programms lernen Sie, wie Sie eine Oberfläche erstellen und die Funktionen der Oberfläche ansprechen.
Das Grundgerüst
Als Vorbereitung wird die in »Vorbild« abgebildete Oberfläche erstellt. Es handelt sich um vier große horizontale Gruppen, die teilweise noch vertikale Gruppen enthalten. Die fertige Beschreibungsdatei finden Sie aber auch auf der CD zum Heft, bzw. auf der Homepage des AMIGA-Magazins.
|
Wurde die Library erfolgreich geöffnet, können evtl. vorhandene Parameter per ReadArgs() geparst werden. Danach wird die Umgebung für die neuen Reaction-Klassen bereitgestellt. Diese Aufgabe übernimmt die Funktion RL_OpenResource(), welche als Parameter den Bildschirm und einen Catalog erwartet. Während der Catalogzeiger NULL sein darf (dann werden nur die internen Texte verwendet), ist der Bildschirmzeiger zwingend erforderlich und kann z.B. mit LockPubScreen(NULL) (entspricht normalerweise der Workbench) ermittelt werden.
Als weiterer Parameter wird RCTResource benötigt. Diese Variable wird von ReActor automatisch mit in das erstellte Objektfile geschrieben und muß durch den Programmierer nur »durchgereicht« werden. Diese Variable stellt quasi den Aufhänger für die komplette Oberflächenbeschreibung dar. Zur Nachrichtenkommunikation werden noch zwei Messageports benötigt, die sich mit CreateMsgPort() erzeugen lassen. Dieser Punkt soll später optional werden. Die Ports werden beim Erzeugen des Fenster per RL_NewObject() mitgegeben. Als zusätzlichen Parameter erwartet die Funktion die ID des zu erzeugenden Objekts (z.B. des Fensters). Dabei werden sämtliche Umgebungsdaten des angegebenen Objekts bereitgestellt, also auch alle Gadgets erzeugt. Möchten Sie im späteren Programmlauf auf diese Gadgets zugreifen, können Sie per RL_GetObjectArray() ein Array mit den Gadgetzeigern abfragen.
Erste Einblicke
Bis jetzt wurde das Fenster nur erzeugt, allerdings noch nicht geöffnet. Das Fensterobjekt kennt unterschiedliche Kommandos, die ihm mit DoMethod() mitgeteilt werden z.B. DoMethod(gb _WindowObj,WM_OPEN) um das Fenster zu öffnen, oder WM_CLOSE um das Fenster zu schließen. Auf zwei weitere Kommandos (WM_ICONIFY und WM_ HANDLEINPUT) wird bei der Beschreibung des Messageloops eingegangen.
|
Das Öffnen des Fensters erfolgt (wie bereits
beschrieben) mit DoMethod(gb_WindowObj,WM_OPEN).
Alternativ stehen auch einige Defines zur
Verfügung, für die Fensterkommunikation sind
dies:
struct Window * RA_OpenWindow(Object *win)
void RA_CloseWindow(Object *win)
ULONG RA_HandleInput(Object *win, UWORD *code)
BOOL RA_Iconify(Object *win)
struct Window * RA_Uniconify(Object *win)
Nach dem Öffnen des
Fensters erfolgt in einer Schleife die
Verarbeitung der Nachrichten (darauf wird später
genauer eingegangen). Am Programmende sind die
reservierten Resourcen in umgekehrter Reihenfolge
freizugeben. RL_CloseResource() gibt dabei alle
im Programmlauf angelegten und noch nicht
freigegebenen Objekte (Fensterdaten und
Gadgetdaten) frei. Zur Verdeutlichung der
Beschreibung sehen Sie in Listing 2 eine kurze
Übersicht der Funktionen in ihrer praktischen
Anwendung (ohne Fehlerrückmeldungen):
Der Message-Loop
|
ULONG windowsignal, signals;
GetAttr(WINDOW_SigMask, gb_WindowObj, &windowsignal);
Dieses wird mit den anderen Signalbits mit einem ODER verbunden, auf dessen Ereignisse gewartet werden soll. In unserem Programm haben wir lediglich ein Fenster, erlauben aber noch CTRL-C zum Abbrechen des Programms von der Shell aus:
const ULONG signals = Wait(windowsignal | SIGBREAKF_CTRL_C);
In einer Endlosschleife werden so lange die Nachrichten verarbeitet, bis der Benutzer das Fenster schließt oder das Programm mit CTRL-C abbricht. Die einzelnen Nachrichten werden mit dem Kommando WM_HAN-DLEINPUT angefordert. Dieses liefert WMHI_LASTMSG, wenn keine Nachrichten mehr vorliegen, sonst einen ULONG-Wert. Dieser setzt sich aus dem oberen Word für die Klasse und dem unteren Word für die Event-Daten zusammen. Daher muß der Programmierer vor der Weiterverarbeitung den Wert bitweise mit WMHI_CLASSMASK maskieren. Für die meisten IDCMP-Ereignisse existieren entsprechende WMHI-Equivalente. Das Gerüst des Message-Loops sehen Sie in Listing 3. Die einzelnen Message-Ereignisse sollten bekannt sein. Alle vorhandenen Defines mit kurzer Beschreibung sind in der Include-Datei classes/window.h zu finden.
Bei den Tastenereignissen ist der betreffende ASCII- oder Rawkey-Code durch (result & WMHI_KEYMASK) zu ermitteln. Der Qualifier läst sich nicht direkt auslesen. Wird dieser benötigt, so ist der umständliche Weg über eine Hook-Funktion notwendig (über WINDOW_IDCMPHook im Fenster zu installieren). Bei RawKeys läßt sich allerdings der zugehörige In-putEvent abfragen (GetAttr(WINDOW_InputEvent, gb_WindowObj, &ie)), um so doch direkt auf den Qualifiercode zugreifen zu können.
Kommunikation mit den Gadgets
|
|
Umgekehrt lassen sich mit SetGadgetAttrs() neue Werte an das Gadget übermitteln. Dabei können auch mehrere Tag-Paare gleichzeitig übergeben werden, welche mit TAG_DONE abzuschießen sind.
SetGadgetAttrs(gb_Gadgets[
Der Parameter gb_Window ist dabei das
Intuition-Fenster in dem sich das Gadget befindet
und darf auch NULL sein. Daten für Gadgets darf
man auch setzen, wenn das Fenster noch nicht oder
gerade nicht geöffnet ist. gb_Gadgets ist das
Array mit allen Gadget-Adressen, wobei über die
GadgetID auf den gewünschten Eintrag zugegriffen
wird. Die einzelnen GadgetIDs werden von ReActor
automatisch in die *.h-Datei geschrieben und sind
im Eingabefeld »Object Name« in der
ReActor-Umgebung frei einzugeben. Zu beachten
ist, daß allein durch das Setzen der neuen Werte
die Anzeige nicht automatisch erneuert (refresht)
wird. Diese Aktion muß ebenfalls vom Programm
angefordert werden:
|
Ein Refresh ist normalerweise nur notwendig, wenn SetGadgetAttrs() != 0 liefert. Das Root-Gadget (Basisklasse) liefert allerdings immer 0, weshalb der Return-Wert allein nicht sehr zuverlässig ist. Um hier überflüssiges Neuzeichnen zu vermeiden, sollten die einzelnen Gadgets (und Tagwerte) ausprobiert werden. Nur bei Bedarf sollte das Programm einen Refresh für das einzelne Gadget (per RefreshGList) auslösen. Alternativ könnte direkt DoGadgetMethod(..., OM_UPDATE,...) zum Einsatz kommen dadurch wird das Gadget automatisch refresht.
|
Interconnection
Einfacher wäre es jedoch, wenn die Gadgets ihre Daten untereinander austauschen würden, ohne daß dazu das Hauptprogramm eingreifen müßte. Auch diese Möglichkeit bieten die BOOPSI-Gadgets an! Per ICA_Target wird das zu informierende Gadget festgelegt und per ICA_Map die Übersetzungskonventionen. So kann beispielsweise beim Bewegen des Scrollers automatisch der Zahlenwert im Integer-Gadget aktualisiert werden. Und auch umgekehrt kann sich bei Eingaben eines neuen Zahlenwertes der Scroller verändern automatisch ohne eine Zeile Programmcode.
Diese Kommunikation funktioniert immer nur für zwei Objekte untereinander. Zur Kommunikation von mehreren Objekten müssen Sie als Verteiler ein Modelclass-Objekt erzeugen. Dieses erhält von jedem Sender den Wert und kann diesen an beliebig viele andere Objekte weitermelden. Dazu ist je Empfänger ein ICClass-Objekt zu erzeugen, das den Wert (aus Modelclass) in den Zielwert (Zieltag) konvertiert. Auch das Programm kann hierbei als Empfänger dienen. Als Empfänger dient dann die ICA_Target-Kennung ICTARGET_IDCMP. Das Programm würde dann eine Message vom Classtyp IDCMP_IDCMPUPDATE erhalten in der ReAction-Umgebung ist ein IDCMPHook notwendig, um diese Nachrichten »abhören« zu können. Zur Zeit ist lediglich eine Tag-Konvertierung möglich. Der Datenwert bleibt aber identisch. Der interne Scrollerwert (0 bis 3) kann somit nicht direkt an das Integer-Gadget weitergegeben werden, da dieses mit den Werten 1 bis 4 arbeitet. Diese Konvertierung muß daher das Programm übernehmen.
Einbindung von Menüs
ReActor nimmt uns zwar die Erstellung der Oberfläche (auch mehrerer Fenster) ab, die Menüs hingegen sind weiterhin auf »altmodische« Weise zu erstellen. Allerdings ist die mit OS 2.x eingeführte Möglichkeit der Menüerzeugung über die gadtools.library so sehr einfach. Daher wird hier nicht weiter darauf eingegangen. Lesen Sie hierzu die entsprechenden Kommentare im Sourcecode. Zu erwähnen ist lediglich, daß die Menüs von Hand lokalisiert werden müssen (s. Kapitel »Localisieren«). Das Einbinden der Menüliste nimmt uns allerdings ReActor wieder ab: WINDOW_MenuStrip, gb_Menues in der Fenstertagliste reicht aus.
Localisieren
Bereits seit AmigaOS 2.1 (Library V38) ist die locale. library Bestandteil des Betriebssystems. Über diese können die landesspezifischen Texte für das Programm abgelegt werden. Auch ReActor benutzt diese Library zur automatischen Lokalisierung der Oberfläche. Alle in der Oberfläche verwendeten Texte werden automatisch (von ReActor) in eine CD-Datei gespeichert. Das Programm wird aber noch wesentlich mehr Texte enthalten, die lokalisiert werden müssen. Diese werden am besten in eine eigene Datei gespeichert! Ändern Sie später etwas an der Oberfläche mit Hilfe von ReActor, würde die Catalog-Datei automatisch mit gespeichert und alle Erweiterungen von Ihnen wären verloren. Die eigenen internen Programmtexte lassen sich einfach mit Hilfe der GetStr(TX_SureToQuit) ermittelt wobei GetStr() wie folgt als Define zu erstellen ist:
#define GetStr(id) GetCatalogStr(gb_Catalog,id,id ## _STR)
Dadurch ersparen Sie sich immer die ausführliche Schreibweise dieses Funktionsaufrufes. Wie folgend noch beschrieben wird, sind die von ReActor erstellten Catalogtexte speziell zu übersetzen. Die eigenen Texte werden normal compiliert und zum Programm hinzugelinkt. Die Aufrufe zum Erzeugen der Cataloge sind in den jeweiligen Catalogdateien angegeben.
Das Programm übersetzen
Der Übersetzungsvorgang ist mit den gängigen Compilern identisch, dabei bieten StormC und MaxonC (ab V4) eine komfortable Oberfläche an. Andere Compiler starten Sie meist per Shellaufruf. Generell ist der Sourcecode ganz normal zu übersetzen (er enthält alle bisher beschriebenen Teile). Hinzugelinkt werden müssen das von ReActor erzeugte Objektfile mit der Oberflächenbeschreibung und die Catalogtexte. Diese sind allerdings zuerst noch zu übersetzen. Dazu wird mit der neuen CatComp-Version die .cd-Datei in eine _cd.asm-Assembler-Datei übersetzt, welche noch assembliert werden muß. Erst die dabei entstandene Objekt-Datei läßt sich zum Programm hinzulinken und enthält dann alle internen Texte für die Oberflächenelemente. Ein Programm besteht somit immer aus (mindestens) den drei folgenden Objektfiles:
Die genauen Übersetzungsanweisungen für verschiedene Compiler finden Sie im Kopf des Sourcecodes. Der Einfachheit halber wurden die internen Programmtexte nicht in eine eigene Datei ausgelagert, sondern mit dem Hauptprogramm übersetzt. Die Textstrings (und String-IDs) sind danach noch von Hand in eine gemeinsame cd-Datei einzutragen, welche mit dem Programm weitergegeben werden kann.
Dieser Teil konnte nur
einen groben Überblick über die neuen
Möglichkeiten der Oberflächengestalltung geben.
Vor allem die notwendigen Änderungen im
Sourcecode sollten aber damit deutlich sein. Der
ausführlich kommentierte Sourcecode zeigt die
komplette Programmumgebung und die
Nachrichtenbehandlung, dabei wird auch auf die
Iconifyfunktion eingegangen und ARexx-Kommandos
unterstützt. Das Programm verwendet viele
unterschiedliche Gadget-Arten und kann somit auch
zum »Nachschlagen« dienen. Sollten dennoch
Fragen auftreten, können Sie sich gerne an den
Autoren wenden:
michael@meicky-soft.de.
Alle Programmteile (Sourceodes, Oberflächenbeschreibung) können unter
http://www.meicky-soft.de/amiga-magazin/reaction.html
geladen werden bzw. befinden sich auch auf der CD zur nächsten Ausgabe.
Das manuelle Eingreifen in das
automatische Layout von Reaction ist sehr
schwierig bis unmöglich. Daher sollten eigene
Anzeigeflächen etc. als neues Gadget
implementiert werden. Dadurch passen sie sich
wieder automatisch in den Layoutprozess ein und
programmseitig haben Sie keine »Arbeit« mehr
damit (Größe/Lage/Refresh). Wie Sie eigene,
reactiontaugliche Gadgets erstellen beschreibt
der Kursteil in der nächsten Ausgabe.
|
Kommentare, Fragen, Korrekturen und Kritik bitte an Webmaster AMIGA schicken.
Zuletzt aktualisiert am 29. Januar 2000, Michael Christoph.