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.


Links

Copyright-Bestimmungen: siehe Über diese Seite
Classic Computer Magazines
[ Join Now | Ring Hub | Random | << Prev | Next >> ]