← ST-Computer 06 / 1987

Komfort in Dialogboxen mit Modula-2

Listings

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.