Komfort in Dialogboxen mit Modula-2

Die normalen Editierroutinen für Dialogboxen bieten nicht viele Möglichkeiten. Bei vielen Anwendungen könnte man speziellere Routinen benötigen. Wie wär’s z.B. mit Fett- oder Kursivschrift in Dialogboxen?

Auslöser dieser Routinen war ein kleines Programm in Modula-2, das einen in einer Dialogbox eingegebenen Text sofort an den Drucker schickt; ihn also als Schreibmaschine benutzt. Das Problem war die Eingabe verschiedener Schriftarten, die natürlich benutzt werden sollten. Die normale GEM-Routine ,FormDo“ bietet solche Möglichkeiten nicht, also mußte eine entsprechende Prozedur geschrieben werden. Das Ergebnis zeigt Bild 1.

Die Routine ,ObjectEditExtended“ bietet diese Editiermöglichkeiten. Die vorhandene Tastenbelegung ist in Bild 2 aufgelistet. Das Programm selber ist in Modula-2 geschrieben und in Listing 1 abgedruckt.

Benutzen ...

Das Modul bietet die eigentliche Routine sowie Konstanten für die Schriftarten. Sie wird aufgerufen mit

ObjectEditExtended (Tree, Object, Type-length, typeline, typestyle);

,Tree‘ enthält die Adresse des Dialogbaumes, in dem editiert werden soll. Die Anzeige der Eingabe sollte in einem normalen GraphicBox-Objekt geschehen, bei dem das ,Outlined‘-Flag gesetzt ist. Sein Index wird in ,Object1 übergeben.

,Typenlength‘ gibt an, wieviele Zeichen maximal eingegeben werden können. Das Ergebnis der Eingabe steht hinterher in ,typeline“, das ein einfaches Feld von Zeichen, also ein String ist. ,typestyle“ gibt dann für jedes Zeichen mit gleichem Feldindex die gewählte Schriftart an. Diese wird entsprechend der VDI-Kodierung dargestellt.

Eine Überprüfung, ob die bereitgestellten Felder von der Länge her ausreichen, findet nicht statt, könnte aber eingebaut werden. Ebenso ist es nicht möglich, einen schon vorhandenen String editieren zu lassen, da ,typline“ immer zunächst mit Blanks aufgefüllt wird (die auch nach der Editierung eventuell hinter der Eingabe noch vorhanden sind). Wer will, kann die Routine also noch verfeinern.

Eine kleine Demonstration der Benutzung von ,ObjectEditExtended“ findet sich in Listing 2. Die dabei verwendete einfache Dialogbox-Resource (siehe Bild 1) wurde mit dem Programm ,RSCMAKER“ aus dem Modula-2 Toolkit umgewandelt.

Pfeil links    Cursor nach links bewegen		
Pfeil rechts   Cursor nach rechts bewegen		
Delete         Zeichen unter dem Crusor löschen und Rest der Zeile nach links ziehen
Backspace      Zeichen links vom Cursor löschen und Rest der Zeile sowie den Cursor nach links ziehen		
Esc            Zeile löschen		
Return         Editieren beenden		
Enter          Editieren beenden		
F1             Unterstreichung ein-/ausschalten		
F2             Kursivschrift ein-/ausschalten		
F3             Hellschrift ein-/ausschalten		

... und dazulernen

Da die einzelnen Programmschritte im Listing kommentiert sind, wollen wir nun noch etwas allgemeiner die Arbeitsweise erläutern.

Die Routine muß einzelne Buchstaben, passsend für das angegebene Objekt, auf den Bildschirm schreiben. Sie muß dazu zunächst die Größe der Buchstaben erfahren, was mit ,GrafHandle‘ geschieht. Beim Aufruf wird dann mit ,ObjectOffset‘ die Lage des Objekts festgestellt. Mit diesen Angaben können wir die Buchstaben passend hineinschreiben.

In ,cyrstyle‘ wird die momentan angewählte Schriftart gehalten. Die Funktionstasten verändern diesen Wert, der dem VDI mit ,SetGraphicTextEffects‘ mitgeteilt wird und dann bei jedem ,Graphic-Text‘-Aufruf verwendet wird.

Den Cursor erzeugen wir, indem ein Rechteck der Größe eines Zeichens mit ,DrawBar‘ gezeichnet wird (,SetCursor“). Durch Setzen des Schreibmodus auf Revers (Parameter 3 bei ,SetWritingMode‘) wird nicht wirklich gezeichnet; vielmehr invertiert der Aufruf genau einen Buchstaben, nämlich den unter dem Cursor. Ein erneuter Aufruf von ,SetCursor“ entfernt ihn wieder.

Interessant ist auch noch die Implementierung der Backspace- und Delete-Taste. Bei ihnen muß jeweils der Bereich rechts vom Cursor um ein Zeichen nach links verschoben werden. Außerdem geschieht die Zeicheneingabe immer im Einfügemodus; dann muß sich dieser Teil nach rechts bewegen.

Dazu dient ,MoveLine‘, das zum Kopieren auf dem Bildschirm ein Rastercopy verwendet. Dafür notwendig ist ein Memory-Form-Definition-Block (MFDB), der. die Bildschirmadresse sowie eine Beschreibung der momentanen Auflösung enthält. Wir besorgen uns diese Informationen im Initialisierungsteil.

Die zu kopierenden Bildschirmbereiche lassen sich leicht mit der Zeichengröße sowie der Objektposition errechnen. Durch die schnelle Routine ,CopyRasterOpaque‘ erhalten wir eine prompte und fließende Reaktion auf dem Bildschirm. Da die Prozedur Werte wie die Zeichengröße vom GEM abfragt, läuft sie übrigens problemlos in Farbe und Schwarz-Weiß.

Die Routine ist neben den oben genannten Verfeinerungen noch um weitere Sondertasten leicht erweiterungsfähig. Man könnte bei Bedarf auch noch die Schriftarten Outlined und Shadowed implementieren.

Listing 1a


DEFINITION MODULE AESExtender;

FROM SYSTEM IMPORT ADDRESS;

CONST Normalstyle	=	0;
	Bold	=	1;
	Light	=	2;
	Italic	=	4;
	Underline	=	8;

PROCEDURE ObjectEditExtended(Tree:ADDRESS; Object. Typelength:INTEGER;
							VAR typeline:ARRAY OF CHAR;
							VAR typestyle;ARRAY OF INTEGER);

END AESExtender.

Listing 1b

IMPLEMENTATION MODULE AESExtender;

(* Robert Tolksdorf - finished 8.4.87 *)

(* optimieren *)

(*$Q+*)

FROM SYSTEM		IMPORT ADR, ADDRESS ;
FROM XBIOS		IMPORT ScreenPhysicalBase, GetResolution,Low, Medium, High ;
FROM GEMAESbase	IMPORT MouseOff, MouseOn, BeginMouseControl, BeginUpdate, EndUpdate, EndMouseControl ; 
FROM AESEvents	IMPORT EventKeyboard ;
FROM AESGraphics	IMPORT	GrafMouse, GratHandle ;
FROM AESObjects	IMPORT	ObjectOffset ;
FROM AESWindows	IMPORT	WindowUpdate ;
FROM GEMVDIbase	IMPORT BigPxyArrayType, PxyArrayType, TextAttrArrayType ;
FROM VDIAttribs	IMPORT SetWritingMode, SetGraphicTextEffects, SetGraphicTextColour, SetFilllnteriorStyle, SetFillColour, SetGraphicTextAlignment ; 
FROM VDIInquires	IMPORT InquireTextAttributes, InquireFillAttributes;
FROM VDIOutputs	IMPORT GraphicText, DrawBar ;
FROM VDIRasters	IMPORT MFDBType, CopyRasterOpaque ;

VAR d:INTEGER;	(* dummy-Variable *)
	MFDB:MFDBType;	(* Memory-Form-Definition-Block *)

PROCEDURE ObjectEditExtended(Tree:ADDRESS; Object, Typelength:INTEGER;
			VAR typeline:ARRAY OF CHAR;
			VAR typestyle:ARRAY OF INTEGER);

VAR vdihandle,linex,liney,curstyle,lasttype,charwidth, charheight:INTEGER; 
	finished:BOOLEAN; 
	fillat,pxy;PxyArrayType; 
	attrib:TextAttrArrayType;

(* ein Rechteck löschen *) 
PROCEDURE ClearRec(x,y,w,h:INTEGER);
BEGIN
	(* in weiß *)
	d:=SetFillColour(vdihandle,0);
	(* Rechteck definieren *)
	pxy[0]:=x; pxy[1]:=y;
	pxy[2]:=x+w-1; pxy[3]:=y+h-1;
	GrafMouse(MouseOff,NIL);
	(* und zeichnen *)
	DrawBar(vdihandle,pxy);
	GrafMouse(MouseOn,NIL); 
	d:=SetFillColour(vdihandle,1);
END ClearRec;

(* ganze Zeile 1I sehen *)
PROCEDURE ClearLine;
BEGIN
	lasttype:=0;
	FOR d:=0 TO Typelength DO 
		typeline[d]:=' '; 
		typestyle[d]:=Normalstyle;
	END;
	ClearRec(linex-8,liney,Typelength*charwidth+7,charheight);
END ClearLine;

(* Zeilenteil nach links oder rechts bewegen *)
PROCEDURE MoveLine (Left: BOOLEAN; Start, end: INTEGER) ;
VAR pxy : BigPxyArrayType;
BEGIN

(* Quell- und Zielbereiche errechnen *) 
pxy[O]:=linex+start*charwidth; 
pxy[1]:=liney;
pxy[2]:=pxy[0]+(end-start+1)*charwidth; 
pxy[3]:=pxy[1]+charheight;

IF Left THEN
	pxy[4]:=pxy[0]-charwidth; 
	pxy[6]:=pxy[2]-charwidth;
ELFE
	pxy[4]:=pxy[0]+charwidth; 
	pxy[6]:=pxy[2]+charwidth;
END;
pxy[5]:=pxy[1];
pxy[7]:=pxy[3];
GrafMouse (MouseOff,NIL) ;
(* Bildschirmbereich kopieren *)
CopyRasterOpaque(vdihandle,3,pxy, ADR(MFDB),ADR(MFDB));
GrafMouse (MouseOn,NIL) ;
(* "Überstehenden" Teil löschen und Zeileninhalt bewegen *)
IF Left THEN
	ClearRec(pxy[2],pxy[1],charwidth,charheight);
	IF Start=0 THEN 
		INC(start) ;
	END;
	FOR d:=start TO end DO
		typeline[d-1]:=typeline[d]; 
		typestyle[d-1]:=typestyle[d] ;
	END ;
ELSE
	ClearRec(pxy[0],pxy[1],charwidth,charheight);
	IF end=Typelength THEN
		DEC (end);
	END;
	FOR d:=end TO start BY -1 DO 
		typeline[d+1]:=typeline[d]; 
		typestyle[d+1]:=typestyle[d];
	END;

END;

END MoveLine;

(* Cursor bei lasttype zeichnen *)

PROCEDURE SetCursor;
BEGIN
	(* XOR-Modus *)
	d:=SetWritingMode(vdihandle,3);
	(* Cursorbereich errechnen *)
	pxy[0]:=linex+lasttype*charwidth-1;
	pxy[1]:=liney;
	pxy[2]:=pxy[0]+charwidth+l;
	pxy[3]:=pxy[1]+charheight;
	GrafMouse(MouseOff,NIL);
	(* und zeichnen *)
	DrawBar(vdihandle,pxy);
	GrafMouse(MouseOn,NIL);
	(* Replace-Modus *) 
	d:=SetWritingMode(vdihandle,1);
END SetCursor;

(* Auf Tasten reagieren *)
PROCEDURE DoTypeWrite;
VAR kret:INTEGER;
	str:ARRAY[0..1] OF CHAR;
BEGIN
	(* auf Eingabe warten *) 
	kret:=EventKeyboard();
	(* Sondertasten behandeln *)
	CASE kret DIV 256 OF 
		1:	(*	ESC	*)
			SetCursor;
			ClearLine; (* Zeile 1! sehen *)
			SetCursor;
		14:	(* BS *)
			IF lasttype>0 THEN 
				SetCursor;
				MoveLine(TRUE,lasttype,Typelength);	(* Bereich verschieben *)
				DEC(lasttype);	(* und Cursor versetzen *)
				SetCursor;
			END;
		28:	(*	RET *)
			finished:=TRUE;	(* beenden *)
		59:	(* F1 *)
			IF ODD(curstyle DIV Bold) THEN (* Fettschrift ein-/ausschalten *) 
				DEC(curstyle,Bold);
			ELSE
				INC(curstyle,Bold);
			END;
			(* Schriftart setzen *)
			d:=SetGraphicTextEffects(vdihandle,curstyle);
		60:	(*	F2 *)
			IF ODD(curstyle DIV Underline) THEN (* Unterstreichen *)
				DEC(curstyle,Underline);	(*	ein-/ausschalten *)
			ELSE
				INC(curstyle,Underline);
			END;
			GrafMouse(MouseOn,NIL);
			(* Cursor weitersetzen *)
			INC(1asttype);
			SetCursor;
		END;
	END;
END DoTypeWrite;

BEGIN
	(* Menüs ur.d Fensteraktionen ausschalten *)
	WindowUpdate(BeginMouseControl);
	WindowUpdate(BeginUpdate);
	(* VDI-Handle holen *)
	vdihandle:=GrafHandle(charwidth,charheight,d,d);
	(* aktuelle Einstellungen merken *)
	InquireTextAttributes(vdihandle,attrib);
	InquireFillAttributes(vdihandle,fillat);
	(* Objektposition holen *)
	ObjectOffset(Tree,Object,linex,liney);
	(* korrigieren wg. Überhang nach links bei Kursivschrift ! *)
	INC(linex,6);
	d:=SetWritingMode(vdihandle,1);	(*	Replace-Modus *)
	d:=SetGraphicTextColour(vdihandle.1);	(*	schwarz	*)
	SetGraphicTextAlignment(vdihandle,0,5,d,d);	(* Bottom-Line *)
	d:=SetFillColour(vdihandle,1);	(* schwarz	*)
	d:=SetFillInteriorStyle(vdihandle,1);	(*	solid *)
	finished:=FALSE; 
	curstyle:=NormalStyle;
	ClearLine;c
	SetCursor;
	REPEAT
		DoTypeWrite 
	UNTIL finished;
	(* alte Einstellungen wiederherstelen *) 
	d:=SetFillInteriorStyle(vdihandle,fillat[0]); 
	d:=SetFillColour(vdihandle,fillat[1]); 
	d:=SetWritingMode(vdihandle,fillat[3]); 
	d:=SetGraphicTextColour(vdihandle,attrib[i]);
	SetGraphicTextAlignment(vdihandle,attrib[3],attrib[4],d,d); 
	d:=SetGraphicTextEffects(vdihandle,Normalstyle);
	(* Fensteraktionen und Menüs wieder zulassen *) 
	WindowUpdate(EndUpdate);
	WindowUpdate(EndMouseControl);
END ObjectEditExtended;

BEGIN
	(* MFDB vorbereiten *)
	WITH KFDB DO
		(* Bildschirmadresse holen *) 
		pointer :=ScreenPhysicalBase();
		(* Auflösung setzen *)
		CASE GetResolution() OF 
			Low: width:=320;
				height:=200;
				(* Schriftart setzen *) 
				d:=SetGraphicTextEffects(vdihandle,curstyle);
		| 61: (* F3 *)
			IF ODD(curstyle DIV Italic) THEN (* Kursivschrift ein-/ *) 
				DEC(curstyle,Italic);	(*	ausschalten	*)
			ELSE
				INC(curstyle,Italic);
			END;
			(* Schriftart setzen *)
			d:=SetGraphicTextEffects(vdihandle, curstyle);
		| 62: (* F4 *)
			IF ODD(curstyle DIV Light) THEN (* Hellschrift ein-/ *)
				DEC(curstyle,Light);	(* ausschalten	*)
			ELSE
				INC(curstyle,Light);
			END;
			(* Schriftart setzen *)
			d:=SetGraphicTextEffects(vdihandle,curstyle);
		| 75: (* <- *)
			IF lasttype>0 THEN 
				SetCursor;
				DEC(lasttype);	(* Cursor nach links *)
				SetCursor;
				curstyle:=typestyle[lasttype];
			END;
		|	77:	(* -> *)
			IF lasttype<Typelength THEN 
				SetCursor;
				INC(lasttype);	(*	Cursor nach rechts *)
				SetCursor;
				curstyle:=typestyle[lasttype];
			END;
		|	83:	( * DEL *.)
			SetCursor;
			MoveLine(TRUE,lasttype+1,Typelength);	(* Bereich verschieben *)
			SetCursor;
		| 114: (* Enter *)
			finished:=TRUE; (* beenden (Zehnerblock) *)
	ELSE
		(* normale Eingabe einfügen *) 
		IF (INTEGER(CHAR(kret))>31) THEN 
			SetCursor;
			MoveLine(FALSE,lasttype,Typelength-2);	(*	Bereich verschieben *)
			typeline[lasttype]:=CHAR(kret);	(* Zeichen und *)
			typestyle[lasttype]:=curstyle;	(*	Stil merken *)
			IF lasttype=Typelength THEN
				DEC(lasttype)	(*	Korrektur am Zeilenende *)
			END;
			(* neues Zeichen ausgeben *)
			d:=SetGraphicTextEffects(vdihandle,curstyle); 
			str[0]:=CHAR(kret); 
			str[1]:=0C;
			GrafMouse(MouseOff,NIL);
			GraphicText(vdihandle,1inex+lasttype*charwidth,liney,str); 
				planes:= 4; 
		| Medium : width:=640;
				height:=200; 
				planes:=2; 
		| High : width:=640;
				height:=400; 
				planes:=1;
		END;
		(* Bildschirmbreite in Worten errechnen *) 
		widthW:=width DIV 16;
		(* Format ist konstant Standard-Format ! *) 
		format:=1;
	END;
END AESExtender.

Listing 2

MODULE Demo;

FROM SYSTEM IMPORT CODE, ADDRESS;
FROM AESResources IMPORT ResourceObjectFix;
FROM AESForms IMPORT FormCenter;
FROM AESObjects IMPORT ObjectDraw;
FROM AESExtender IMPORT ObjectEditExtended;

(* Durch das Modul Resource steht die benjtigte Dialogbox im 
Programmcode. Es wurde mit dem RSCMAKER erstellt. Die Baum-Adresse 
steht in diesem Fall in ObjectAddr.
Es ersetzt die ResourceLoad und ResourceGetAddr Aufrufe 
bei externen RSC Dateien *)

MODULE resource;
(* Produced by ResourceMaker 0.10a
	Copyright (c) 1985, 1986 Modula 2 Software Ltd.
	Copyright (c) 1985,	1986	TDI	Software	Ine.
	Resource file Version: 00000H *)

	IMPORT CODE, ADDRESS, ResourceObjectFix;
	EXPORT ObjectAddr;

	CONST
		(* object types that don’t have to be relocated *)
		GBOX = 20;
		GPROGDEF = 24;
		GIBOX = 25;
		GBOXCHAR = 27;

	CONST
		(* resource file object counts and ir.dicies *) 
		nrObjects = 8; ixObjects = 134; 
		nrTrees = 1; ixTrees = 326;

	(* resource file data inserted into CODE stream *)
	(*$S-,$P-,$T-*)
	PROCEDURE RESOURCEDATA0;
	BEGIN
		CODE(00000H,00086H,OC086H,00086H,00086H,00086H,00024H,00086H); 
		CODE(00086H,00146H,00008H,000Ö1H,00000H,000O0H,00000H,00000H); 
		CODE(00000H,0014AH,04631H,0203DH,02046H,06574H.07400H,04632H); 
		CODE(0203DH,02055H,06E74H,06572H,07374H,07269H,06368H,0656EH); 
		CODE(00046H,03320H,03D20H,04B75H,07273H,06976H,00046H,03420H); 
		CODE(03D20H,04865H,06C6CH,00027H,04F62H,06A65H,06374H,04564H); 
		CODE(06974H,04578H,07465H.06E64H,06564H,02700H,04B6FH,06D66H); 
		CODE(C6F72H,07461H,06265H,06C20H,04564H,06974H,06965H,07265H); 
		CODE(06E20H,06D69H,07400H,0FFFFH,00001H,00007H,00014H,00000H); 
		CODE(00010H,00002H,01100H,00000H,00000H,00039H,00007H,00002H); 
		CODE(0FFFFH,0FFFFH,00014H,00O00H,00010H,000FFH,01100H,00002H); 
		CODE(00003H,00035H,00001H,00003H,0FFFFH,0FFFFH,000ICH,00000H); 
		CODE(00000H,00000H,00024H,00002H,00005H,00009H,00001H,00004H);
		CODE(0FFFFH,0FFFFH,000ICH,00000H,00000H,00000H,0002EH,0000DH);
		CODE(00005H,00012H.00001H,00005H,0FFFFH,0FFFFH,0O01CH,00000H);
		CODE(00000H.00000H.00041H,00021H,00005H,0000BH,00001H,00006H); 
		CODE(0FFFFH,0FFFFH,0001CH,00000H,00000H,00000H,0004DH,0002EH); 
		CODE(00005H,00009H,00001H,00007H,0FFFFH,0FFFFH,0001CH,00000H); 
		CODE(00000H,00000H,00057H,00020H,O0001H,00014H,00001H,00000H); 
		CODE(0FFFFH,0FFFFH,0001CH,00020H,000C0H,00000H,0006CH.00005H);
		CODE(00001H,00019H.00001H,00000H,00086H);
	END RESOURCEDATA0;

TYPE OBJECT = RECORD
			next, head: INTEGER; 
			tail, type; INTEGER; 
			flags, state: INTEGER; 
			spec: ADDRESS; 
			x, y: INTEGER; 
			width, height: INTEGER;
		END;
VAR i: CARDINAL; o: INTEGER; x: ADDRESS;
	TreeAddr: POINTER TO ARRAY [0..nrTrees-1] OF ADDRESS;
	ObjectAddr: POINTER TO ARRAY [0..nrObjects-1] OF OBJECT;

BEGIN
	(* relocate tree indicies *) 
	x := ADDRESS(RESOÜRCEDATA0) + ixTrees;
	TreeAddr := x;
	FOR i := 0 TO nrTrees-1 DO
		TreeAddr^[i] := TreeAddr^[i] + ADDRESS(RESOURCEDATA0); INC(x,4);
	END;
	(* relocate object specs *) 
	x := ADDRESS(RESOURCEDATA0) + ixObjects;
	ObjectAddr := x;
	FOR i := 0 TO nrObjects-1 DO 
		WITH ObjectAddr^[i] DO
			IF (type # GBOX) & (type # GPROGDEF) &
				(type # GIBOX) & (type # GBOXCHAR) THEN 
				 (* relocate against resource base *) 
				 spec := spec + ADDRESS(RESOURCEDATA0);
			END;
			(* Fix up the scaling *)
			ResourceObjectFix(ObjectAddr,i); (*0.10a*)
		END;
	END;
END resource;

CONST
	DEMOBOX = 0 ;
	EDIT = 1 ; 
	editlength = 50;

VAR x, y , w , h :INTEGER;
	line:ARRAY [0..editlength] OF CHAR; 
	style:ARRAY[0..editlength] OF INTEGER;

BEGIN
	(* Dialogbox zentrieren *)
	FormCenter(ObjectAddr,x,y,w,h);
	(* Dialogbox zeichnen *)
	ObjectDraw(ObjectAddr,DEMOBOX,5,x,y,w,h);
	(* und Editieren aufrufen *)
	ObjectEditExtended(ObjectAddr,EDIT,50,line,style);
END Demo.


Aus: ST-Computer 06 / 1987, Seite 34

Links

Copyright-Bestimmungen: siehe Über diese Seite