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.
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
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.