(*#######################################################################
                            M A G I C M E N U S
  #######################################################################
  V1.02         27.10.90  Peter Hellinger
  V1.01         21.10.90  Peter Hellinger
  V1.00         02.09.90  Peter Hellinger
  #######################################################################*)

IMPLEMENTATION MODULE mtMenus;

(*------------------------------*)
(*       COMPILERSWITCHES       *)
(*------------------------------*)
(*  TDI-Version:   DEAKTIVIERT  *)
(*------------------------------*)
(* V-  Overflow-Checks          *)
(* R-  Range-Checks             *)
(* S-  Stack-Check              *)
(* N-  NIL-Checks               *)
(* T-  TDI-Compiler vor 3.01    *)
(* Q+  Branch statt Jumps       *)
(*                              *)
(*------------------------------*)
(*  MM2-Version:     AKTIVIERT  *)
(*------------------------------*)
(*$R-   Range-Checks            *)
(*$S-   Stack-Check             *)
(*                              *)
(*------------------------------*)


FROM SYSTEM     IMPORT  ADDRESS, ADR;
FROM Storage    IMPORT  ALLOCATE, DEALLOCATE;
FROM MagicSys   IMPORT  Nil, Null, Bit0, Bit1, Bit2, Bit3, Bit4, Bit5, Bit6, Bit7,
                        Bit8, Bit9, Bit10, Bit11, Bit12, Bit13, Bit14, Bit15,
                        LOC, Byte, ByteSet, sWORD, sINTEGER, sCARDINAL, sBITSET,
                        lWORD, lINTEGER, lCARDINAL, lBITSET,
                        CastToChar, CastToByte, CastToByteset, CastToInt,
                        CastToCard, CastToBitset, CastToWord, CastToLInt,
                        CastToLCard, CastToLBitset, CastToLWord, CastToAddr;
FROM MagicAES   IMPORT  GBOX, GTEXT, GBOXTEXT, GIBOX, GSTRING, GTITLE,
                        Exit, DISABLED, OBJECT, ObjcDraw, ObjcFind,
                        BEGMCTRL, ENDMCTRL, WindUpdate, WFFULLXYWH, WindGet,
                        FormButton, GrafHandle, MUKEYBD, MUBUTTON, MUM1, 
                        MUM2, MUMESAG, MUTIMER, EvntMulti, AESIntIn, AESIntOut,
                        AESCall;
FROM mtAppl     IMPORT  VDIHandle, MouseOn, MouseOff, MouseArrow;
FROM mtArea     IMPORT  AREA, NewAREA, DisposeAREA, FreeArea, SaveArea,
                        CopyArea, RestoreArea, MOVEUP, MOVEDOWN, MOVELEFT,
                        MOVERIGHT, MoveArea;
FROM MagicStrings  IMPORT  Assign, Append, Length;

IMPORT  MagicAES, MagicVDI;



CONST   MaxObjects =    51;
        Links =         Bit0;
        Rechts =        Bit1;


CONST   Enter =         072H;
        Return =        01CH;
        CurUp =         048H;
        CurDown =       050H;
        CurLeft =       04BH;
        CurRight =      04DH;
        Undo =          061H;


TYPE    tRect =         RECORD
                         x, y, w, h: sINTEGER;
                        END;

TYPE    obTree =        POINTER TO ARRAY [0..1000] OF MagicAES.OBJECT;
        tString =       ARRAY [0..40] OF CHAR;


TYPE    MENUBAR =       POINTER TO Menubar;
        Menubar =       RECORD
                         line:  ARRAY [0..255] OF CHAR;
                         tree:  obTree;
                         num:   sINTEGER;
                         win:   sINTEGER;
                         sub:   sINTEGER;
                         start: sINTEGER;
                         draw:  sINTEGER;
                         spos:  sINTEGER;
                         entry: ARRAY [0..MaxObjects] OF RECORD
                                 text: ARRAY [0..40] OF CHAR;
                                 width: sINTEGER;
                                 pos:   sINTEGER;
                                END;
                         react: RECORD
                                 x: sINTEGER;
                                 y: sINTEGER;
                                 w: sINTEGER;
                                 h: sINTEGER;
                                END;
                        END;


VAR     Dropdown:       ARRAY [0..MaxObjects] OF OBJECT;
        menuArea:       AREA;
        BAR:            MENUBAR;
        b:              sBITSET;
        bool, rekExit:  BOOLEAN;
        screen:         tRect;
        chW, chH:       sINTEGER;
        bW, bH:         sINTEGER;
        mWidth:         sINTEGER;
        mHeight:        sINTEGER;
        ScrollStr:      ARRAY [0..9] OF CHAR;


PROCEDURE scanType (t: obTree; entry, flag: sINTEGER): sINTEGER;
(* Scannt nach einem bestimmten Typflag *)
VAR o, r: sINTEGER;
BEGIN
 o:= entry;
 WHILE (o >= entry)  DO
  WITH t^[o] DO
   IF flag = obType THEN  rekExit:= TRUE;  RETURN o;  END;
   IF (obHead > -1) THEN
    r:= scanType (t, obHead, flag);
    IF rekExit THEN RETURN r; END;
   END;
   o:= obNext;
  END;
 END;
 RETURN 0;
END scanType;


PROCEDURE DoEvent (VAR x, y: sINTEGER;
                   VAR button: sBITSET;
                   VAR scan: sINTEGER): sBITSET;
VAR event: sBITSET;
    i:     sINTEGER;
    split: RECORD
            CASE: BOOLEAN OF
             TRUE: wert: sINTEGER;|
             FALSE: hi: CHAR;
                    lo: CHAR;|
            END;
           END;
BEGIN
 (* Array's laden *)
 event:= {MUKEYBD, MUTIMER, MUBUTTON};
 AESIntIn[ 0]:= CastToInt (event);
 AESIntIn[ 1]:= 257;
 AESIntIn[ 2]:= 3;
 AESIntIn[ 3]:= 0;
 i:= AESCall(25, 16, 7, 1, 0);
 event:= CastToBitset (i);
 x:= AESIntOut[1];
 y:= AESIntOut[2];
 button:= CastToBitset (AESIntOut[3]);
 (* kbshift:= CastToBitset (AESIntOut[4]); *)
 split.wert:= AESIntOut[5];
 scan:= ORD (split.hi);
 (* ascii:= split.lo; *)
 RETURN event;
END DoEvent;


PROCEDURE Entprelle;
VAR x, y:   sINTEGER;
    button: sBITSET;
BEGIN
 REPEAT
  MagicAES.GrafMkstate (x, y, button, b);
 UNTIL button = {};
END Entprelle;


PROCEDURE GetMenu (mx, my: sINTEGER): sINTEGER;
VAR j: sINTEGER;
BEGIN
 WITH BAR^ DO
  WITH react DO
   IF (my > y) AND (my < (y + h)) THEN
    IF (mx > x) AND (mx < (x + 3 * chW)) THEN  RETURN -2;  END;
    IF (mx > (x + 3 * chW) - 1) AND (mx < (x + 6 * chW)) THEN  RETURN -3;  END;
    FOR j:= start TO draw DO
     IF (mx >= (x + entry[j].pos)) AND
        (mx <= (x + entry[j].pos + entry[j].width)) THEN
      RETURN j;
     END;
    END;
   END;
  END;
 END;
 RETURN -1; 
END GetMenu;


PROCEDURE DoMenu (t: obTree; drop: sINTEGER): sINTEGER;
VAR mx, my, ox, oy, i, j, o: sINTEGER;
    ob, oldob, taste, scan, clicks: sINTEGER;
    button, kbshift, event: sBITSET;
    ascii: CHAR;

 PROCEDURE DrawBar (o: sINTEGER);
 VAR r: tRect;
 BEGIN
  IF o > 0 THEN
   r.x:= t^[0].obX + t^[o].obX;
   r.y:= t^[0].obY + t^[o].obY;
   r.w:= r.x + t^[o].obWidth - 1;
   r.h:= r.y + t^[o].obHeight - 1;
   MagicVDI.Bar (VDIHandle, r);
  END;
 END DrawBar;

BEGIN
 i:= MagicVDI.SetFillcolor (VDIHandle, 1); 
 bool:= MagicVDI.SetFillperimeter (VDIHandle, FALSE);
 oldob:= -1;  ob:= -1;  ox:= -1;  oy:= -1;
 LOOP
  event:= DoEvent (mx, my, button, scan);
  (* Anderes Men gewhlt? *)
  j:= GetMenu (mx, my); 
  IF (j # -1) AND (j # drop) THEN  RETURN -1;  END;
  (* Objekt finden *)
  IF (mx # ox) OR (my # oy) THEN
   ob:= MagicAES.ObjcFind (t, 0, MaxObjects, mx, my);  ox:= mx;  oy:= my;
  END;
  IF (MUKEYBD IN event) THEN
   CASE scan OF
    Return,
    Enter:      MouseOff;  DrawBar (oldob);  MouseOn;  EXIT;
                |
    CurUp:      o:= ob;
                IF o > 1 THEN
                 DEC (o);
                 IF (DISABLED IN t^[o].obState) THEN  DEC (o); END;
                 IF o >= 1 THEN  ob:= o;  END;
                ELSE
                 ob:= t^[0].obTail;
                END;
                |
     CurDown:   o:= ob;
                IF (o < t^[0].obTail) AND (o > 0) THEN
                INC (o);
                IF (DISABLED IN t^[o].obState) THEN  INC (o); END;
                IF o <= t^[0].obTail THEN  ob:= o;  END;
                ELSE
                ob:= 1;
                END;
                |
     Undo:      ob:= -1;  EXIT;
                |
     ELSE ;
   END;
  END;
  (* Rechte Maustaste? *)
  IF (MUBUTTON IN event) AND (Rechts IN button) THEN
   Entprelle;  ob:= -1;  EXIT;
  END;
  (* Objekt gltig? *)
  IF (ob > 0) AND NOT (DISABLED IN t^[ob].obState) THEN
   (* Objekt selektiert? *)
   IF ob # oldob THEN
    MouseOff;  DrawBar (oldob);  DrawBar (ob);  MouseOn;  oldob:= ob;
   END;
   (* Maustaste? Rechts wurde vorher abgefragt, hier kann nur noch Links kommen *)
   IF (MUBUTTON IN event) THEN  Entprelle;  EXIT;  END;
  ELSIF (MUBUTTON IN event) THEN
   Entprelle;  ob:= -1;  EXIT;
  END;
 END; (* LOOP *)
 MouseOn;
 RETURN ob;
END DoMenu;


PROCEDURE MakeMenu (subnum: sINTEGER): sINTEGER;
VAR maxW, maxH, n, i, j, ob, offset: sINTEGER;
BEGIN
 j:= 0;  n:= 1;  maxW:= 0;  maxH:= 0;
 (*-- Basisobjekt --*)
 Dropdown[0].obNext:=    -1;
 Dropdown[0].obHead:=    -1;
 Dropdown[0].obTail:=    -1;
 Dropdown[0].obType:=    GBOX;
 Dropdown[0].obFlags:=   {};
 Dropdown[0].obState:=   {};
 Dropdown[0].Box.char:=  0C;
 Dropdown[0].Box.frame:= 377C;
 Dropdown[0].Box.flags:= {Bit15, Bit11};
 Dropdown[0].obX:=       0;
 Dropdown[0].obY:=       0;
 Dropdown[0].obWidth:=   0;
 Dropdown[0].obHeight:=  0;
 ob:= BAR^.sub + 1;
 FOR j:= 1 TO subnum DO  ob:= BAR^.tree^[ob].obNext;  END;
 IF ob < BAR^.sub THEN  RETURN -1;  END;
 offset:= ob - 1;
 j:= BAR^.tree^[ob].obHead;
 (*-- Objekte addieren --*)
 LOOP
  i:= scanType (BAR^.tree, j, GSTRING);
  IF BAR^.tree^[i].obWidth > maxW THEN  maxW:= BAR^.tree^[i].obWidth;  END;
  Dropdown[n].obNext:=    -1;
  Dropdown[n].obHead:=    -1;
  Dropdown[n].obTail:=    -1;
  Dropdown[n].obType:=    BAR^.tree^[i].obType;
  Dropdown[n].obFlags:=   BAR^.tree^[i].obFlags;
  Dropdown[n].obState:=   BAR^.tree^[i].obState;
  Dropdown[n].StringPtr:= BAR^.tree^[i].StringPtr;
  Dropdown[n].obX:=       0;
  Dropdown[n].obY:=       maxH;
  Dropdown[n].obWidth:=   BAR^.tree^[i].obWidth;
  Dropdown[n].obHeight:=  chH;
  MagicAES.ObjcAdd (ADR(Dropdown), 0, n);
  INC (n);  INC (maxH, chH);  j:= i + 1;
  IF i = BAR^.tree^[ob].obTail THEN  EXIT;  END;
 END;
 FOR i:= 0 TO n - 1 DO  Dropdown[i].obWidth:= maxW;  END;
 Dropdown[0].obHeight:= maxH;
 RETURN offset;
END MakeMenu;


PROCEDURE NewMenu (menu: ADDRESS; VAR bar: MENUBAR): BOOLEAN;
VAR i: sINTEGER;
BEGIN
 NEW (bar);
 IF bar = NIL THEN  RETURN FALSE;  END;
 MagicAES.GrafHandle (i, chW, chH, bW, bH);
 WITH bar^ DO
  i:= 3;  num:= 0;  tree:= menu;  spos:= sINTEGER(Length (ScrollStr)) * chW; 
  LOOP
   Assign (tree^[i].StringPtr^, entry[num].text);
   entry[num].width:= sINTEGER(Length (tree^[i].StringPtr^)) * chW;
   entry[num].pos:= 0;
   IF i = tree^[2].obTail THEN  EXIT;  END;
   INC (i);  INC (num);
  END;
  sub:= i + 1;
  start:= 0;
 END;
 RETURN TRUE;
END NewMenu;


PROCEDURE FreeMenu (VAR bar: MENUBAR);
BEGIN
 IF BAR = bar THEN BAR:= NIL; END;
 DISPOSE (bar);
END FreeMenu;


PROCEDURE DrawMenu (bar: MENUBAR; window: sINTEGER; VAR wx, wy, ww, wh: sINTEGER);
VAR i, l: sINTEGER;
    r:    tRect;
    adr:  ADDRESS;
BEGIN
 IF bar # NIL THEN
  MagicAES.WindGet (window, MagicAES.WFWORKXYWH, r);
  mWidth:= r.x + r.w;  mHeight:= r.y + r.h;
  (* Reaktions-Rechteck berechnen *)
  WITH bar^ DO
   win:= window;
   react.x:= r.x;
   react.y:= r.y - bH;
   react.w:= r.w;
   react.h:= bH;
   wx:= react.x;
   wy:= react.y;
   ww:= react.w;
   wh:= react.h;
   l:= spos;  i:= start;  Assign(ScrollStr, line);
   WHILE (i <= num) AND ((l + entry[i].width) < (r.w - bW)) DO
    Append (entry[i].text, line);  
    entry[i].pos:= l;
    INC (l, entry[i].width);  INC (i);
   END;
   draw:= i - 1;
   adr:= ADR(line);
   MagicAES.WindSet (win, MagicAES.WFINFO, adr);
  END; 
  MagicAES.WindGet (0, MagicAES.WFTOP, r);
  IF r.x = window THEN  BAR:= bar;  END;
 END;
END DrawMenu;


PROCEDURE HandleMenu (VAR menu, eintrag: sINTEGER);
TYPE tScrol = (links, rechts);
VAR  i, j, mx, my, scan, off: sINTEGER;
     scrol: tScrol;
     b, sr: tRect;
     button, s: sBITSET;

 PROCEDURE InvertMenu;
 BEGIN
  MouseOff;   MagicVDI.FillRectangle (VDIHandle, b);  MouseOn;
 END InvertMenu;

 PROCEDURE ScrollMenu (dir: sINTEGER);
 VAR i, l: sINTEGER;
     adr:  ADDRESS;
 BEGIN
  WITH BAR^ DO
   InvertMenu;
   LOOP
    MagicAES.GrafMkstate (mx, my, button, s);
    IF GetMenu (mx, my) # dir THEN  EXIT;  END;
    IF button # {} THEN
     Entprelle;
     IF (dir = -3) AND (num > (start)) THEN  INC (start);  END;
     IF (dir = -2) AND (start > 0) THEN  DEC (start);  END;
     Assign(ScrollStr, line);  l:= spos;  i:= start;
     WHILE (i <= num) AND ((l + entry[i].width) < (react.w - bW)) DO
      Append (entry[i].text, line);  
      entry[i].pos:= l;
      INC (l, entry[i].width);  INC (i);
     END;
     draw:= i - 1;
     adr:= ADR(line);
     MagicAES.WindSet (win, MagicAES.WFINFO, adr);
     InvertMenu;
     EXIT;
    END;
   END; (* LOOP *)
   InvertMenu;
  END;
 END ScrollMenu;

BEGIN
 menu:= -1;  eintrag:= -1;
 IF BAR # NIL THEN
  i:= MagicVDI.SetWritemode (VDIHandle, MagicVDI.XOR);
  i:= MagicVDI.SetFillcolor (VDIHandle, 1);
  MagicAES.WindGet (0, 7, screen);
  mWidth:= screen.x + screen.w;
  mHeight:= screen.y + screen.h;
  WITH BAR^ DO
   WindUpdate (BEGMCTRL);
   MagicAES.GrafMkstate (mx, my, s, s);
   j:= GetMenu (mx, my);
   IF j >= 0 THEN
    WITH entry[j] DO
     b.x:= react.x + pos;
     b.y:= react.y + 1; 
     b.w:= react.x + pos + width;
     b.h:= react.y + bH - 2;
     InvertMenu;
     off:= MakeMenu (j) + 1;
     WITH Dropdown[0] DO
      obX:= react.x + pos;
      obY:= react.y + bH;
      IF (obX + obWidth) > mWidth THEN  obX:= mWidth - obWidth;  END;
      IF (obY + obHeight) > mHeight THEN  obY:= react.y - obHeight;  END;
      sr.x:= obX - 1;  sr.y:= obY - 1;  sr.w:= obWidth + 2;  sr.h:= obHeight + 2;
     END;
     bool:= SaveArea (menuArea, sr);
     ObjcDraw (ADR (Dropdown), 0, MaxObjects, screen);
     eintrag:= DoMenu (ADR (Dropdown), j);
     IF eintrag >= 0 THEN
      menu:= j + 3;  INC (eintrag, off);
     END;
     RestoreArea (menuArea);
     FreeArea (menuArea);
     InvertMenu;
    END; (* WITH entry[j] *)
   ELSIF j = -2 THEN
    b.x:= react.x;
    b.y:= react.y + 1; 
    b.w:= react.x + (3 * chW);
    b.h:= react.y + bH - 2;
    ScrollMenu (j);
   ELSIF j = -3 THEN
    b.x:= react.x + (3 * chW);
    b.y:= react.y + 1; 
    b.w:= react.x + (6 * chW);
    b.h:= react.y + bH - 2;
    ScrollMenu (j);
   END;
  END; (* WITH BAR^ *)
  WindUpdate (ENDMCTRL);
  i:= MagicVDI.SetWritemode (VDIHandle, MagicVDI.REPLACE);
 END; (* IF BAR *)
END HandleMenu;


BEGIN
 bool:= NewAREA (menuArea);
 BAR:= NIL;
 ScrollStr:= ' <  > ';
 ScrollStr[1]:= 4C;
 ScrollStr[4]:= 3C;
END mtMenus.
