|
|
@@ -26,10 +26,24 @@ CONST
|
|
|
|
|
|
vfPressed = 8; (* internal, transient *)
|
|
|
|
|
|
+ MAXMENUS = 8;
|
|
|
+ MAXMITEMS = 16;
|
|
|
+
|
|
|
TYPE
|
|
|
Cell = RECORD cp : CARDINAL; attr : CARDINAL; END;
|
|
|
BytePtr = POINTER TO BYTE;
|
|
|
|
|
|
+ MenuItem = RECORD
|
|
|
+ text : ARRAY [0..47] OF CHAR;
|
|
|
+ tag : INTEGER;
|
|
|
+ sep : BOOLEAN;
|
|
|
+ END;
|
|
|
+ MenuDef = RECORD
|
|
|
+ title : ARRAY [0..31] OF CHAR;
|
|
|
+ items : ARRAY [0..MAXMITEMS - 1] OF MenuItem;
|
|
|
+ count : INTEGER;
|
|
|
+ END;
|
|
|
+
|
|
|
VAR
|
|
|
cols, rows : INTEGER;
|
|
|
cells : ARRAY [0..MAXCELLS - 1] OF Cell;
|
|
|
@@ -58,6 +72,12 @@ VAR
|
|
|
focusList : ARRAY [0..MAXVIEWS - 1] OF ViewPtr;
|
|
|
focusCount : INTEGER;
|
|
|
|
|
|
+ menus : ARRAY [0..MAXMENUS - 1] OF MenuDef;
|
|
|
+ menuCount : INTEGER;
|
|
|
+ menuOpen : INTEGER;
|
|
|
+ menuSel : INTEGER;
|
|
|
+ menuShown : BOOLEAN;
|
|
|
+
|
|
|
(*========================================================================*)
|
|
|
(* libc struct helpers *)
|
|
|
(*========================================================================*)
|
|
|
@@ -201,6 +221,7 @@ BEGIN
|
|
|
cursorX := 0; cursorY := 0; cursorShown := FALSE;
|
|
|
clipN := 0;
|
|
|
modal := NIL; dragWin := NIL; lastSender := NIL;
|
|
|
+ menuCount := 0; menuOpen := -1; menuSel := 0; menuShown := FALSE;
|
|
|
(* reset view pool *)
|
|
|
FOR i := 0 TO MAXVIEWS - 1 DO used[i] := FALSE END;
|
|
|
desk := NIL;
|
|
|
@@ -1034,12 +1055,231 @@ BEGIN
|
|
|
END
|
|
|
END DrawView;
|
|
|
|
|
|
+(*========================================================================*)
|
|
|
+(* Menu bar + drop-down menus *)
|
|
|
+(*========================================================================*)
|
|
|
+
|
|
|
+PROCEDURE MenuBarInit;
|
|
|
+BEGIN
|
|
|
+ menuCount := 0; menuOpen := -1; menuSel := 0; menuShown := FALSE
|
|
|
+END MenuBarInit;
|
|
|
+
|
|
|
+PROCEDURE MenuAdd (title : ARRAY OF CHAR);
|
|
|
+BEGIN
|
|
|
+ IF menuCount < MAXMENUS THEN
|
|
|
+ CopyText(menus[menuCount].title, title);
|
|
|
+ menus[menuCount].count := 0;
|
|
|
+ menuCount := menuCount + 1;
|
|
|
+ menuShown := TRUE
|
|
|
+ END
|
|
|
+END MenuAdd;
|
|
|
+
|
|
|
+PROCEDURE MenuItemAdd (text : ARRAY OF CHAR; tag : INTEGER);
|
|
|
+ VAR i : INTEGER;
|
|
|
+BEGIN
|
|
|
+ IF menuCount > 0 THEN
|
|
|
+ i := menus[menuCount - 1].count;
|
|
|
+ IF i < MAXMITEMS THEN
|
|
|
+ CopyText(menus[menuCount - 1].items[i].text, text);
|
|
|
+ menus[menuCount - 1].items[i].tag := tag;
|
|
|
+ menus[menuCount - 1].items[i].sep := FALSE;
|
|
|
+ menus[menuCount - 1].count := i + 1
|
|
|
+ END
|
|
|
+ END
|
|
|
+END MenuItemAdd;
|
|
|
+
|
|
|
+PROCEDURE MenuSep;
|
|
|
+ VAR i : INTEGER;
|
|
|
+BEGIN
|
|
|
+ IF menuCount > 0 THEN
|
|
|
+ i := menus[menuCount - 1].count;
|
|
|
+ IF i < MAXMITEMS THEN
|
|
|
+ menus[menuCount - 1].items[i].text[0] := 0C;
|
|
|
+ menus[menuCount - 1].items[i].tag := 0;
|
|
|
+ menus[menuCount - 1].items[i].sep := TRUE;
|
|
|
+ menus[menuCount - 1].count := i + 1
|
|
|
+ END
|
|
|
+ END
|
|
|
+END MenuSep;
|
|
|
+
|
|
|
+PROCEDURE MenuBar (show : BOOLEAN);
|
|
|
+BEGIN
|
|
|
+ menuShown := show;
|
|
|
+ IF NOT show THEN menuOpen := -1 END
|
|
|
+END MenuBar;
|
|
|
+
|
|
|
+PROCEDURE MenuTitleX (i : INTEGER) : INTEGER;
|
|
|
+ VAR k, x : INTEGER;
|
|
|
+BEGIN
|
|
|
+ x := 1;
|
|
|
+ FOR k := 0 TO i - 1 DO
|
|
|
+ x := x + TextLen(menus[k].title) + 2
|
|
|
+ END;
|
|
|
+ RETURN x
|
|
|
+END MenuTitleX;
|
|
|
+
|
|
|
+PROCEDURE MenuWidth (i : INTEGER) : INTEGER;
|
|
|
+ VAR k, w, l : INTEGER;
|
|
|
+BEGIN
|
|
|
+ w := TextLen(menus[i].title) + 2;
|
|
|
+ FOR k := 0 TO menus[i].count - 1 DO
|
|
|
+ l := TextLen(menus[i].items[k].text) + 4;
|
|
|
+ IF l > w THEN w := l END
|
|
|
+ END;
|
|
|
+ RETURN w
|
|
|
+END MenuWidth;
|
|
|
+
|
|
|
+PROCEDURE MenuTitleAt (x : INTEGER) : INTEGER;
|
|
|
+ VAR i, xx : INTEGER;
|
|
|
+BEGIN
|
|
|
+ FOR i := 0 TO menuCount - 1 DO
|
|
|
+ xx := MenuTitleX(i);
|
|
|
+ IF (x >= xx) AND (x < xx + TextLen(menus[i].title)) THEN RETURN i END
|
|
|
+ END;
|
|
|
+ RETURN -1
|
|
|
+END MenuTitleAt;
|
|
|
+
|
|
|
+PROCEDURE FirstSel (i : INTEGER) : INTEGER;
|
|
|
+ VAR k : INTEGER;
|
|
|
+BEGIN
|
|
|
+ k := 0;
|
|
|
+ WHILE (k < menus[i].count) AND menus[i].items[k].sep DO k := k + 1 END;
|
|
|
+ IF k >= menus[i].count THEN k := 0 END;
|
|
|
+ RETURN k
|
|
|
+END FirstSel;
|
|
|
+
|
|
|
+PROCEDURE MenuStep (i, cur, dir : INTEGER) : INTEGER;
|
|
|
+ VAR k, n : INTEGER; found : BOOLEAN;
|
|
|
+BEGIN
|
|
|
+ n := menus[i].count;
|
|
|
+ IF n = 0 THEN RETURN 0 END;
|
|
|
+ k := cur; found := FALSE;
|
|
|
+ WHILE NOT found DO
|
|
|
+ k := (k + dir + n) MOD n;
|
|
|
+ IF (NOT menus[i].items[k].sep) OR (k = cur) THEN found := TRUE END
|
|
|
+ END;
|
|
|
+ RETURN k
|
|
|
+END MenuStep;
|
|
|
+
|
|
|
+PROCEDURE DrawMenuBar;
|
|
|
+ VAR i, k, x : INTEGER; r : Rect; attr : Attr;
|
|
|
+ space : CARDINAL;
|
|
|
+BEGIN
|
|
|
+ IF (NOT menuShown) OR (menuCount = 0) THEN RETURN END;
|
|
|
+ space := VAL(CARDINAL, ORD(' '));
|
|
|
+ Fill(0, 0, cols, 1, space, A(Black, LightGray, FALSE));
|
|
|
+ FOR i := 0 TO menuCount - 1 DO
|
|
|
+ x := MenuTitleX(i);
|
|
|
+ IF menuOpen = i THEN attr := A(White, Blue, FALSE)
|
|
|
+ ELSE attr := A(Black, LightGray, FALSE) END;
|
|
|
+ DrawText(x, 0, menus[i].title, attr)
|
|
|
+ END;
|
|
|
+ IF menuOpen < 0 THEN RETURN END;
|
|
|
+ i := menuOpen;
|
|
|
+ r.x := MenuTitleX(i); r.y := 1;
|
|
|
+ r.w := MenuWidth(i); r.h := menus[i].count + 2;
|
|
|
+ DrawShadow(r, A(Black, Black, FALSE));
|
|
|
+ Fill(r.x, r.y, r.w, r.h, space, A(Black, LightGray, FALSE));
|
|
|
+ DrawBox(r, SingleFrame, A(Black, LightGray, FALSE));
|
|
|
+ FOR k := 0 TO menus[i].count - 1 DO
|
|
|
+ IF menus[i].items[k].sep THEN
|
|
|
+ DrawHLine(r.x + 1, r.y + 1 + k, r.w - 2, H_SINGLE,
|
|
|
+ A(DarkGray, LightGray, FALSE))
|
|
|
+ ELSE
|
|
|
+ IF k = menuSel THEN attr := A(White, Blue, FALSE)
|
|
|
+ ELSE attr := A(Black, LightGray, FALSE) END;
|
|
|
+ Fill(r.x + 1, r.y + 1 + k, r.w - 2, 1, space, attr);
|
|
|
+ DrawText(r.x + 2, r.y + 1 + k, menus[i].items[k].text, attr)
|
|
|
+ END
|
|
|
+ END
|
|
|
+END DrawMenuBar;
|
|
|
+
|
|
|
+PROCEDURE MenuHandle (e : Event; VAR handled : BOOLEAN) : INTEGER;
|
|
|
+ VAR i, idx : INTEGER; r : Rect;
|
|
|
+BEGIN
|
|
|
+ handled := FALSE;
|
|
|
+ IF (NOT menuShown) OR (menuCount = 0) THEN RETURN cmNone END;
|
|
|
+
|
|
|
+ IF e.kind = evKey THEN
|
|
|
+ IF e.key = kCtrlC THEN RETURN cmNone END;
|
|
|
+ IF e.key = kF10 THEN
|
|
|
+ IF menuOpen < 0 THEN menuOpen := 0 ELSE menuOpen := (menuOpen + 1) MOD menuCount END;
|
|
|
+ menuSel := FirstSel(menuOpen);
|
|
|
+ handled := TRUE; RETURN cmNone
|
|
|
+ END;
|
|
|
+ IF menuOpen >= 0 THEN
|
|
|
+ handled := TRUE;
|
|
|
+ IF e.key = kEsc THEN menuOpen := -1
|
|
|
+ ELSIF e.key = kLeft THEN
|
|
|
+ menuOpen := (menuOpen + menuCount - 1) MOD menuCount;
|
|
|
+ menuSel := FirstSel(menuOpen)
|
|
|
+ ELSIF e.key = kRight THEN
|
|
|
+ menuOpen := (menuOpen + 1) MOD menuCount;
|
|
|
+ menuSel := FirstSel(menuOpen)
|
|
|
+ ELSIF e.key = kUp THEN menuSel := MenuStep(menuOpen, menuSel, -1)
|
|
|
+ ELSIF e.key = kDown THEN menuSel := MenuStep(menuOpen, menuSel, +1)
|
|
|
+ ELSIF (e.key = kEnter) OR (e.key = kSpace) THEN
|
|
|
+ IF NOT menus[menuOpen].items[menuSel].sep THEN
|
|
|
+ i := menus[menuOpen].items[menuSel].tag;
|
|
|
+ menuOpen := -1; lastSender := NIL;
|
|
|
+ RETURN i
|
|
|
+ END
|
|
|
+ END;
|
|
|
+ RETURN cmNone
|
|
|
+ END;
|
|
|
+ RETURN cmNone
|
|
|
+ END;
|
|
|
+
|
|
|
+ IF e.kind # evMouse THEN RETURN cmNone END;
|
|
|
+
|
|
|
+ (* mouse over a title *)
|
|
|
+ IF e.my = 0 THEN
|
|
|
+ i := MenuTitleAt(e.mx);
|
|
|
+ IF i >= 0 THEN
|
|
|
+ handled := TRUE;
|
|
|
+ IF e.mpressed THEN
|
|
|
+ IF menuOpen = i THEN menuOpen := -1
|
|
|
+ ELSE menuOpen := i; menuSel := FirstSel(i) END
|
|
|
+ ELSIF (e.mbtn > 0) OR (e.mwheel # 0) THEN
|
|
|
+ menuOpen := i; menuSel := FirstSel(i)
|
|
|
+ ELSIF menuOpen >= 0 THEN
|
|
|
+ menuOpen := i; menuSel := FirstSel(i)
|
|
|
+ END;
|
|
|
+ RETURN cmNone
|
|
|
+ END
|
|
|
+ END;
|
|
|
+
|
|
|
+ IF menuOpen >= 0 THEN
|
|
|
+ r.x := MenuTitleX(menuOpen); r.y := 1;
|
|
|
+ r.w := MenuWidth(menuOpen); r.h := menus[menuOpen].count + 2;
|
|
|
+ IF Contains(r, e.mx, e.my) THEN
|
|
|
+ handled := TRUE;
|
|
|
+ idx := e.my - (r.y + 1);
|
|
|
+ IF (idx >= 0) AND (idx < menus[menuOpen].count) AND
|
|
|
+ (NOT menus[menuOpen].items[idx].sep) THEN
|
|
|
+ menuSel := idx;
|
|
|
+ IF e.mpressed THEN
|
|
|
+ i := menus[menuOpen].items[idx].tag;
|
|
|
+ menuOpen := -1; lastSender := NIL;
|
|
|
+ RETURN i
|
|
|
+ END
|
|
|
+ END;
|
|
|
+ RETURN cmNone
|
|
|
+ ELSIF e.mpressed THEN
|
|
|
+ menuOpen := -1;
|
|
|
+ handled := TRUE
|
|
|
+ END
|
|
|
+ END;
|
|
|
+ RETURN cmNone
|
|
|
+END MenuHandle;
|
|
|
+
|
|
|
PROCEDURE DrawTree;
|
|
|
BEGIN
|
|
|
Clear(A(LightGray, Blue, FALSE));
|
|
|
HideCursor;
|
|
|
clipN := 0;
|
|
|
- IF desk # NIL THEN DrawView(desk) END
|
|
|
+ IF desk # NIL THEN DrawView(desk) END;
|
|
|
+ DrawMenuBar
|
|
|
END DrawTree;
|
|
|
|
|
|
PROCEDURE Finish;
|
|
|
@@ -1286,9 +1526,13 @@ BEGIN
|
|
|
END DispatchKey;
|
|
|
|
|
|
PROCEDURE HandleEvent (e : Event) : INTEGER;
|
|
|
- VAR cmd : INTEGER; root : ViewPtr;
|
|
|
+ VAR cmd : INTEGER; root : ViewPtr; handled : BOOLEAN;
|
|
|
BEGIN
|
|
|
IF e.kind = evNone THEN RETURN cmNone END;
|
|
|
+ IF modal = NIL THEN
|
|
|
+ cmd := MenuHandle(e, handled);
|
|
|
+ IF handled THEN RETURN cmd END
|
|
|
+ END;
|
|
|
root := modal;
|
|
|
IF root = NIL THEN root := desk END;
|
|
|
IF root = NIL THEN RETURN cmNone END;
|