Drop-Down-Menüs

Von allen Programmen für den Atari ST sind die GEM-Applikationen mit Fenstern und Drop-Down-Menüs die Krone der Programmierung. Kaum ein professionelles Programm kann auf sie verzichten. Um so erstaunlicher ist die Erkenntnis, daß der am schwierigsten anmutende Teil, nämlich die Menüleisten, weitaus einfacher zu beherrschen sind als beispielsweise die Fenstertechnik in allen Variationen.

Nach diesem Kurs werden Sie in der Lage sein, Ihren selbstgeschriebenen Programmen in C, MODULA oder Pascal ein professionelles Aussehen mit Drop-Down-Menüs und Formularen zu geben.

Der grundsätzliche Unterschied besteht darin, daß Fensterinhalte vollkommen vom Benutzer kontrolliert werden müssen, GEM sich also ’nur’ um den Fensterrand und dessen Komponenten kümmert. Anders jedoch bei den Menüs, denn ist hier erst einmal die Menüleiste aktiviert, so braucht das Programm nur noch auf eine Meldung zu warten, die besagt, welchen Eintrag der Benutzer angewählt hat. So werde ich in diesem Kurs zuerst auf die Drop-Down-Menüs, die Formulare und die damit zusammenhängenden Funktionen eingehen. Das ganze werde ich anhand eines Beispielprogrammes in der Programmiersprache C aufrollen.

Zum Verständnis dieses Artikels wäre es recht gut, wenn Sie C nicht nur für den dritten Buchstaben des Alphabetes halten und sich schon ein wenig mit GEM beschäftigt hätten, was aber keineswegs ausschließt, daß Sie auch als Pascal- oder Modula-Programmierer von diesem Kurs profitieren können, denn die Funktionsnamen sind glücklicherweise fast alle genormt. Das Programm, der zentrale Teil des Kurses, wurde in C geschrieben, wobei alte C-Programmierer sicherlich über die, für C-Begriffe, unverschämt ausführliche Programmierweise die Nase rümpfen werden, aber auch der Basic- oder Pascal-Programmierer soll an seine Sprache erinnert werden und nicht an das Listing eines amoklaufenden Druckers.

Entwicklung eines Drop-Down-Menüs

Um mit einem Drop-Down-Menü zu arbeiten, muß man logischerweise erst einmal ein solches konstruieren. Das ganze von Hand zu machen, ist ein nahezu menschenunwürdiger Aufwand, weshalb wir zum Resorce-Construction Set (RCS) greifen und uns als Ziel das endgültige Aussehen der Menüleiste vorgeben:

Das sieht sehr schön aus, aber wie kommt man dazu? Ganz einfach: Man lädt das RCS und schiebt das Menü-Symbol aus dem oberen in das untere Fenster. Nun weiß das Programm, daß wir zuerst ein Drop-Down-Menü (als ersten Baum) konstruieren wollen und es fragt nach dem Namen. Dieser muß in Großbuchstaben eingegeben werden, und wir wählen einen sinnvollen Namen wie MENÜ aus. Klicken Sie dann zweimal mit der Maus auf dieses Symbol, so entsteht sofort im Arbeitsfenster eine typische Menüleiste mit den Einträgen ’Desk’ und ’File’.

Bild 1: Das RCS-File

Da wir ’File’ nicht benötigen, werfen wir es in den Papierkorb. Klicken Sie nun einmal auf ’Desk’, so fällt der oben gezeigte Kasten herunter, nur daß statt 'über dieses Spiel’ noch ’Your Message here’ steht. Diesen Mißstand beheben wir durch einen Doppelklick auf dem Objekt und erhalten sofort ein Formular angeboten, in dem wir den Namen und noch einiges mehr (z. B. Schatten, Haken, Umrandung, ...) ändern können.

Titel oder Einträge erhalten Sie, wenn Sie die Icons TITLE oder ENTRY an die gewünschte Stelle übertragen und der Haken vor Anfänger wird durch zusätzliches Anwählen des Feldes ’CHEKED’ im Namensformular erreicht. Dieser Haken ist übrigens der Grund, warum vor jedem Eintrag ein Space stehen sollte. Die Größe der herabfallenden Kästen kann durch Drük-ken der Maustaste in der rechten unteren Ecke erreicht werden. Und da Sie durch Drücken von SHIFT aus den Bewegungsfunktionen eine Kopierfunktion machen können, sparen Sie noch etwas mehr Arbeit. Mit diesem Wissen ausgestattet können Sie ganz einfach die gewünschte Menüleiste zusammenbauen.

Ist dies getan, so sind noch einige Schönheitsoperationen von Nöten. Klicken Sie beispielsweise auf ’Freak’, so wird der Eintrag nur bis zum Wortende invertiert. Das kann genauso behoben werden, wie man die Größe der herabfallenden Kästen ändert, man zieht den Namen bis zum Ende des Kastens. Schließlich müssen den Einträgen sozusagen noch Privatnamen gegeben werden, die nur für das Programm benötigt werden und anhand derer die angewählten Einträge erkannt werden können. Diese Namen werden vom RCS beim Erzeugen des Resource-File in der Datei < Name > ,H mitgeliefert und weisen jedem dieser Namen den Index eines Eintrages zu.

So benennen wir 'über dieses Spiel’ mit Hilfe der Name-Funktion des RCS als ABOUT, die 4 Spielstufen als STUFEI bis STUFE4, 'Sound aus’ als SOUND, 'Auf in den Kampf!’ als START und ’Zum Desktop’ als QUIT. Wie gesagt, diese Namen dienen nur dazu, im Programm den gewählten Eintrag zu erkennen, sie tauchen im fertigen Programm dann nicht mehr auf (wohl aber im C-Listing).

So, das Wichtigste ist geschafft. Aber was, wenn nun der Menüeintrag 'über dieses Spiel’ angewählt wird. Dann wollen wir aller Welt sagen, wer für dieses Programm verantwortlich ist, und das soll doch wohl zur Befriedigung unseres Egos möglichst schön aussehen. Also erzeugen wir einen zweiten Objektbaum von der Struktur FREE, die uns ein Rechteck zur freien Verfügung stellt. (Der einzige Unterschied zwischen FREE und MESSAGE besteht darin, daß MESSAGE noch gewisse Formatierungsregeln einhält, während man in FREE die Objekte absolut frei positionieren kann.)

Diesen Baum nennen wir INFO, und Sie können ihn völlig frei gestalten.

Nur dürfen Sie nicht vergessen, Ihrem Baum einen OK- oder Abbruchsknopf mit dem STATUS EXIT zu geben, sonst kann er später im Programm nicht richtig verwaltet werden. Und da das Programm diesen Knopf noch manipulieren muß, geben Sie ihm mit der Name-Funktion des RCS einen Namen, zum Beispiel OK.

Sind Sie damit fertig, so müssen Sie noch die Zahl der verwendeten Objekte in diesem Baum mit der Funktion INFO im RCS ermitteln und notieren, da diese später gebraucht wird. Und nach dem Abspeichern besitzen Sie auf Ihrer Diskette drei Dateien. Das sind < Name > .rsc, < Name > .h und < Name > .def (eine RCS-eigene Hilfsdatei).

Zum Schluß noch eine Warnung: erzeugen Sie niemals gleichzeitig zwei Menüleisten mit dem RCS, sonst spielt ihr ST Bombenfabrik oder Pilzzüchter, je nach TOS-Version.

So, das rsc-File haben wir nun. Also wenden wir uns den Befehlen für die Drop-Down-Menüs und Formulare zu:

Formulare und Menüleisten manipulieren

Nach der üblichen und absolut notwendigen GEM-Initialisierung (im Demoprogramm open__work();) erfolgt üblicherweise sofort der Befehl zum Laden des rsc-Files. Dieser hat das Format:

	ret = rsrc_load(< NAME>);

ret liefert einen Integerwert, 0 für einen Fehler und alle anderen bei einem Erfolg. Also ist die Standardformulierung zur Fehlerbehandlung am Beispiel unseres Programms:

if (rsrc__load(”Name.rsc”) = =0)

Fehlermeldung;

oder C-typischer:

if (!rsrc_load(”Name.rsc”))

Fehlermeldung;

Anschließend werden die Adressen der beiden Objektbäume ermittelt, da sie für alle anderen Funktionen praktisch als Identifikationsnummer dienen. Deshalb müssen im Deklarationsteil die beiden long (32-Bit) Variablen menu_addr und info—addr deklariert werden. Die Funktion dazu lautet:

ret = rsrc_gaddr(typ,index,&adresse);

ret liefert, wie alle anderen baummanipulierenden Funktionen eine 0, falls ein Fehler aufgetreten ist. Aber nur bei rsrc_load und rsrc__gaddr lohnt sich überhaupt eine Abfrage, denn dort werden fast alle Fehler abgefangen.

typ darf Werte zwischen 0 und 16 annehmen, und bestimmt die Art der Datenstruktur, deren Adresse wir benötigen. Da das Programm nur die Adresse eines Baumes braucht, hat typ hier den Wert 0.

index ist sozusagen die Kennung des Baumes, wir haben beispielsweise 2 Bäume, also auch 2 Kennungen, die in der Datei <Name>.h vom RCS geliefert wurden, nämlich MENU und INFO.

Und adresse ist schließlich die Variable, in der die Adresse des Baumes abgelegt werden soll, deshalb auch das & davor (also kein Schreibfehler). Das bedeutet wiederum als praktisches Beispiel:

ret=rsrc—gaddr(0,MENU,&menu_addr);

oder

ret=rsrc__gaddr(0,INF0,&info_addr);

Nachdem wir die Bäume unter Dach und Fach haben, wollen wir, daß sich etwa sichtbares tut. Hier sind also alle Befehle zur Manipulation der Menüleiste:

Aktivieren und deaktivieren der Menüleiste

ret = menu___bar(menuadresse,typ);

Bsp.: menu___bar(menu___addr,1);

menuadresse ist die eben von rsrc_gaddr ermittelte long-Variable und mit typ kann bestimmt werden, ob die Menüzeile gezeichnet und aktiviert (typ = 1) oder gelöscht werden soll (typ = 0).

So, die Menüleiste ist jetzt sichtbar, und als erstes benötigen wir eine Funktion, die erkennt, welcher Eintrag ausgewählt wurde. Dies ist:

ret = evnt_msg(msg_buff);

Diese Funktion wartet darauf, daß jemand einen Eintrag aus der Menüleiste auswählt. Dann übergibt sie alle notwendigen Informationen an msg— buff. Dabei ist ret immer gleich 1, hat also keine Bedeutung und kann weggelassen werden und msg—buff ist ein Arraya mit 8 Wörtern, weshalb im Deklarationsteil die Deklaration so heißen muß: int msg_buff[8]; Dieses Arraya hat für die Menüleisten folgendes Format:

0 Wort = msg___buff[0] = 10
( = MN___SELECTED, in der Include-Datei gemdefs.h) ist die Kennung dafür, daß jemand einen Menüeintrag angewählt hat.

3 Wort =
msg___buff[3] = Objektindex des angewählten Menütitels

4 Wort =
msg___buff[4] = Objektindex des angewählten Menüeintrages

Die restlichen Wörter sind hier nicht weiter interessant.

Ein Beispiel: Es wurde der Eintrag 'Anfänger’ im Untermenü ’Spielstufe’ angewählt. Dann steht im 1. Wort eine 10, im 3. Wort der Index des Menütitels, der für dieses Programm allerdings nicht wichtig ist, und im 4. Wort steht der Index von 'Anfänger', den wir im RCS als STUFEI bezeichnet haben, und der, falls Sie genau dieselbe Menüleiste haben wie ich, den Wert 19 hat, wie Sie im Programmli-sting in der eingebundenen Datei SPIEL.H leicht sehen können.

Normalisieren bzw. invertieren eines Menütitels

ret = menu_tnormal(menuadresse,index,typ);

index ist der Index des Menütitels. (Den bekommen Sie ja gratis im 3. Wort des Mesage-Buffers) und typ bestimmt wie bei menu_bar, ob der Eintrag normal (typ = 0) oder invertiert (typ = l) gezeichnet werden soll.

Jetzt werden Sie sich fragen, wozu das gut sein soll. So einfach ist das gar nicht zu erklären. Also: GEM ist ja ganz fähig, es verwaltet fast alles, aber genau dann, wenn der Benutzer einen Eintrag ausgewählt hat, bleibt der Obertitel, aus dem der Eintrag stammte, invertiert. Dies ist nützlich, da der Benutzer erkennen kann, unter welchem Obertitel die von ihm angewählte Funktion steht. Aber man muß eben die Menüleiste nach jeder Wahl wieder normalisieren, und das geschieht mit dieser Funktion.

Hier ein Beispiel des Befehls:

menu__tnormal(menu_addr, msg_buff [3],0);

Setzen und Löschen eines Hakens vor einem Eintrag

ret = menu_icheck(menuadresse, index, typ);

index ist der Index des Menüeintrages, der bearbeitet werden soll und typ ist wie oben bestimmend, ob der Haken, auch check-mark genannt, gesetzt (typ = l) oder gelöscht werden soll (typ = 0). So löscht bei unserem Programm menu____icheck(menu__addr, STUFE1,0) den Haken vor 'Anfänger'.

Aktivieren bzw. deaktivieren eines Menüeintrages

ret = menu_ienable(menuadresse, index, typ);

Diese Funktion kann einen Menüeintrag deaktivieren, das heißt, er erscheint im Menü in heller Schrift und kann nicht angewählt werden. Dies kann beispielsweise verwendet werden, den Spieler zu mindestens einem Spiel zu zwingen, eher wird die QUIT-Funktion im Menü nicht aktiviert. typ hat die Bedeutung typ=l zum Aktivieren und typ = 0 zum Deaktivieren.

Beispiel: menu__ienable(menu_addr,QUIT,0); deaktiviert QUIT.

Ändern des Wortlautes eines Eintrages

ret = menu_text(menüadresse,index,text);

index ist der Index des zu ändernden Eintrages und text ist der neue Wortlaut des Eintrages. Hiermit wird beispielsweise im Programm der Wortlaut beim Anwählen des Eintrages 'Sound aus’ in 'Sound ein’ umgewandelt. Dies geschieht durch:

menu_text(menu_addr,SOUND,"Sound ein”);

Achtung: Der neue Text darf auf keinen Fall größer als der alte sein, sonst wird die Baumstruktur zerstört. (Aber man kann ja im RCS einfach ein paar Spaces mehr eintragen).

So, den ersten Teil haben Sie jetzt hinter sich gebracht. Kommen wir nun zu den Formularbefehlen. Unter einem Formular unter GEM versteht man das, was es im Büro schon seit langer Zeit ist. Ein vorgefertigtes Rechteck, in dem nur noch vom Programm bestimmte Teile geändert oder beschrieben werden können. Doch ich will hier nicht auf alle Befehle der Formularverwaltung eingehen und nur die wichtigsten behandeln, denn Formulare sind weitaus komplizierter zu steuern als Menüs.

Bild 2: Warnmeldung

Darstellen und kontrollieren einer Warnmeldung

knopf = form_alert(return_knopf.string);

Diese Funktion ist, obwohl äußerlich einfach, dennoch ziemlich kompliziert. Aber andererseits ermöglicht Sie dem Programmierer, ohne mit dem RCS ein Formular erstellt zu haben, eine Meldung mit wahlweise 4 Pictogrammen und max. 3 Quittierungsknöpfen mit einem Text von maximal 5 Zeilen zu je 40 Zeichen auf den Bildschirm zu bringen. Dabei wird automatisch auf das richtige Format geachtet und der Bildschirminhalt unter der Meldung gesichert. Keine Angst, das klingt viel komplizierter als es ist. Hier ein Beispiel für eine Warnmeldung:

knopf=form_alert(1,”[3][Grausamer Fehler I Das rsc-File ist verschollen] [Abbruch]”);

Diese Funktion erzeugt eine Warnmeldung, wie sie in Abb. 1 zu sehen ist. In knöpf erhält man die Nummer des Knopfes, den der Benutzer angewählt hat. Die Nummer in return_knopf besagt, daß ein Knopf mit dieser Nummer auch durch die RETURN-Taste an wählbar ist und dick umrandet erscheint. (0 = kein Knopf, 1 = erste Knopf, usw.).

Und in String stehen schließlich alle anderen Informationen. Das allgemeine Format ist: [Pictogrammnummer][Text der Mittei lung][Text der Knöpfe]

Die Pictogrammnummer kann folgende Werte erhalten:

0 = kein Pictogramm
1 = NOTE-Pictogramm
2 = WAIT-Pictogramm
3 = STOP-Pictogramm

Der Text der Mitteilung kann durch ein | (logisches oder) zeilenweise getrennt werden, ebenso wie der Text zu einem Knopf von dem Text zu einem anderen durch '|’ getrennt wird. Also noch ein Beispiel:

knopf=form_alert(1,” 0 Dies ist eine Meldung I Ja Vielleicht Nein ”);

Das Ergebnis sehen Sie in der Abb. 2, und in knöpf erhält man den Wert des gedrückten Knopfes, bei ’Ja’ eine 1, bei 'Vielleicht’ eine 2 und bei ’Nein’ eine 3.

Ermitteln der ’idealen’ Position eines Baumes

Dabei verstehe ich unter einer idealen Position die Lage eines Baumes, also des Mitteilungsrechteckes, in der Mitte des Bildschirms. Die folgende Funktion errechnet diese unter Angabe der Objektadresse.

form__center(baumadresse,&x_mitte, &y_mitte,&w_mitte,&h_mitte);

baumadresse ist die in einer long-Variablen gespeicherte und mit rsrc_gaddr ermittelte Adresse einer Baumstruktur. Die integer-Variable x_mitte, y_mitte, w_mitte und h_mitte werden von der Funktion zurückgegeben und enthalten die ’idealen Koordinaten’ und die Weite und Höhe des Rechtecks. Hier wieder ein Beispiel aus unserem Programm:

form_center(info_addr,&xinfo,&yinfo, &winfo,&hinfo);

Multifunktion form_dial für 4 Aufgaben

Bei typ = 0 reserviert diese Funktion einen Bildschirmbereich, um später die Fensterrandkomponenten wieder aufbauen zu können. Es wird jedoch nicht der Bildschirminhalt, der 'unter’ dem Formular liegen wird, gesichert. Deshalb muß das Programm nach dem Löschen eines Formulars den zerstörten Bildschirm wieder aufbauen (also nicht wie bei form_alert). Dabei sind dann x, y, w und h die Maße des zu sichernden Rechtecks. Das Format ist dann:

ret = form___dial(typ,x,y,w,h);

Bei typ=1 zeichnet die Funktion einen sich ausbreitenden Kasten in mehreren Stufen von den Maßen klein., bis zu gross... Diese Funktion ist für das Programm nicht notwendig, sie dient nur dazu, den Eindruck zu erwecken, daß das Formular auf den Bildschirm gelegt wird. Das Format ist:

ret = form___dial(typ,klein_x,klein_y, klein_w.klein_h.gross__x.gross__y, gross_w,gross__h);

Bei typ = 2 passiert genau das Gegenteil von typ = l. ES wird ein schrumpfender Kasten von gross., bis klein., gezeichnet. Das Format ist dasselbe und diese Funktion ist auch nicht unbedingt notwendig.

Bei typ = 3 wird der in typ = 0 gesicherte Bildschirminhalt wieder freigegeben, also die Fensterrandkomponenten neu gezeichnet. Das Format ist wie bei typ=o.

Allgemein gilt, daß diese Funktion mit dem typ-Wert 0 und 1 vor dem Darstellen des Formulars aufgerufen wird, und die beiden anderen direkt nach dem Löschen des Baumes.

Übergeben der Kontrolle über ein Formular an GEM

index = form___do(baumadresse,textindex);

Ist das Objekt gezeichnet, so muß das Programm erfahren, was der Benutzer mit ihm macht. Diese Funktion übernimmt die vollkommene Kontrolle über ein Formular und wacht ebenso über 'Knopfdrücke’ wie Texteingaben, baumadresse ist die Adresse des zu kontrollierenden Formulars und text-index ist der Index des ersten zu editierenden Textfeldes. Ist keines vorhanden, so wird textindex auf 0 gesetzt. Wollte man beispielsweise den Namen des Spielers erfragen und würde diesen Eingabestring NAME nennen, so müßte man dessen Index in textindex einsetzen, damit GEM weiß, wo es den Cursor zuerst positionieren soll. Und in index erhält man den Index des Objektes, dessen Bestätigung zum Abbruch der Kontrolle geführt hat, das also den Status ’EXIT tragen muß. Hier ein Beispiel:

index = form_do(info_addr.0);
Bild 3: Alert-Box

Ändern des Status eines Objekts

Ein Objekt kann folgende Statuskombinationen besitzen:

kein Bit: NORMAL
Bit 0: SELECTED = Objekt ist schwarz gefärbt, d. h. angewählt
Bit 1: CROSSED = Objekt wird durchgestrichen
Bit 2: CHECKED = Objekt erhält einen Haken
Bit 3: DISABLED = Objekt wird schwach gezeichnet
Bit 4: OUTLINED = um das Objekt wird ein Rahmen gezeichnet
Bit 5: SHADOWED = das Objekt wirft einen Schatten nach rechts unten.

Und mit dieser Funktion kann dieser Status beliebig geändert werden:

ret=objc_change(baumadresse,index,0,xclip,yclip,wclip,hclip,Status,typ);

index ist der Index des zu ändernden Objektes, die 0 ist ein von GEM für spätere Anwendungen reserviertes Wort, das immer auf Null gesetzt werden sollte. Die „clip-Variablen bestimmen den Bereich, in dem der Objektstatus geändert werden soll. Dies ist für die Fenster-Technik interessant, wir setzen hier die Maße des Bildschirms ein. Status ist der neue Objektstatus, hier stehen die oben beschriebenen Werte, also zum Beispiel objekt = 32 für einen Schatten um das Objekt. Und typ bestimmt schließlich, ob das Objekt nach der Statusänderung neu gezeichnet werden (typ=l) oder ob nur der Status geändert werden soll (typ = 0).

Weitere Erläuterungen zum Programm

Hier habe ich noch einmal alle wichtigen Funktionen im Programm zusammengefaßt, doch zuerst noch einige Erklärungen zu dem sogenannten Clippen. Darunter versteht man das Begrenzen aller Ausgaberoutinen (Grafik, Text, ...) auf ein bestimmtes Rechteck. Begrenzen Sie die Ausgabe beispielsweise auf ein Quadrat in der Mitte des Bildschirms und geben dann den Befehl zum Zeichnen einer Diagonale, so wird die Gerade in Wirklichkeit nur im vorher bestimmten Rechteck gezeichnet. Das wird im Programm angewendet, wo ja durch das Zeichnen der Info-Anzeige ein Teil des Bildschirms zerstört wurde. Dieser Bereich ist aber genau bekannt (er steht in xinfo,yinfo,...), und es wäre ungeschickt, einfach den ganzen Bildschirm neu zu zeichnen. Also clippen wir diesen Bereich und rufen dann die Funktion zum Zeichnen des Titelbildes auf. Achtung: das Clippen funktioniert nicht bei der Funktion zum Bildschirm löschen v__clrwk(handle); deshalb greift das Programm auf eine eigene Löschfunktion zurück, die sich clippen läßt. Es wird einfach ein weißes Rechteck von der Größe des Bildschirms gezeichnet. Doch nun noch einiges zum Programm:



	/*********************************************************************/
	/****    Demonstrationsprogramm fuer die Drop-Down-Menues         ****/
	/*********************************************************************/ 
	/****  von  Dirk Owerfeldt, Steinstr. 22,  6601 Heusweiler-Holz   ****/
	/*********************************************************************/
	/********************/
	/** DEFINE-Befehle **/
	/********************/
	
	#define TRUE 1
	#define FALSE 0
	#define Hide()	graf_mouse(256, OL)
	#define Show()	graf_mouse(257, OL)
	
	/**************************************/
	/** Eingebundene RCS-Datei 'SPIEL.H'  */
	/**************************************/
	
	/* Sie schreiben am besten #include "spiel.h" */
	
	#define MENU 0	/*	TREE */
	#define ABOUT 10	/*	OBJECT	in	TREE #0 */
	#define STUFE1 19	/*	OBJECT	in	TREE #0 */
	#define STUFE2 20	/*	OBJECT	in	TREE #0 */
	#define STUFE3 21	/*	OBJECT	in	TREE #0 */
	#define STUFE4 22	/*	OBJECT	in	TREE #0 */
	#define SOUND 24	/*	OBJECT	in	TREE #0 */
	#define START 26	/*	OBJECT	in	TREE #0 */
	#define QUIT 28	/*	OBJECT	in	TREE #0 */
	#define INFO 1	/*	TREE */
	#define OK 6	/*	OBJECT	in	TREE #1 */
	#define TIEFE 2	/*	Tiefe der Info-Anzeige */
	
	/*****************************************/
	/** Externe Variablen fuer den Compiler **/
	/*****************************************/
	
	int	contrl[12],	/*	Diese Variablen werden vom Compiler wegen des */
		intin[128],	/*	internen Aufbaus von GEM benoetigt und werden */
		ptsin[128],	/*	von den verschiedenen GEM-Funktionen veraendert. */
		intout[128],
		ptsout[128];/* Als Anfaenger brauchen Sie sich nicht um sie zu */
					/* kuemmern, sie muessen nur im Programm als externe*/
		int	work_out[57],	/*	Variablen am Programmanfang deklariert werden.	*/
		work_in[12];
	
	int handle;	/* Enthaelt die Geraetekennung und muss bei vielen */
	/* Gem-Funktionen als Parameter uebergeben werden. */
	int q;
	
	/******************************************/
	/**  Externe Variablen fuer das Programm **/
	/******************************************/
	
	int msg_buff[8];	/*	Buffer fuer Ereignissverwaltung	*/
		
	int xinfo,yinfo,winfo,hinfo;	/*	Hilfsvariablen fuer Infoanzeige	*/
	int xdesk,ydesk,wdesk,hdesk;	/*	Hilfsvariablen fuer Clip-Parameter */
		
	long menu_addr,info_addr;	/*	Startadressen der Objektbaeume	*/
		
	int sound=TRUE;	/* Variable zum Erkennen von Sound ein/aus	*/
	int stufe STUFE1;	/* Anfangsschwierigkeit auf 'Anfaenger' setzen */
	
	/*********************/
	/** Hilfsfunktionen **/
	/*********************/
	
	open_work()	/* Oeffnen der 'Arbeitsstation' unter GEM */
	{	/* (unbedingt notwendige Prozedur)	*/
		int i ;
		appl_init();
		for(i=0;i<10;i++)	/*	Pascal: for i:=0 to 9 do */
		{
			work_in[i]=1;
		}
		work_in[10]=2;
		v_opnvwk(work_in,&handle,work_out);
	}
	clr() //* Bildschirm loeschen (v_clrwk laesst sich nicht clippen) */
	{
		int xy[4];
		vsf_interior(hand1e,0);	/* weiss ausfuellen */
		vsl_color(handle,0);	/* weisser Rand	*/
		xy[0]=0; xy[1]-0; xy[2]=640; xy[3]=400;
		v_bar(handle,xy);
		vsl_color(handle,1);	/*	Farbe wieder schwarz */
	}
		
	titelbild()	/* Aufbau des Titelbildes und aktivieren der */
	{		/* Menüleiste.	*y
		
		int xy[4];	/*Array fuer die Funktion zum Zeichnen von*/
					/*Rechtecken mit abgerundeten Ecken.	*/
		Hide();		/* Mauszeiger ausschalten */ 
		clr();		/* Bildschirm loeschen */
		
		vst_point(handle,20,&q,&q,&q,&q);	/*	Buchstabengroesse maximal	*/
		vst_effects(handle,17);	/*	Schrift hohl+fett	*/
		v_gtext(handle,230,120,"DAS SPIEL"); /*	Text ausgeben	*/
		vst_point(handle,12,&q,&q,&q,&q);	/*	Normale Buchstabengroesse	*/
		vst_effects(handle,4);	/*Schrift kursiv	*/
	
	5,"Eine Demo von Dirk Owerfeldt !");
	
		vsf_interior(handle,2);	/*	Punktiertes Fuellmuster */
		vsf_style(handle,9);	/*	Mauerwerk als Punkt-Muster*/
		
		xy[0]=2; xy[1]=250;		/*	erstes Koordinatenpaar */
		xy[2]=638; xy[3]=398:	/*	zweites Koordinatenpaar */
		v_rfbox(handle,xy);		/*	'rundes' Rechteck zeichnen*/
		
		menu_bar(menu_addr,1);	/* Menueliste zeichnen	*/
		Show();					/* Mauszeiger einschalten */
	}
		
	iraw_info()	/*	Zeichnen der Info-Anzeige (Ausloeser: Ueber dieses Spiel) */
	{
		int xy[4];	/* Arraya fuer vs_clip-Funktion */
		
		/* Koordinaten errechnen lassen, so dass der Info-Baum in der Mitte des */
		/* Bildschirms erscheint. (Koordinaten stehen dann in den info-Variablen).*/
		
		form_center ( info_addr, &xinfo, &yinfo, &winfo, &hinfo);
		
		/* Reservieren des Bildschirmbereiches fuer die Fensterrandkomponenten. */
		
		form_dial(0,xinfo,yinfo,winfo,hinfo);
		
		/* Zeichnen eines sich vergroessernden Rechtecks von der Groesse 1 und */ 
		/* den Koordinaten 1,1 bis zu den Abmessungen des Info-Rechteckes.	*/
		
		form_dial(1,1,1,1,1,xinfo,yinfo,winfo,hinfo);
		
		/* Zeichnen des ganzen Info-Baumes ohne Begrenzung beim Zeichnen, darum */
		/* werden als Clip-Parameter die Abmessungen des Bildschirms eingetragen */
		
		objc_draw(info_addr,0,TIEFE,0,0,wdesk,hdesk);
		
		/* Uebergeben der Kontrolle ueber den Info-Baum an GEM, das wartet bis */ 
		/* Knopf vom Status ’EXIT' angeklickt worden ist. */
		
		form_do(info_addr,0);	/*	warten	bis	OK-Feld angeklickt */
		
		/* Freigeben des reservierten Speicherbereichs (hat ein Neuzeichnen der */ 
		/* vom Info-Baum zerstoerten Fensterraender zur Folge) und Zeichnen eines */ 
		/* schrumpfenden Rechtecks. */
		
		form_dial(2,1,1,1,1,xinfo,yinfo,winfo,hinfo); 
		form_dial(3,xinfo,yinfo,winfo,hinfo);
		
		/* Objektstatus der OK-Taste ist nach der Betaetigung mit Return oder */ 
		/* der Maus SELECTED', d.h Bit 1 ist gesetzt und muss zurueckgesetzt */
		/* werden auf ‘NORMAL'- sonst ist nach einen 2. Aufruf der Info-Anzeige */
		/* die OK-Taste noch immer schwarz. (Format siehe Text)	*/
		
		objc_change(info_addr,OK,0,xdesk,ydesk,wdesk,hdesk,0,0);
		
		/* Alle Ausgabeoperationen wifden von dem folgenden Befehl auf ein ge- */
		/* wisses Rechteck begrenzt, in diesem Fall genau auf die Masse des von */
		/* dem Formular zertoerten Bereichs.	*/
		
		xy[0]=xinfo-1; xy[1]=yinfo-1; xy[2]=xinfo+winfo; xy[3]=yinfo+hinfo;
		vs_c1ip(handle,1,xy);	/*	Clip	ein! */
		
		titelbild();	/*	Titelbild	neu	zeichnen */
		vs_clip(handle,0,xy);	/*	Clip aus! */
	}
		
	/*****************************************/
	/** Hauptfunktion [ main()-Funktion ]	**/
	/*****************************************/
		
	main()
	{
		int q; /* Hilfsvariable fuer alle Aufgaben */
		
		open_work();	/*	Arbeitstation oeffnen	*/
		wind_get(0,4,&xdesk,&ydesk,&wdesk,&hdesk);	/*	max. Groesse bestimmen	*/
		graf_mouse(3,OL);	/*	Hand als Maussymbol	*/
		
		/****************** /
		/* RSC-FILE LADEN */
		/****************** /
		
		if (rsrc_load("SPIEL.RSC")==0)
		{
			form_alert(1,
			"[3] [Grausamer Fehler !! !Das RSC-File ist verschollen.] [Abbruch]") ;
			gemdos(0x0);	/*	Programmabbruch	*/
		}
		
		/***************************/
		/* Basisadressen ermitteln */
		/***************************/
		
		rsrc_gaddr(0, MENU, &mnenu_addr);	/* Adresse des Menubaumes */
		rsrc_gaddr(0, INFO, &info_addr);	/* Adresse der Info-Anzeige */
		
		/**********************/
		/* Titelbild aufbauen */
		/**********************/
		
		titelbild();
		
		/****************************/
		/* Menüleiste kontrollieren */
		/****************************/
		
		while(TRUE) /* bis in alle Ewigkeit diese Schleife ausfuehren */
		{
			evnt_mesag(msg_buff);	/*	Warte auf eine Aktion in der Menüleiste */
		
			/******************************************/
			/* Ist eine Spielstufe angewaehlt worden? */
			/******************************************/
		
	STUFE1)&&(msg_buff[4] < =STUFE4))	/*	'&&' < = > 'AND' */
		{
			menu_icheck(menu_addr,stufe,0);	/*	Aktuellen Haken loeschen	*/
			menu icheck(menu_addr,msg_buff[4],1);	/*	neuen Haken setzen	/
			stufe=msg_buff[4];	/*	neue aktuelle Stufe setzen	*/
		}
	
		/************************************/
		/* Ist sonst was angewaehlt worden? */
		/************************************/
		
		switch(msg_buff[4])	/*	Welche Meldung ist angekommen ? */
		{
			case ABOUT:	draw_info();
					break;
		
			case SOUND:	if (sound) { menu_text (menu_addr, SOUND," Sound ein");
								sound=FALSE; }
					else { menu_text(menu_addr,SOUND," Sound aus");
								sound=TRUE;	}
					break;
		
			case START: spiel();	/* zum eigentlichen Spielprogramm */
					titelbild();	/* Titelbild neu zeichnen	*/
					break;
		
			case QUIT:	q=form_alert(1,
			"[3] [Wollen Sie dieses Programm|wirklich beenden ?] [Beenden | Weiter]");
					if (q==1) gemdos(0x0);
	
		} /* switch-Ende */
		
		menu_tnormal(menu_addr,msg_buff[3],1);	/*	Menüleiste normalisieren in— */
												/* dem der Titel, dessen Index */
												/* in msg-buff[3] steht, neu	*/
												/* (normal) gezeichnet wird. */
	} /* Ende der Endlosschleife */
		
	} /* Ende der Hauptfunktion main() */
		
	spiel ()
	{
		char h[50];
		clr();	/* Bildschirm loeschen */
		v_gtext (handle , 100,100 ," Ihr Spiel steht hier ! ") ;
		
		sprintf(h,"Es wurde die Spielstufe %1d angewählt !“,stufe-STUFE1+1); 
		v_gtext(handle,100,200,h);
		
		if (sound) v_gtext(handle,100,250,"Sie wollten Musik!"); 
		else	v_gtext(handle,100,250,"Sie wollten keine Musik!");
	—> Taste"); 
		gemdos(0x1);
	}

So, jetzt müßten Sie in der Lage sein, selbst Menüleisten zu entwickeln und zu steuern.

Das Programm wurde mit dem MEGAMAX C-Compiler entwickelt und compiliert.

Dirk Owerfeldt



Aus: ST-Computer 10 / 1986, Seite 53

Links

Copyright-Bestimmungen: siehe Über diese Seite