Für GEM-Fenster gibt es ja mittlerweile einen ganzen Haufen Zusatzroutinen, die das Handling selbiger vereinfachen sollen. Nun, damit soll jetzt nicht Schluß sein, und deshalb kommt hier auch schon die nächste Routine, die darauf wartet, in den Fenster-Händler eingebaut zu werden.
Die Problemstellung zu Anfang war folgende: Es sollten I Textausgaben auf den Bildschirm getätigt werden, die nur in Fenstern erscheinen sollten (so sollte es übrigens gängige Praxis sein). Genauer gesagt werden die Ausgaben in Fenster nur in den sogenannten Arbeitsbereich getätigt. Dieser Arbeitsbereich ist die Fläche im Fenster ohne die Slider, die Titelzeile usw.
Damit nicht am rechten Rand dieses Bereichs bloß ein halbes Zeichen erscheint, mußte dafür gesorgt werden, daß die Breite des Fensterarbeitsbereichs genau ein Vielfaches der Breite eines Zeichens ist. Selbiges gilt analog auch für die Flöhe des Arbeitsbereichs. Sie sollte ein Vielfaches der Zeilenhöhe sein, damit keine halbe Zeile im Fenster erscheint. Doch allein mit der Modifizierung der Breite und Flöhe des Arbeitsbereichs ist es nicht getan.
Nun sollten diese Textausgaben - da ein Fenster ja bekannterweise nach rechts und unten aus dem Bildschirm hinausgeschoben werden kann - zusätzlich bündig zum rechten und unteren Bildschirmrand sein, das heißt, es sollte ebenfalls kein halbes Zeichen am Bildschirmrand zu sehen sein, falls das Fenster aus dem Bildschirm hinausgeschoben wurde. Um dies zu erreichen, mußten die Fenster passend plaziert werden. Der Arbeitsbereich dieses Fensters mußte so positioniert werden, daß die vom Fenster sichtbare Breite des Arbeitsbereichs auf dem Bildschirm ebenfalls durch die Zeichenbreite ohne Rest teilbar ist. Selbiges mußte analog mit der Flöhe des vom Fenster sichtbaren Arbeitsbereichs geschehen. Diese sichtbare Flöhe sollte durch die Zeilenhöhe ohne Rest teilbar sein. Flierzu mußte die Position (x-/y-Koordinate) des Arbeitsbereichs korrekt gesetzt werden.
Bei RectToElement handelt es sich nun nicht direkt um eine Fensterzusatzfunktion, sondern um eine sehr allgemeine Funktion, die sich lediglich mit der Bearbeitung eines Rechtecks beschäftigt und deshalb auch nicht bloß im Zusammenhang mit Fenstern Verwendung finden kann, doch gerade hier eine wichtige Funktion übernimmt. Nun, der geneigte Leser wird sich jetzt denken, was kann sie denn nun, die Funktion? Diesem Leser möchte ich eine Antwort nicht schuldig bleiben. RectToElement ist eine reine Rechenfunktion. Sie modifiziert das Rechteck rect folgendermaßen: Die Breite wird dahingehend geändert, daß sie durch den Parameter welm ohne Rest teilbar ist. Zusätzlich wird welm noch dazu benutzt, die x-Koordinate des Rechtecks bezüglich des rechten Bildschirmrandes zu setzen. Die Differenz zwischen x-Koordinate und dem rechten Bildschirmrand soll ebenfalls ohne Rest durch welm teilbar sein. Analog wird mit der Höhe verfahren. Sie soll später durch heim ohne Rest teilbar sein. Die y-Koordinate wird so gesetzt, daß die Differenz von y-Koordinate und unterem Bildschirmrand ebenfalls ohne Rest durch heim teilbar ist. Damit hätte man das Rechteck, das den Arbeitsbereich eines Fensters beschreiben könnte (siehe Anfangsbeispiel) auf die Koordinaten gebracht, mit denen die oben gesetzten Anforderungen erfüllt werden.
Der Aufruf von RectToElement ist eine kinderleichte Angelegenheit. Die ersten drei Parameter wurden bereits erwähnt, rect beschreibt mit seinen vier Strukturelementen g_x, g_y, g_w und g_h das zu modifizierende Rechteck. Der Parameter welm bezeichnet die Breite eines Elements, das bei der Festlegung der Breite und der x-Koordinate des Rechtecks einfließt, und heim beschreibt die Elementhöhe, die bei der Berechnung der Rechteckhöhe und der y-Koordinate des Rechtecks Verwendung findet. Es bleibt noch der vierte Parameter.
Das sogenannte Flag ist ein etwas komplexerer Parameter, da er verschiedene Informationen gleichzeitig beinhalten kann, flag ist dazu gedacht, der Funktion eines oder mehrere der im Header definierten Makros zu übergeben. Sollte flag gleich NOSET sein, macht RectToElement überhaupt nichts, sondern wird unverrichteter Dinge beendet. Die Makros DOSETX, DOSETY, DOSETW und DOSETH haben folgende Bedeutung. Ist flag gleich DOSETX, wird nur die x-Koordinate des Rechtecks bearbeitet, ist flag gleich DOSETW, nur die Breite. Analoges gilt für die anderen beiden Makros. DOSETXYWH ist bloß eine Vereinfachung für die Übergabe aller vier Parameter, also etwas für die Schreibfaulen, trägt aber auch zur Übersichtlichkeit bei. In diesem Fall werden alle vier Komponenten der Struktur, die das Rechteck beschreiben, bearbeitet. Mit den letzten beiden Makros hat es noch eine besondere Bewandtnis. Bis jetzt wurde das Rechteck immer so verändert, daß z.B. bei der Änderung der Breite immerdie Breite gewählt wurde, die näher am Ausgangswert lag. So konnte es passieren, daß das Rechteck mal breiter wurde als vorher oder auch mal schmaler, je nachdem wie die Breite vorher war. Wird flag jetzt zusätzlich mit TOBIGGER oder verknüpft, werden das Rechteck bzw. die zu bearbeitenden Komponenten so modifiziert, daß das Rechteck später mindestens genauso groß ist wie vorher oder größere Ausmaße besitzt. Man könnte flag dafür z.B. mit DOSETXYWHITOBIGGER angeben. Verknüpft man flag mit TOSMALLER, werden die angegebenen Strukturelemente so verändert, daß das Rechteck genauso groß oder kleiner ist als vorher. Wird keines der beiden Makros TOBIGGER oder TOSMALLER verwendet, wird wieder der am nächsten liegende Wert für die Berechnung der Strukturelemente benutzt.
Die Funktionsweise ist im Prinzip völlig banal. Ich beschreibe hier exemplarisch die Berechnung der Breite des übergebenen Rechtecks, also der Komponente g_w. Zuerst wird überprüft, ob überhaupt etwas geändert werden muß. Dies geschieht über die Modulo-Rechnung, indem g_w Modulo welm gerechnet wird. Ist das Ergebnis ungleich Null, muß etwas geändert werden, da in diesem Fall g_w nicht ohne Rest durch welm teilbar ist. Nun wird anhand des Parameters flag oder aber im Default-Fall anhand der Differenz zum näheren Wunschwert differenziert, ob die Breite verkleinert oder vergrößert wird. Analog wird die Komponente g_h bearbeitet.
Die Berechnung der x- und y-Koordinate gestaltet sich etwas aufwendiger, da sie nicht relativ zu Null, sondern zum rechten bzw. unteren Bildschirmrand gesetzt werden muß. Zuerst wird (exemplarisch für g_x) die Differenz zwischen g_x und dem rechten Bildschirmrand berechnet. Auf diesen Wert werden dieselben Rechenoperationen wie oben beschrieben angewendet, mit einem Unterschied. Wurde das Rechteck bei der Berechnung der Breite und Höhe vergrößert, indem das entsprechende Strukturelement einen höheren Wert erhielt, wird das Rechteck bei der Berechnung der x-und y-Koordinate vergrößert, indem das passende Strukturelement verkleinert wird. Nun wird aus der Differenz wieder die x-Koordinate berechnet und fertig. Dies gilt analog für die y-Koordinate. Nur eines noch: Der Verschub der oberen linken Ecke dieses Rechtecks, also der x- und y-Koordinate, wird sofort von der Breite und Höhe des Rechtecks subtrahiert bzw. dazuaddiert, je nachdem, ob das Rechteck verkleinert oder vergrößert wird. Diese Rechenaktion wird nur durchgeführt, wenn die Höhe bzw. die Breite auch wirklich bearbeitet werden sollen. Der Sinn besteht darin, daß die rechte untere Ecke des Rechtecks (abhängig von Breite und Höhe) trotz Änderung der x- und y-Koordinate unberührt bleibt und erst durch die Betrachtung der Breite und Höhe geändert wird.
Als Beispiel zur Anwendung von RectToElement sei hier auf Listing 3 verwiesen. Es stellt eine simple Fensterverwaltung dar, die bei der Bewegung und Veränderung der Größe des Fensters Gebrauch der Routine RectToElement macht. Sie stellt das Problem des Ausgangspunktes dieses Artikels dar (allerdings nicht mehr als Problem, sondern als dessen Lösung). Die Elementgröße ist dort auf 40 in der Höhe sowie in der Breite festgelegt. So, das war’s. Allen, die sich die Mühe machen, die Funktion abzutippen, wünsche ich viel Spaß. Das Demoprogramm habe ich extra kurz gehalten, damit das Abtippen desselben nicht zu sehr in Arbeit ausartet.
/* RECT.C
(c) 1992 MAXON Computer
*/
#include <aes.h>
#include "rect.h"
void RectToElement( GRECT *rect, int welm, int helm, int flag )
{
int x, y,
vx, vy,
sw, sh,
dx, dy, dw, dh;
/* Breite und Höhe ermitteln */
wind_get( 0, WF_WORKXYWH,
&dx, &dy, &dw, &dh );
sw = dx + dw;
sh = dy + dh;
/* Wenn die X-Koordinate gesetzt werden soll... */
if ( flag & DOSETX )
{
/* relativ zur rechten unteren Bilschirmecke */
x = sw - rect->g_x;
/* Muß überhaupt was geändert werden? */
if( x % welm )
{
/* in die beiden verschiedenen Richtungen snappen */
if( (flag & TOSMALLER) ||
(x % welm < welm/2) )
x -= x % welm;
if< (flag & TOBIGGER) ||
(x % welm >= welm/2) )
x += welm - (x % welm);
/* Verschub ermitteln */
vx = rect->g_x;
rect->g_x = sw - x;
vx = -(vx - rect->g_x);
/* Verschub evtl. von der Breite abziehen */
if( flag & DOSETW )
rect->g_w -= vx;
}
}
/* Wenn die Y-Koordinate gesetzt werden soll... */
if( flag & DOSETY )
{
/* relativ zur rechten unteren Bilschirmecke */
y = sh - rect->g_y;
/* Muß überhaupt was geändert werden? */
if( y % helm )
{
/* In die beiden verschiedenen Richtungen snappen */
if( (flag & TOSMALLER) ||
(y % helm < helm/2) )
y -= y % helm;
if( (flag & TOBIGGER) ||
(y % helm >= helm/2) )
y += helm - (y % helm);
/* Verschub ermitteln */
vy = rect->g_y;
rect->g_y = sh - y;
vy = -(vy - rect->g_y) ;
/* Verschub evtl, von der Höhe abziehen */
if( flag & DOSETH )
rect->g_h -= vy;
}
}
/* Wenn die Breite gesetzt werden soll... */
if( flag & DOSETW )
/* Muß überhaupt was geändert werden? */
if ( rect->g_w % welm )
{
/* In die beiden verschiedenen Richtungen snappen */
if( (flag & TOSMALLER) ||
(rect->g_w % welm < welm/2) )
rect->g_w -= rect->g_w % welm;
if( (flag & TOBIGGER) ||
(rect->g_w % welm >= welm/2) )
rect->g_w += welm - (rect->g_w % welm);
}
/* Wenn die Höhe gesetzt werden soll... */
if( flag & DOSETH )
/* Muß überhaupt was geändert werden? */
if ( rect->g_h % helm )
{
/* In die beiden verschiedenen Richtungen snappen */
if( (flag & TOSMALLER) ||
(rect->g_h % helm < helm/2) )
rect->g_h -= rect->g_h % helm;
if( (flag & TOBIGGER) ||
(rect->g h % helm >= helm/2) )
rect->g_h += helm - (rect->g_h % helm);
}
}
/* RECT.H */
#ifndef ___RECT
# define __RECT
# define NOSET 0x00
# define DOSETX 0x01
# define DOSETY 0x02
# define DOSETW 0x04
# define DOSETH 0x08
# define DOSETXYWH (DOSETX|DOSETY|DOSETW|DOSETH)
# define TOBIGGER 0x10
# define TOSMALLER 0x20
# include <aes.h>
void RectToElement( GRECT *rect,
int welm,
int helm,
int flag );
#endif
/* MAIN.C
(c) 1992 MAXON Computer
*/
#include <aes.h>
#include <vdi.h>
#include "rect.h"
void ResizeWindow( int handle, int x, int y );
void MoveWindow( int handle, int x, int y );
int RedrawWindow( int handle );
int crkind = NAME|CLOSER|MOVER|SIZER, vhandle;
int main( void )
{
int handle,
finish = 0, dummy, i,
defx, defy, defw, defh,
msg[8],
work_in[11],
work_out[57];
/* Bei den AES anmelden */
if( appl_init() < 0 )
return( -1 );
/* VDI-Workstation anmelden */
vhandle = graf_handle( &dummy, &dummy, &dummy, &dummy ) ;
for( i = 0; i < 10; work_in[i++] = 1 );
work_in[10] = 2;
v_opnvwk( work_in, &vhandle, work_out );
if( !vhandle )
{
appl_exit();
return( -1 );
}
vswr_mode( vhandle, MD_REPLACE );
vsf_interior( vhandle, FIS_HOLLOW );
vsl_width( vhandle, 1 );
/* Default Fenstergröße ermitteln */
wind_get( 0, WF_WORKXYWH, &defx, &defy, &defw, &defh );
/* Fenster erzeugen */
handle = wind_create( crkind, defx, defy, defw, defh );
if( handle < 0 )
return( -1 );
/* Name setzen */
wind_set( handle, WF_NAME, " RectToElement-Demo " );
/* Fenster öffnen */
if( !wind_open( handle, defx, defy, defw, defh ) )
return( -1 );
graf_mouse( ARROW, 0L );
/* Eventschleife */
while( !finish )
{
evnt_mesag( msg );
switch( msg[0] )
{
case WM_CLOSED :
finish = 1;
break;
case WM_SIZED :
ResizeWindow( msg[3], msg[5], msg[7] );
break;
case WM_MOVED :
MoveWindow( msg[3], msg[4], msg[5] );
break;
case WM_REDRAW :
RedrawWindow( msg[3] );
break;
}
}
v_clsvwk( vhandle );
appl_exit();
return{ 0 );
}
void ResizeWindow( int handle, int w, int h )
{
GRECT wind;
wind_get( handle, WF_CURRXYWH,
&wind.g_x, &wind.g_y,
&wind.g_w, Swind.g_h );
wind.g_w = w;
wind.g_h = h;
wind_calc( WC_WORK, crkind,
wind.g_x, wind.g_y,
wind.g_w, wind.g_h,
&wind.g_x, &wind.g_y,
&wind.g_w, &wind.g_h );
RectToElement( &wind, 40, 40, DOSETW|DOSETH );
wind_calc( WC_BORDER, crkind,
wind.g_x, wind.g_y,
wind.g_w, wind.g_h,
&wind.g_x, &wind.g_y,
&wind.g_w, &wind.g_h );
wind_set( handle, WF_CURRXYWH,
wind.g_x, wind.g_y,
wind.g w, wind.g_h );
RedrawWindow ( handle ) ;
}
void MoveWindowi int handle, int x, int y )
{
GRECT wind;
wind_get( handle, WF_CURRXYWH,
wind.g_x, wind.g_y,
wind.g_w, wind.g_h );
wind.g_x = x;
wind.g_y = y;
wind_calc( WC_WORK, crkind,
wind.g_x, wind.g_y,
wind.g_w, wind.g_h,
&wind.g_x, &wind.g_y,
&wind.g_w, &wind.g_h );
RectToElement( &wind, 40, 40, DOSETX|DOSETY );
wind_calc( WC_BORDER, crkind,
wind.g_x, wind.g_y,
wind.g_w, wind.g_h,
&wind.g_x, &wind.g_y,
&wind.g_w, &wind.g_h );
wind_set( handle, WF_CURRXYWH,
wind.g_x, wind.g_y,
wind.g w, wind.g_h );
}
int RedrawWindow( int handle )
{
int pxy[4];
GRECT wind;
graf_mouse( M_OFF, 0L );
wind_update( BEG_UPDATE );
if( wind_get( handle, WF_FIRSTXYWH,
&wind.g_x, &wind.g_y,
&wind.g_w, &wind.g_h ) )
while( wind.g_w && wind.g_h )
{
pxy[0] = wind.g_x;
pxy[1] = wind.g_y;
pxy[2] = wind.g_x + wind.g_w - 1;
pxy[3] = wind.g_y + wind.g_h - 1;
vr_recfl( vhandle, pxy );
v_pline( vhandle, 2, pxy );
pxy[0] = wind.g_x + wind.g_w - 1;
pxy[2] = wind.g_x;
v_pline( vhandle, 2, pxy );
/* nächstes Rechteck */
if( !wind_get( handle, WF_NEXTXYWH,
&wind.g_x, &wind.g_y,
&wind.g_w, &wind.g_h ) )
wind.g_w = wind.g_h = 0 ;
}
graf_mouse( M_ON, 0L );
wind_update ( END_UPDATE ) ;
return( 1 );
}
RECTDEMO.APP
=
PCSTART.O
MAIN.C
RECT.C
PCSTDLIB.DIB
PCEXTDIB.LIB
PCTOSLIB.LIB