ST-Ecke: HEADER-Files des Resource-Construction-Set

Diesmal wollen wir uns besonders mit dem sogenannten HEADER-File des Resource-Construction-Set (von Digital Research) beschäftigen. Diese mit dem Extender ’.H’ (bzw. in manchen anderen Resource Construction Sets ’.RSH’) abgespeicherte Datei erleichtert das Arbeiten mit Resourcen ungemein, da das Suchen der Objektnummern entfällt. Im zweiten Teil der heutigen Ecke werde ich ein wenig auf die oft erwähnte Routine rc_intersect() eingehen und ihren Source-Code erklären.

In letzter Zeit stellte sich heraus, daß das Programmieren von GEM-Programmen auf dem Atari ST ohne ein Resource-Construction-Set sehr mühsam ist. Deshalb hat sich ATARI vor einiger Zeit entschlossen, dieses äußerst hilfreiche Programm allgemein zugänglich zu machen. Dadurch müssen bei den verschiedenen Listings die Resourcen der Programme nicht mehr als Zahlen mitgeliefert werden. Wer gibt schon gerne Zahlenkolonnen ein, die über mehrere Seiten gehen? Das RCS können alle Leser über den Leser-Service der Redaktion gegen einen geringen Unkostenbeitrag beziehen. Trotzdem sei hier noch einmal erwähnt, daß es nicht PUBLIC-DOMAIN ist!

Diejenigen, die unter ’C’ schon mit dem RCS gearbeitet haben, werden die oben erwähnte ’.H’-Datei vielleicht schon kennen, die das RCS auf Wunsch produziert. Was hat es damit auf sich? Wie Sie vielleicht wissen, ist es für die interne Bearbeitung von Baumstrukturen unerläßlich, die Indexnummer bestimmter Objekte zu kennen. Konstruiert man also z. B. mit dem RCS eine Dialogbox, so entspricht jedem Objekt eine Objektnummer, die man durch Analyse der Baumstruktur herausbekommen könnte. Allerdings ist das eine sehr mühsame Arbeit, die zunichte gemacht wird, wenn man an der Dialogbox etwas ändert. Dann kann es nämlich passieren, daß sich die Objektreihenfolge ändert. Nun haben sich die Autoren des RCS zu diesem Zweck eine Lösung einfallen lassen: Man kann im RCS jedem Objekt einen Namen geben! Gibt man dann die eben erwähnte Datei mit dem RCS aus, so erzeugt es eine Definitionsliste, die zum entsprechenden Namen die entsprechende Objektnummer zuordnet. Diese Definitionen (man könnte sie auch Konstanten nennen) werden dann mit in das Programm aufgenommen (#include <name.h>) und können statt der mühsam herausgesuchten Objektnummer verwendet werden. Diejenigen, die bisher ihre Objektnummern herausgesucht haben, werden schnell erkennen, welch große Hilfe damit gegeben ist.

Das Problem ist nun, daß bisher (GFA-) Basic-Anwender mit dieser Datei überhaupt nichts anfangen konnten, da sie explizit auf ’C’ zugeschnitten ist. Dieses Problem hört ab sofort auf! Wir liefern in dieser Ausgabe ein Programm, das diese Datei so umformt, daß sie in GFA-BASIC benutzt werden kann. Sicher muß man zugeben, daß es in GFA-BASIC keine Konstanten gibt - in ’C’ verbrauchen diese keinen Speicherplatz - so daß für jede Objektnummer ein Speicherplatz für eine Variable 'verschwendet’ wird. Außerdem darf man nicht alle Namen verwenden, da bestimmte Namen mit den Basic-Befehlswörtern kollidieren würden. Für die Namen der Objekte gelten also die gleichen Bedingungen wie für Variablennamen, da aus den Objektnamen Variablen gemacht werden. Trotzdem überwiegt die Arbeitserleichterung gegenüber diesen kleinen Einschränkungen. Sollten Sie dennoch einmal einen Namen verwendet haben, der mit den Befehlswörtern von GFA-Basic kollidiert, so zeigt es diese Zeile nach dem Laden mit dem Symbol ’ = >’ an.

Nun schreiten wir zur Tat: Zuerst schauen Sie sich bitte einmal die Original-’C’-Datei an und vergleichen sie dann mit der Basic-Datei.

Ausgabe des RCS als ’C’-Source-Code:

#define	DATUM 0		/*	TREE */
#define	MASKE 1		/*	TREE */
#define	PAK2 1		/* OBJECT in TREE #1 */
#define	NACH 7		/* OBJECT in TREE #1 */
#define	MASKBRK	11	/* OBJECT in TREE #1 */
#define MASKOK 8	/* OBJECT in TREE #1 */
#define	PAKETNR	2	/* OBJECT in TREE #1 */
#define	UNAME 3		/* OBJECT in TREE #1 */
#define	STRASSE	4	/* OBJECT in TREE #1 */
#define	PLZ 5		/* OBJECT in TREE #1 */
#define	ORT 6		/* OBJECT in TREE #1 */
#define GEWICHT 9	/* OBJECT in TREE #1 */
#define	NNAHME	10	/* OBJECT in TREE #1 */
#define DATE 1		/* OBJECT in TREE #0 */

Umgewandelte Datei, benutzbar für GFA-Basic:

DATUM=0		! TREE
MASKE=1		! TREE
PAK2=1		! OBJECT in TREE #1
NACH=7		! OBJECT in TREE #1
MASKBRK=11	! OBJECT in TREE #1
MASKOK=8	! OBJECT in TREE #1
PAKETNR=2	! OBJECT in TREE #1
UNAME=3		! OBJECT in TREE #1
STRASSE=4	! OBJECT in TREE #1
PLZ=5		! OBJECT in TREE #1
ORT=6		! OBJECT in TREE #1
GEWICHT=9	! OBJECT in TREE *1
NNAHME=10	! OBJECT in TREE #1
DATE=1		! OBJECT in TREE #0
PAKET=2 	! OBJECT in TREE #0

Sie sehen also, daß man die ’#define’-Anweisung entfernen, ein „ = “ einzigen und die Bemerkungsanweisung ’/*’ gegen die Basic-Anweisung ’!’ austauschen muß. Wenn Sie das Programm starten, dann wählen Sie die ’.H’-Datei an. Daraufhin generiert das Programm eine ’.LST’-Datei, die Sie dann zu Ihrem Programm an den Anfang mit MERGE laden können. Dadurch werden die Variablen mit den entsprechenden Werten versehen. Ab sofort können Sie nun ihre Objekte unter diesem Namen ansprechen. Denken Sie aber daran, daß Sie bei einer Änderung der Resource-Datei die ’.H’-Datei mit dem untenstehenden Programm umändern und dann wieder in ihrem Programm austauschen.

Nun wünsche ich Ihnen viel Erfolg beim weiteren Programmieren von Dialogboxen und Menüs, das sicherlich ab sofort viel schneller gehen wird!

C-Tips zum Windowhandling

Jetzt komme ich zu ein paar Routinen, die oft benutzt werden, auch wenn sie keine originalen GEM-Routinen sind. Sie werden aber so häufig gebraucht, daß sie bei manchen Compilern schon vorhanden sind. Für diejenigen, die nicht wissen, wie diese Routinen funktionieren und für welche Anwendung sie zu gebrauchen sind, möchte ich nun näher darauf eingehen. Sie sind besonders bei der Handhabung von Fenstern sehr hilfreich.

Die meistverwendete Routine ist rc_intersect (siehe Listing 3):

int rc_intersect(p1, p2) /* Testet Überlappungen von Ausschnitten */ 
GRECT *p1, *p2;				/* Übergabe der Koordinaten */
{
	int tx, ty, tw, th;		/* Zwischenwerte der Koordinaten */

/* Ermittele die kleinere der beiden X-Koordinaten, die den rechten Rand der Ausschnitte kennzeichnen */
	tw = min(p2->g_x + p2->g_w, p1->g_x + p1->g_w);

/* Ermittele die kleinere der beiden Y-Koordinaten, die den unteren Rand der Ausschnitte kennzeichnen */
	th = min(p2->g_y + p2->g_h, p1->g_y + p1->g_h);

/* Ermittele die größere der beiden X-Kordlnaten, die den linken Rand der Auschnltte kennzeichnen */ 
	tx = max(p2->g_x, p1->g_x);

/* Ermittele die größere der beiden Y-Kordinaten, die den unteren Rand der Auschnitte kennzeichnen */ 
	ty = max(p2->g_y, p1->g_y);

/* Schreibe den Überlappungsausschnitt in die erste GRECT-Struktur */
	p2->g_x = tx; 
	p2->g_y = ty;
	p2->g_w = tw - tx; /* Errechne Breite der Überlappung */ 
	p2->g_h = th - ty; /* Errechne Höhe der Überlappung */

/* War überhaupt eine Überlappung ? */ 
	return( (tw > tx) && (th > ty) );
}

Listing 3

Zum besseren Verständnis des Folgenden schauen Sie sich bitte Bild 1 an. Auf ihm sind drei Möglichkeiten angegeben, wie zum Beispiel zwei Fenster auf dem Desktop liegen können. Stellen Sie sich vor, Sie haben zwei Rechtecke (zum Beispiel zwei Fenster), bei denen Sie feststellen möchten, ob Sie sich überlappen. Eine Anwendung könnte sein, daß Sie in diesem Fall nur die Schnittfläche dieser beiden Flächen neu zeichnen wollen.

In der weitverbreiteten do_redraw()-Routine wird mit rc_intersect zum Beispiel festgestellt, ob der zu zeichnende Fensterausschnitt mit den gegebenen Clipping-Koordinaten kollidiert; ist dies der Fall, so werden vor dem Zeichnen des Fensterinhaltes die Clipping-Koordinaten gesetzt, rc_intersect() erwartet also zwei Koodinatenpaare, die die beiden Rechtecke definieren. Diese Rechteckdefinition ist durch die Struktur GRECT (siehe Listing 4) gegeben:

Ein Beispiel wird diese nützliche Routine sicher verdeutlichen:

typedef struct	grect
{	
	int	g_x;		/* linke obere Ecke des Rechtecks */
	int	g_y;		/* linke obere Ecke des Rechtecks */
	int	g_w;		/* Breite des Rechtecks */
	int	g_h;		/* Höhe des Rechtecks */
} GRECT	

/* Ein Beispiel wird diese nützliche Routine sicher verdeutlichen: */

main()	
{
	GRECT re1, re2;

	re1.g_x=20;	/* Rechteck 1 */
	re1.g_y=20; 
	re1.g_w=250; 
	re1.g_h=100;

	re2.g_x=100;	/*	Rechteck 2 */
	re2.g_y=80; 
	re2.g_w=250; 
	re2.g_h=100;

	if (rc_intersect(&re1, &re2)	/* überprüfen und Berechnen */
	{								/* des Bereichs */
		printf("Die beiden Rechtecke überlappen sich.\n"); 
		printf("Die Daten des Schnittbereichs sind die folgenden:\n"); 
		printf("x: %d\n y: %d\n Breite: %d\n Höhe: %d\n", re1.g_x,re1.g_y,re1.g_w,re1.g_h);
	}
	else
		printf("Die Rechtecke überschneiden sich nicht !\n"); 
	gemdos(7); 						/* AUf Tastendruck warten */

Listing 4

In dem Beispiel aus Listing 4 werden die beiden Rechtecke re1 und re2 verglichen. Dabei rechnet rc_intersect() die eventuelle Überschneidung aus und gibt dann FALSE oder TRUE zurück, je nachdem, ob eine Überschneidung vorlag oder nicht. Man beachte dabei, daß sich die Koordinaten der Schnittfläche nun in der Variable RE1 befinden! Das bedeutet natürlich, daß die alten Koordinaten von RE1 verloren sind. Mit den Schnittflächenkoordinaten kann man nun weiterrechnen.

Die nächste, ebenfalls für Fenster hilfreiche Routine ist rc_constrain().

Stellen Sie sich vor, der Benutzer verschiebt ein Fenster so, daß ein Teil des Fensters außerhalb des Desktops liegt. Sie wollen aber, daß ihr Programm das Fenster automatisch wieder so zurückschiebt, daß es sich wieder vollkommen auf der Desktopoberfläche befindet. Dieses Neuberechnen und Verschieben nimmt Ihnen rc_constrain() ab. Zuerst übergeben Sie die Koordinaten, in denen sich Ihr Bereich befinden darf. Diese Koordinaten befinden sich dann in der GRECT-Struktur-Variable pc. Danach übergeben Sie ihre Fensterkoordinaten. Liegen sie außerhalb der zuerst übergebenen Koordinaten, so 'drängt’ rc_constrain() die Koordinaten Ihres Fensters in den Umgebungsbereich. Beispiel: Es sei Ihr Aufruf rc_constrain(&re1,&re2) und re2 enthält die Daten des Fensters, dann werden die Koordinatenänderungen direkt in re2 durchgeführt. Das heißt, nach dem Aufruf sind die Koordinaten des Fensters so geändert, daß es nun im vorgeschriebenen Bereich liegt. Sie brauchen es nur noch neu zu zeichnen.

Oft (zum Beispiel in VDI) werden die Werte von Rechteckausschnitten nicht in der GRECT-Struktur benötigt, sondern in Feldern. Diese Umwandlung übernimmt die Funktion grect_to_array(). Ein Programmausschnitt könnte dann wie in Listing 6 ausschauen:

GRECT re1,re2;

int feld[4];

	/* Irgendwelche Operationen mit den Rechteckbereichen wie z.B.: */ 
	
	rc_intersect(&re1,&re2);

	grect_to_array(&re1,feld); /* Umwandlung der GRECT-Struktur */
								/* in gegenüberliegende Rechteck- */
								/* koordinaten */

/* Die Funktion selbst sieht dann folgendermaßen aus: */

grect_to_array(area, array) /* Schreibe Strukturwerte in Feld */ 
GRECT *rechteck; 
int *array;
{
	*array++ = rechteck->g_x;
	*array++ = rechteck->g_y:
	*array++ = rechteck->g_x + rechteck->g_w - 1;
	*array = rechteck->g_y + rechteck->g_h - 1;
}

Listing 6

Und nun, zum Schluß unseres Streifzuges durch die nützlichen Routinen für die Fensterverwaltung, möchte ich auf die Prozedur align() eingehen. Häufig will man aus Geschwindigkeitsgründen erreichen, daß bestimmte Bereiche auf einer durch 8 (Bytegrenze) oder 16 (Wortgrenze) teilbaren Koordinate liegt. Dadurch kann man erhebliche Geschwindigkeitsverbesserungen bei Raster-Kopierungen erzwingen. Diese Umrechnung führt align() durch. Als erstes übergibt man die umzurechnende Koordinate, als zweites wird die Zahl übergeben, auf die die Koordinate umgerechnet werden soll. Soll beispielsweise auf Wortgrenze umgerechnet werden, so übergibt man die Zahl 16.

Damit wären wir für diesen Monat am Ende angelangt. Ich möchte wieder alle Leser auffordern, uns Ihre Softwareentwicklungsprobleme mitzuteilen. Ein Leser stellte zum Beispiel die Frage, ob wir nicht einmal rc_intersect veröffentlichen könnten. Und natürlich sind wir immer daran interessiert neue Tips und Tricks zu erfahren, um sie allen Lesern zugänglich zu machen.

' ***********************************
' **                               **
' **    '.H'-Datei - Umwandlung    **
' **                               **
' **       HE      Stefan  Höhn    **
' **                               **
' ***********************************
'
Fileselect "D:\*.H","",A$	! Datei mit '.H * auswählen lassen
If A$="" Then	! ABBRUCH ist angewählt worden. Ende
	End			! des Programms.
Endif
A2$=Left$(A$,Len(A$)-1)+"LST"	! Neuen Extender '.LST' austauschen
Fd=Gemdos(&H3D,L:Varptr(A$),0)	! gemdos(*3d): Fopen: Datei öffnen
L=Gemdos(&H42,L:0,Fd,2)			! gemdos(*42): Fseek: Zeiger an das Ende
'								! der Datei setzen. Ergebnis = L&nge
A=Gemdos(&H3E,Fd)				! Datei wieder schließen.
Open "u",#1,A$					! Datei zum Lesen öffnen
Open "o",#2,A2$					! Datei zum Schreiben öffnen
Zaehl=0							! Zähler löschen
I=0								! Zeilenindex löschen
While Zaehl<>L-1				! Bis zum Ende der Datei von Disk lesen
	Z=1							! Zeilenindex auf Anfang setzen
	Input #1,B$					! Zeile einlesen (bis CRLF)
	Zaehl=Zaehl+Len(B$)+2 !+CR+LF	! Zähler um Länge der Zeile plus 2 Zeichen
	'							! für Carriage Return und Linefeed erhöhen
	X$=Right$(B$,Len(B$)-8)	! '#define' abschneiden
	While Mid$(X$,Z-1,1)<>"="	!
		If Mid$(X$,Z,1)=" " Then	! bis Space suchen
			Mid$(X$,Z,1)="=""	! mit '=' ersetzen
		Endif
		Inc	Z					! Zähler erhöhen
	Wend						! Wenn Space mit ersetzt, dann Abbruch
	While Mid$(X$,Z-1,2<>"  "	! Suchen, bis 2 Spaces gefunden
		Inc Z						! Das bedeutet, daß an dieser Stelle die
	Wend						! Bemerkungen folgen.
	Mid$(X$,Z,2) = " !"			! Basic-Rem-Abkürzung ' einfügen
	While Z<Len(B$)				! Bis zum Ende der Zelle suchen
		If Mid$(X$,Z,1)="*" Or Mid$(X$,Z,1)="/" Or Mid$(X$,Z,1)=Chr$(9) Then 
			Mid$(X$,Z,1)=" "	! Wenn '/*' oder '*/' gefunden, dann
		Endif					! entferne die Zeichen
		Inc Z
	Wend
	Print X$					! Auf den Bildschirm ausgeben
	Print #2,X$					! In die Datei ausgeben
Wend
Close							! Alle Dateien schließen

Listing 2

rc_constrain(pc, pt) /* Box 'pt’ in Box 'pc' drängen */
GRECT	*pc;
GRECT	*pt;
{
	if (pt->g_x < pc->g_x)	/* Ist (X von pt) < (Y von pc) ? */
		pt->g_x = pc->g_x;	/* dann nimm neue X-Koordlnate an */
	if (pt->g_y < pc->g_y)	/* Ist (Y von pt) < <Y von pc) ? */
		pt->g_y = pc->g_y;	/* dann nimm neue Y-Koordinate an */

	/* Wenn die rechte äußere Kante von pt den Bereich von pc
	verläßt, dann ziehe die X-Koordinate soweit nach links, daß diese Kante wieder im Bereich von pc liegt */ 
	if ((pt->g_x + pt-)g_w)	> (pc->g_x	+ pc->g_w))
		pt->g_x = (pc->g_x + pc->g_w) -	pt->g_w;

	/* Wenn die untere äußere Kante von pt den Bereich von pc verläßt, dann ziehe die X-Koordinate soweit nach oben, daß diese Kante wieder im Bereich von pc liegt */ 
	if ((pt->g_y + pt->g_h) > (pc->g_y + pc->g_h))
		pt->g_y = (pc->g_y + pc->g_h) - pt->g_h;
}

/* rc_equal testet, ob zwei Bereiche genau gleich groß sind und */ 
/* gibt dann entsprechend FALSE oder TRUE zurück: */

int rc_equal(p1, p2) /* Ist Rechteck Pi gleich Rechteck P2 ? */ 
GRECT *pl, *p2;
{
	/* Wenn X- und Y-Koordinaten sowie die Breite und die Höhe
	übereinstimmen, dann gebe TRUE, sonst gebe FALSE zurück */
	if ((p1->g_x != p2->g_x) || (p1->g_y != p2->g_y) || 
		(p1->g_w != p2->g_w) || (p1->g_h != p2->g_h)) 
			return(FALSE); 
	return(TRUE);
}

Listing 5

int align(x,n) /* Umrechnung einer Koordinate auf die nächste */ 
				/* durch n teilbare Stelle */ 
int x, n; /* Für Wortbreitenausrichtung bitte n=16 verwenden*/
{
	x += (n >> 2) - 1;	/* Runden und */
	x = n * (x / n);	/* Rest entfernen */
	return (x);			/* Gebe Wert zurück */
}

Listing 7



Aus: ST-Computer 05 / 1987, Seite 47

Links

Copyright-Bestimmungen: siehe Über diese Seite