ST-Ecke: Ein neuer Dialog-Handler

Wem ist es beim Programmieren von Dialogboxen noch nicht so ergangen, daß er beim Konstruieren einer Dialogbox einfach kein Ausgangsobjekt eingebaut hat? Mit dem Erfolg, daß ein Verlassen desselben nach Aufruf der Funktion form_do() nicht mehr möglich war? Oder ist es nicht ärgerlich, daß das Fortbewegen von Textfeld zu Textfeld mit den Cursor-Tasten und nicht mit der Return-Taste vollzogen werden muß? Diesen beiden .Ärgernissen1 wollen wir uns diesen Monat annehmen. Noch dazu werden wir die Bindings für zwei zusätzliche, bisher kaum beachtete Routinen, form_button und form_keybd, im AES liefern!

Willkommen zu unserer fünften ST-Ecke in der Juli-August-Ausgabe der ST-Computer. Da bis September eine lange Zeit bis zur nächsten ST-Ecke vergehen muß, habe ich mir ein ganz besonderes Bonbon ausgedacht. Es kamen viele Anfragen bezüglich der objc_edit-Routine, mit dem verzweifelten Aufschrei, daß mehr Abstürze als Erfolge zu vermelden waren, was, wie Sie später sehen werden, als verständlich anzusehen ist. Sie werden außerdem in dieser ST-Ecke erfahren, wie Sie ganz einfach Ihre eigene form_do()-Routine schreiben können. Sie sehen, diesen Monat wollen wir hauptsächlich die Fortgeschrittenen unter Ihnen ansprechen, wobei natürlich auch die Anfänger herzlich zu unserem Streifzug durch das AES eingeladen sind.

Ein Fehler bei Objc_edit()

Fangen wir also mit der Beschreibung der objc_edit Routine an: Und genau dies ist der Punkt, warum die meisten unter Ihnen gescheitert sind, denn zwischen der Beschreibung der objc_edit-Routine in der weitverbreiteten Literatur, die sich auf die Digital-Research-Dokumentation stützt, und des Bindings besteht ein Unterschied!!! Das bedeutet, daß die Übergabe der Parameter im Digital-Research-C-Compiler unterschiedlich zur angegebenen Dokumentation gemacht worden ist. Da sich die meisten Softwarefirmen aber an dieses Binding und nicht an die Dokumentation an sich gehalten haben, hat sich dieser Fehler leider auch in andere Softwarepakete eingeschlichen. Schauen wir uns also an, wie die Original-Parameterübergabe abläuft:

Wie man sieht, wurde beim Binding im Atari sogar der vierte Parameter vollständig weggelassen, was dafür spricht, daß er gar nicht benötigt wird. Ärgerlich ist na-rarlich, daß Parameter vertauscht wurden, die Adressen enthalten, womit in der Regel ein Systemabsturz ,vorprogrammiert' ist.

Um mit beiden Parameterformaten arbeiten zu können — wir wollen die Programme so schreiben, daß sie übertragen sind — habe ich eine Definition verwendet, die roigendermaßen aussieht:

	#define Objc_edit (a,b,c,d,e,f) 
	objc_edit(a,b,c,f,e)

Dieses Makro ermöglicht es dem Programmierer, sowohl die dokumentierte Form — großgeschriebener Befehl — als auch die tatsächlich im Binding enthaltene Form — kleingeschriebener Befehl — zu erwenden. Sie werden in dem folgenden Lasting erkenne, daß ich die in der Dokumentation angegebene Form verwenden werde, damit eine bessere Les- und Übertragbarkeit gegeben ist.

Wichtig bei dieser Funktion ist nicht, daß das Objekt auch wirklich editierbar ist — dies ist nur für form_do() ein Hilfsmittel —, sondern, daß die Objekte von der Objektart G_TEXT oder G_BOXTEXT sind.

Achtung! Kein Atari-Format:

objc_edit(ob_edtree, ob_edobject, ob_edchar, ob_edidx, ob_edkind, & ob_ednewidx)

objc_edit, so, wie es als Binding in den meisten Compilern und Interpretern vorliegt:

objc_edit(ob_edtree, ob_edobject, ob_edchar, &obednewidx, ob_edkind)

Zwei neue AES-Routinen

Kommen wir nun zu den weniger bekannten Routinen form_keybd() und form_button(), die, obwohl kaum erwähnt, im Betriebssystem des Atari ST Vorkommen. Zunächst form_keybd: form_keybd ist im Prinzip ein Filter, der alle Control-Codes (Steuerzeichen) wie Cursortasten oder die Esc-Taste übernimmt und ihre entsprechenden Funktionen durchführt. War das übergebene Zeichen ein Steuerzeichen, so führt form_keybd die entsprechende Funktion durch und gibt eine 0 zurück. Ist das Zeichen kein Steuerzeichen, wird es nicht herausgefiltert, sondern einfach wieder zurückgegeben — man kann es dann als ganz normales Zeichen in das momentan aktuelle Text feld mit objc_edit einfügen. Besonders wenn man einen eigenen Dialog-Manager schreiben möchte, zeigen sich die Vereinfachungen durch diese Routine.

**Nun zur Parametererklärung von Objc_edit:**

ob_edtree: Zu editierender Baum

ob_edobject: Zu editierendes Textobjekt (editable textfield)

ob_edchar: einzufügender Buchstabe im GEM-Code, der im Low-Byte den ASCII-Code und im High-Byte den Scancode der Tastatur enthält. Dieser GEM-Code wird von evnt_multi geliefert.

ob_newidx: enthält die neue Position des Cursors innerhalb des Textfeldes.

ob_edkind: bestimmt das Verhalten der Funktion:

0: ED_START wird nicht benutzt
1: ED_INIT initialisiert das Editieren des Textfeldes und setzt zum Beispiel den Cursor in dieses Textfeld. Soll ein anderes Textfeld editiert werden, muß mit ED_END dieses Textfeld erst beendet werden.
2: ED_Char sorgt für das Einschieben des Buchstabens in das Textfeld.
3: Schließt das Editieren des Textfeldes und entfernt den Cursor.

form_button() übernimmt die aufwendige Arbeit, die beim Anklicken eines Objektes mit der Maus vollzogen werden muß. So ist gerade das Bearbeiten von Radio-Buttons, müßte man es selbst programmieren, sehr aufwendig. Ist nun das angewählte Objekt mit EXIT oder TOUCHEXIT gekennzeichnet, so gibt form_button() dem aufrufenden Programm den Wert FALSE zurück, woran dieses Programm erkennen kann, daß der Benutzer die Dialogboxbearbeitung abbrechen möchte. Wird als Anzahl der Mausklicks eine zweite übergeben, so wird das höchste Bit des Objektes gesetzt, das von form_button zurückkommt. Eine Eigenschaft, die übrigens auch von form_do() weitergegeben wird, denn wenn die Dialogbox mit einem Double-Click verlassen wurde, so ist das oberste Bit des zurückgegebenen Objektes gesetzt. Wird auf dieses Bit keinen Wert gelegt, so sollte es besser ausgeblendet werden, um Fehlfunktionen zu vermeiden.

Nachdem wir nun das Handwerkszeug zum Schreiben einer eigenen Form_do()-Routine haben, noch ein paar Worte zu dem Begriff Binding. Ein Binding (sprich beinding) ist eine Anbindung einer Formulierung eines Hochsprachenbefehls an das Betriebssystem. So werden alle nötigen Parameter eines GEM-Befehls durch das Binding in GEM-eigene Felder (Adressen und Wort-Felder wie zum Beispiel int_in[ ]) geschrieben und dann das AES oder das VDI angesprungen. Anhand des Inhalts der Felder entscheiden dann AFS, VDI etc., welche Prozedur intern gemeint worden ist und führen diese dann aus. Da dieses Einschreiben in die Felder auf die Dauer recht aufwendig zu programmieren ist (wer kann sich schon merken, wo welcher Parameter wann eingeschrieben werden muß), legt man sich Bindings (Prozeduren, die die Parameterm in die Felder schreiben und dann in das VDI, AES etc. springen) an. Am besten fügen Sie die für die Bindings benötigten Definitionen in ,gemdefs.h‘ ein, während die Bindings am besten in einer Library (Bibliothek) ihren Platz finden würden, um nicht immer mitcompiliert zu werden.

Ein eigener Dialogbox-Manager

Im Prinzip finden Sie in dem abgedruckten Listing eine recht genaue Wiedergabe der Original form_do()-Routine des AFS, die allerdings um einige kleine Features erweitert worden ist. Zunächst überlegen wir uns, woran sich diese Routine kümmern soll:

— Wird der Routine kein bestimmtes Textfeld übergeben (zweiter Parameter von form_do = 0), so muß sie schauen, ob sich ein Textfeld in der Dialogbox befindet und setzt dieses dann als erstes Textfeld ein. — Wenn der Benutzer einen Buchstaben eingibt, so muß er gegebenenfalls in ein vorhandenes Textfeld eingefügt werden. Bestimmte Steuercodes wie ESC etc. müssen ausgeführt werden. — Klickt der Benutzer mit der Maus ein Objekt an, so muß dieses Objekt eventuell selektiert werden oder, falls es ein Textfeld ist, muß die Routine den Cursor in dieses Textfeld stellen. Nicht zu vergessen die Handhabung der Radio-Buttons.

Im ersten Moment klingt dies nach sehr viel Arbeit. Arbeit, die uns aber glücklicherweise durch die Routinen form_keybd() und form_button() größtenteils abgenommen wird. Gehen wir nun Schritt für Schritt durch die neue form_do()-Routine, die wegen Unterschieden zu der Original-Routine in do_form umgenannt worden ist. Die Parameter bleiben die Gleichen.

Zunächst schalten wir einmal die Maussteuerung so um, daß der Benutzer zum Beispiel keine Menüzeilen mehr auswählen kann. Danach kümmern wir uns um das erste Textfeld. Für das Suchen des ersten Textfeldes ab einem bestimmten Objekt haben wir uns die Routine find_objc (nicht zu verwechseln mit objc_find()) zunutze gemacht, die uns diese Arbeit übernimmt. Sie kann sogar durch Übergabe des DEFAULT-Wertes im dritten Parameter nach dem ersten DEFAULT-gekennzeichneten Objekt suchen. Findet die Routine kein entsprechendes Objekt, so gibt sie eine Null zurück. Rufen wir nun die Routine fm_inifld() auf, so sucht diese mit Hilfe der Routine find_objc() nach dem ersten Textfeld in der Baumstruktur, falls do_form() nicht schon ein Textobjekt übergeben worden ist. Der nächste Teil der do_form-Routine wird in einer Schleife abgearbeitet, solange kein (TOUCH)EXIT-Knopf, keine UNDO-Taste und kein RETURN bei vorhandenem DEFAULT-Objekt gedrückt worden sind. Nun muß im nächsten Schritt das aktuelle (jetzt unser erstes) Textfeld zum Editieren initialisiert werden, was mit der Routine objc_edit und angegebenem EDIINT geschieht. Danach erscheint der Textfeld-Cursor in dem angegebenen Textfeld. Nun befindet sich do_form() in einer evnt_multi-Schleife, die auf Tastatur und Maus (Double-Click der Maus ist auch zugelassen) reagiert.

Abb. 1: Beispiel für die neue Form do-Routine

RETURN simuliert die Cursortaste

Jetzt kommt die erste Neuerung in unserer do_form-Routine gegenüber dem Vorbild. Ist die Taste RETURN (GEM-Code Hex 1c0d) angewählt worden, so müssen wir (mit find_objc()) überprüfen, ob ein DEFAULT-Objekt vorhanden ist.

Ist dies nicht der Fall, wandeln wir einfach den Tastaturcode von RETURN in den Steuercode Cursor-Unten um, der dann — wie oben angesprochen — von form_keybd ausgewertet wird. Wird der RETURN-Code noch dadurch benötigt, daß ein DEFAULT-Objekt vorhanden ist, lassen wir ihn einfach ,in Ruhe', was natürlich den Nachteil hat, daß man sich nicht von Textfeld zu Textfeld mit RETURN weiterbewegen kann. Man kann halt nicht alles haben ... Als nächsten Schritt führen wir unsere zweite Erweiterung durch, indem wir auf die UNDO-Taste abfragen. Ist diese gedrückt worden, so müssen wir nur dafür sorgen, daß die do_form-Routine ordentlich verlassen wird. Durch das Löschen der Variablen fm_cont und fm_next_obj zwingen wir die unten vorkommende Abfrage dazu, das momentane Editfeld für die Editierung zu schließen und danach die Routine zu verlassen Dabei wird übrigens der Wert 0 zurückgegeben, so daß man daran erkennen kann, daß der Benutzer die Dialogboxen mit UNDO verlassen hat.

Soweit unsere eigenen Änderungen: Ist die Taste UNDO nicht angewählt worden, so geschieht das eigentliche Abarbeiten der gedrückten Taste. Zunächst schicken wir den Tastencode dazu durch den Filter form_keybd(), der uns — wie oben schon erwähnt — das Handling der Steuercodes abnimmt. Hat form_keybd() unser Zeichen nicht verwerten können, werten wir es aus, in dem wir es mit objc_edit() in das aktuelle Textfeld einfügen. Damit hätten wir — dank form_keybd() — die Arten der Tastaureingabe geschafft.

Bleibt nur das Anklicken eines Objektes mit der Maus. Dazu übergeben wir, falls ein Mausklick vorlag — fm_which enthält das Bit MU_BUTTON —, die X- und Y-Koordinaten der objc_find()-Routine, die sucht, ob unter der Maus ein Objekt zu finden ist. Ist die Maus außerhalb der Dialogbox gedrückt worden, so ist der Rückgabewert von objc_find() eine -1. Tritt dieser Fall auf, reagieren wir mit einem — mehr oder weniger durchdringenden — Ton darauf. Dies geschieht mn der Routine Dosound(), die im Xbios des Atari ST zu finden ist. Möchten Sie einen anderen Ton oder gar eine kleine Melodie, steht Ihnen die Datenmenge fm_sound_dta[] zur freien Verfügung. Wenn nun allerdings ein Objekt unter dem Mauszeiger gefunden worden ist (Rückgabewert >= 0), lassen wir form_button() die ausführliche Arbeit des Selektierens von zum Beispiel Radio-Buttons und Auswertens von (TOUCH)EXIT-Buttons durchführen. Dadurch haben wir viel Arbeit gespart. Als letzten wichtigen Schritt müssen wir eventuell die Editierung des aktuellen Textstrings ausschalten. Dies muß dann getan werden, wenn entweder ein anderer Textstring über Cursor, RETURN oder Maus angewählt worden ist oder die do_form()-Routine durch einen Button oder dadurch die Anwahl der UNDO-Taste verlassen werden soll. Denken Sie daran, daß die objc_edit()-Routine immer nur einen aktuellen Textstring bearbeiten kann, so daß ein Einschalten eines neuen Textstrings erst nach dem Ausschalten des alten Textstrings möglich ist.

Wenn der Benutzer nun wünscht, den Dialog abzubrechen, geben wir die Mauskontrolle an GEM (wind_(END_MCTRL)) und das Ausgangsobjekt an das aufrufende Programm wieder zurück. Zum Schluß möchte ich noch kurz auf find_objc eingehen. Wie oben erwähnt, dient die Routine dazu, ein Objekt mit bestimmten Flags zu suchen. Sie können sie natürlich insofern erweitern, daß sie auch das Suchen von anderen Flags außer EDITABLE und DEFAULT übernimmt. Beachten Sie bitte, daß, wenn Sie nach DEFAULT suchen, nur vorwärts gesucht wird, und wenn Sie FMD_BACKWARD übergeben, daß sowohl der ,case FMD_BACKWARD‘ als auch die folgende ,case‘-Befehlsfolge ausgeführt werden, da kein ,break1 eingefügt worden ist.

Ein Ausblick

Sicherlich könnte man in diese nun offen dargelegte do_form()-Routine noch einige sinnvolle Eigenschaften einbauen. So wäre es zum Beispiel interessant, bestimmte Objekte, gekennzeichnet durch bestimmte Flags — in OB_FLAGS sind einige Bits unbenutzt, die problemlos verwendet werden können —, nur dadurch selektierbar zu machen, daß die Maus sie berührt. Ein Tip dazu: Eine Schleife, die dauernd das unter der Maus befindliche Objekt durch objc_find() errechnet, dürfte recht langsam sein. Oder wie wäre es, wenn Sie die RETURN-Abfrage der Textfelder dahingehend erweitern würden, daß, wenn der Cursor am letzten Textfeld angekommen ist, er wieder zum ersten Textfeld springt? Eine äußerst noble Idee wäre es, daß man in der Objektstruktur an einer Stelle einen Tastaturcode unterbringt und dann auf Tastendruck dieses Objekt selektieren kann. Sie erkennen sicherlich, daß ohne größeren Aufwand eine Menge Ideen zu verwirklichen sind. Lassen Sie ihrer Fantasie freien Lauf. Sollte Ihnen eine besonders gute (aber nicht zu komplizierte) Erweiterung einfallen, so könnte man diese vielleicht in einer der folgenden ST-Ecken abdrucken. Nur Mut!

Ich hoffe, daß ich mit dieser Ausgabe einigen Lesern unter ihnen helfen konnte, da ich aus den Zuschriften entnehmen konnte, daß viele der Programmierer Probleme mit der objc_edit()-Routine zu haben schienen. Außerdem dürfte mit do_form() das leidige Neustarten des Computers der Vergangenheit angehören, falls man mal einen EXIT-Knopf vergessen hat. Zuletzt möchte ich nicht verschweigen, daß die Grundvorlage (die aber leider so nicht funktionierte) von TIM OREN, dem Schreiber des RCS stammt, und einige Änderungen zur Lauffähigkeit der Routine wie auch den Bindings von Klaus Ullmann durchgeführt worden sind.

(SH)


#include <obdefs.h>	/*	Deklaration der Konstanten und Strukturen */
#include <osbind.h>	/*	Betriebssystemroutinen */
#include <gemdefs.h>	/*	GEM-Definitionen */
#include <define.h>	/*	Definitionen wie z.B. TRUE. */
#include "beispiel.h"	/*	Einbinden der vom RCS definierten Namen */

main()
{
	int exit_obj;
	long tree;	/*	enthält Adresse des Baumes */

	appl_init();	/*	Application anmelden */

	/* Resource-Datei laden und falls ein Fehler passiert ist, so muß
	die Applikation abgemeldet werden und dann das Programm verlassen 
	werden */

	if (!rsrc_load("beispiel.rsc"))	/* Datei laden. Fehler ? */
	{								/* Ja! Meldung ausgeben */
		form_alert(1,"[1][Beim Laden der RSC-Datei|ist ein Fehler aufgetreten.][ Abbruch ]"); 
		appl_exit();				/* Applikation abmelden */
		return;						/*	Abbruch */
	}

	rsrc_gaddr(0,0,&tree);	/* Adresse des Baumes Beispiel */

	box_draw(tree,320,200,30,30);	/* Box auf dem Bildschirm zeichnen */
	exit_obj=do_form(tree,EDIT1);	/* Dialogbox abarbeiten, 1. Editfeld */
	undo_objc{tree,exit_obj,SELECTED);/* Ausgangsobjekt deselektieren */ 
	box_undraw(tree,320,200,30,30); /* Box wieder schließen */

	appl_exit(); /* Applikation abmelden */
}

box_draw(tree,x,y,w,h) 
int x, y, w, h; 
long tree;
{
	int xdial,ydial,wdial,hdial;

	form_center ( tree, &xdial, Sydial, &wdial, &hdial ); 
	form_dial ( 0, x, y, w, h, xdial, ydial, wdial, hdial );
	form_dial ( 1, x, y, w, h, xdial, ydial, wdial, hdial );
	objc_draw ( tree, 0, 8, xdial, ydial, wdial, hdial );
}

box_undraw(tree,x,y,w,h) 
int x, y, w, h; 
long tree;
{
	int xdial,ydial,wdial,hdial;

	form_center ( tree, &xdial, &ydial, &wdial, &hdial ); 
	form_dial ( 2, x, y, w, h, xdial, ydial, wdial, hdial );
	form_dial ( 3, x, y, w, h, xdial, ydial, wdial, hdial );
}

undo_objc(tree,which,bit) 
long tree; 
int which,bit;
{
	OBJECT *zeiger;

	zeiger = (OBJECT*) (tree+ which*sizeof(OBJECT)); 
	zeiger->ob_state &= ^bit;
}

do_objc(tree,which,bit) 
long tree; 
int which,bit;
{
	OBJECT *zeiger;

	zeiger * (OBJECT*) (tree+ which*sizeof(OBJECT)); 
	zeiger->ob_state |= bit;
}

/*
	Diese Definition wandelt den GEM-DRI in den ATARI-GEM Befehl um
*/

#define Objc_edit(a,b,c,d,e,f) objc_edit(a,b,c,f,e)
/*      ^^^^^^ DRI-Angabe        ^^^^^Atari-Binding */

/*
	Definitionen für form_keybd, form_button und do_form 
	Sie sollten in die Datei gembind.h eingebunden werden.
*/

#define FMD_FORWARD 1	/* vorwärts suchen */
#define FMD_BACKWARD 2	/* rückwärts suchen */
#define FMD_DEFLT 0		/* nach DEFAULT-Objekten suchen */

#define FM_FORM addr_in[0] /* Obergabeparameter für Binding */
#define FM_OBJ int_in[0]

#define FM_CLKS int_in[1]
#define FM_INXTOB int_in[2]
#define FM_ICHAR int_in[1] 
#define FM_ONXTOB int_out[l]
#define FM_OCHAR int_out[2]

#define FORM_KEVBD 55	/* Funktionsnummer der AES-Routine */
#define FORH_BUTTON 56 /* Funktionsnummer der AES-Routine */

#define RET_CODE int_out[0] /* Rückgabe der Funktion */

extern long addr_in[]; 	/* Binding-Variablen */
extern int int_in[], int_out[];

/*
	Die folgenden Definitionen sollten am besten in die 
	AES - Library ihres Compilers eingebunden werden.
	Die folgenden Bindings sind für MEGAHAX-C. Der Unterschied zum 
	Beispiel zu DRI liegt darin, daß MEGAMAX intern schon die Feld-
	Variablen für GEM definiert. Dies geschieht allerdings unter 
	anderen Namen als in bei Digital Research dokumentiert, so daß aus 
	intin[] bei MEGAMAX int_in[] usw. wird.
*/
/*
	Einbindung des neuen Befehls FORM_KEYBD
*/

int form_keybd(form, obj, nxt_obj, thechar, pnxt_obj, pchar) 
long form;		/* Baumadresse */
int obj;		/* zu editierendes Objekt */
int nxt_obj;	/* nächstes Editfeld */
int thechar;	/* Buchstabe in GEM-Code (SCANcode/Ascii */
int *pnxt_obj;	/* nächstes Objekt als Rückgabe */
int *pchar;		/* Buchstabe zurück, wenn nicht gebraucht
				, sonst = 0 */
{
	FM_FORM = form;
	FM_OBJ = obj;
	FM_INXTOB = nxt_obj;
	FM_ICHAR = thechar; 
	crys_if( FORM_KEYBD );
	*pnxt_obj = FM_ONXTOB;
	*pchar = FM_OCHAR; 
	return( RET_CODE );
}

/*
	Einbindung des neuen Befehls FORM_BUTTON
*/
int form_button(form, obj, clks, pnxt_obj)
OBJECT	*form;		/* Baumadresse */
int		obj;		/*	Objekt */
int		clks;		/*	Anzahl der Mausklicks (für Touchexit) */
int		*pnxt_obj;	/*	Rückgabe */
{
	FM_FORM = (long)form;
	FM_OBJ = obj;
	FM_CLKS = clks; 
	crys_if( FORM_BUTTON );
	*pnxt_obj = FM_ONXTOB; 
	return( RET_CODE );
}
/*
	Diese Funktion sucht das erste EDIT-Feld im Baum, falls fm_start_fld gleich 0 ist, sonst springt sie gleich zurück

	Die Parameter sind
	fm_tree	-	Adresse	des zu durchsuchenden Baumes
	fm_start_fld - Feld bei dem die Suche losgehen soll!?
*/

int fm_inifld(fm_tree,fm_start_fld) 
long fm_tree; 
int fm_start_fld;
{
	if(fm_start_fld == 0)	/* Nur suchen, wenn keins gegeben */
							/* hier beginnt die Suche */ 
		fm_start_fld = find_obj(fm_tree,0,FMD_FORWARD); 
	return(fm_start_fld);	/*	Rückgabe des ersten EDIT-Feldes */
}
/*
	Hier ist nun die eigentliche Suchroutine, die nach dem ersten Textfeld oder Default-Objekt sucht.

	Die Parameter sind
		fm_tree	-	Adresse des zu durchsuchenden Baumes
		fm_start_obj - das Startobjekt 
		fm_witch	-	gibt	die Suchrichtung an
*/
int find_obj(fm_tree,fm_start_obj,fm_which)
OBJECT *fm_tree;	/*	Objektweises durchlaufen */
int fm_start_obj;	/*	Startobjekt */
int fm_which;	/*	Welches Flag: EDITABLE oder DEFAULT */
{
	int	fm_obj;		/*	laufende Objektnummer	*/
	int	fm_flag;	/*	Flag nach dem gesucht wird	*/
	int	fm_theflag;	/*	Flag des laufenden Objektes	*/
	int	fm_inc;		/*	legt die Suchrichtung fest	*/

	fm_obj = 0;		/*	laufendes Objekt auf 0 setzen	•/
	fm_flag = EDITABLE;	/*	es wird nach EDITABLE gesucht	•/
	fm_inc = 1;		/*	Suchrichtung in fm_inc setzen 1 entspricht vorwärts	*/

	switch(fm_which)
		{
		case FMD_BACKWARD:
			fm_inc = -1;	/*	-1 addieren, rückwärts	*/
		case FMD_FORWARD:
			fm_obj = fm_start_obj + fm_inc; /* addieren */ 
			break; 
		case FMD_DEFLT:
			fm_flag = 2;	/* nach DEFAULT-Flag suchen */
			break;
		}
	while(fm_obj >= 0)	/*	so lange Suchen bis Objekt >=0 */
		{
		fm_theflag = fm_tree[fm_obj].ob_flags; /* Flags von Objekt feststellen */ 
		if(fm_theflag &	fm_flag)	/*	und auf EDITABLE überprüfen	*/
			return(fm_obj);	/*	wenn gefunden -> zurück	*/
		if(fm_theflag &	LASTOB)	/*	falls letztes Objekt ->	*/
			fm_obj = -1;	/*	zurück mit nicht gefunden	*/
		else
			fm_obj += fm_inc;	/* Zähler Objekt weiter laufen lassen */
		}
	return(fm_start_obj);	/*	Rücksprung bei nicht gefunden */
}
/*
	Tontabelle für do_form()
	Kann bei Kenntnis des Dosound-Formats beliebig geändert werden
*/
char fm_sound_dta[] =
	{0,0x34,1,0,2,0,3,0,4,0,5,0,6,0,7,0xfe,
	8,31,9,0,10,0,11,0,12,16,13,9,0xff,0};

/*
do_form

	Dieser Befehl ist eine fast genaue Kopie des form_do Befehls.
	Wegen dieser Unterschiede wurde eine Namensänderung durchgeführt.
	Er besitzt die gleichen Parameter wie form_do().

	Die Abänderungen, die hier vorgenommen wurden, bestehen darin, daß diese Routine auf die Taste UNDO mit dem Verlassen der Box reagiert.
	Außerdem wird mit der Taste RETURN ein Vorangehen in den editierbaren Textfeldern bewirkt(!). sofern keine Taste als DEFAULT angemeldet wurde.
*/

do__form(fm_tree,fm_start_fld)	/* Neuer Dialog-Handler */
long fm__tree;	/* Baumstrukturadresse */
int fm_start_fld;	/* Startobjekt: erstes Textfeld */
{
	int fm_edit_obj;	/* momentanes Objekt */
	int	fm_next_obj;	/*	nächstes	Objekt */
	int	fm_which;		/*	Rückgabe des evnt_multi	*/
	int	fm_cont;		/*	Flag für Fortlaufen der	Schleife */
	int	fm_idx;			/*	Index im Editfeld (Position)	*/
	int fm_mx,fm_my,fm_mb,fm_ks,fm_kr,fm_br; /* Event-Variablen */
	int fm_msg[8];	/* Message-Buffer */

	wind_update(BEG_MCTRL);	/* Bedienung der Maus auf eigene */
							/* Kontrolle umschalten	*/
	fm_next_obj = fm__inifld(fm_tree,fm_start_fld); /* erstes Editfeld suchen */ 
	fm_edit_obj = 0;	/*	wird unten benutzt */
	fm_cont = TRUE;	/*	Schleife laufen lassen */

	while(fm_cont)	/*	Solange	Flag * TRUE */
		{
		/* Wenn neues Textfeld angewählt */
		if ((fm_next_obj != 0) && (fm_edit_obj != fm_next_obj))
			{
			fm_edit_obj = fm_next_obj; 
			fm_next_obj = 0;
			/* Textfeld für Editieren vorbereiten */
			Objc_edit(fm_tree,fm_edit_obj,0,fm_idx,EDINIT,&fm_idx);
			}
		/* Auf Tastendruck, Maustaste oder Message warten */ 
		fm_which = evnt_multi(MU_KEYBD|MU_BUTTON|MU_MESAG,
			0x02,0x01,0x01,
			0,0,0,0,0,
			0,0,0,0,0, 
			fm_msg,
			0,0,
			&fm_mx, &fm_my, &fm_mb, &fm_ks, &fm_kr, &fm_br);

		if(fm_which & MU_KEYBD) /• Taste ist gedrückt worden */
			{

/* Im folgenden wird mit find_obj abgeprüft, ob in dem Baum ein Objekt mit DEFAULT-Flag vorhanden ist. Ist dies nicht der Fall,

so wird die Tastenkombination RETURN in die Tastenkombination der Cursor-Unten-Taste umgewandelt, die für ein Fortschreiten in den Editfeldern sorgt.

Tastenkombination RETURN:	0x1C0D
Tastenkombination Cursor-Unten: 0x5000	*/

		if (fm_kr & 0x1c0d && !find_obj(fm_tree,0,FMD_DEFLT)) fm_kr=0x5000;

/* Ist die Taste UNDO gedrückt worden ? */
		if((fm_kr & 0xff00) =* 0x6100)	/*	JA	*/
			{
			fm_cont = 0;	/*	Schleife abbrechen */
			fm_next_obj = 0;	/*	kein Editfeld mehr */
			}
		else /* Taste UNDO nicht gedrückt worden •/
			{
/* Form_keybd{) sorgt dafür, daß eventuelle Steuertasten wie zum Beispiel die Cursor-Oben- oder Cursor-Unten-Tasten ausgeführt werden */

			fm_cont = form_keybd {fm_tree, fm_edit_obj, fm_next_obj, fm_kr, &fm_next_obj, &fm_kr);

/* Wenn form_keybd() das Zeichen verwendet hat, so schickt es eine Null zurück. Andernfalls müssen wir uns um das Zeichen kümmern, d.h. in das momentane Editfeld einfügen, was mit Objc_edit geschieht */

			if(fm_kr)
				Objc_edit (fm_tree, fm_edit_obj, fm_kr, fm_idx, EDCHAR, &fm_idx);
			}
		}
	if(fm_which & MU_BUTTON) /* Maustaste wurde gedrückt */
		{
		/* Suche Objekt unter dem Maussymbol */
		fm_next_obj = objc_find(fm_tree,ROOT,MAX_DEPTH,fm_mx,fm_my);

		/* Ist das Ergebnis -1, so befindet sich der Mauszeiger außerhalb der Dialogbox ! => Ton erzeugen */ 
		if(fm_next_obj == -1)
			{
			Dosound(fm_sound_dta);	/* Ton erzeugen */
			fm_next_obj = 0;	/*	kein Objekt */
			}
		else /* Andernfalls Objekt gefunden, eventuell selektieren etc. */ 
			fm_cont = form_button(fm_ tree, fm_next_obj, fm_br,&fm_next_obj);
		}
	/* Wenn folgende Bedingungen erfüllt, muß Editieren des aktuellen Textfeldes beendet werden: neues Textfeld, Abbruch der do_form */

	if( (! fm_cont) || ((fm_next_obj != 0) && (fm_next_obj != fm_edit_obj)))
		Objc_edit(fm_tree,fm_edit_obj,0,fm_idx,EDEND,&fm_idx);
	}
	wind update(END_MCTRL);	/* Mauskontrolle zurück an GEM */
	return(fm_next_obj);	/* Exit Objekt zurückgeben	•/
}
( Ende des Listings)


Aus: ST-Computer 08 / 1987, Seite 20

Links

Copyright-Bestimmungen: siehe Über diese Seite