Subversion Repositories Kolibri OS

Rev

Rev 9628 | Rev 9659 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. (*
  2.     Copyright 2021, 2022 Anton Krotov
  3.  
  4.     This file is part of CEdit.
  5.  
  6.     CEdit is free software: you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation, either version 3 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     CEdit is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with CEdit. If not, see <http://www.gnu.org/licenses/>.
  18. *)
  19.  
  20. MODULE Menu;
  21.  
  22. IMPORT
  23.     SYSTEM, G := Graph, List, K := KolibriOS;
  24.  
  25. CONST
  26.     fontHeight = 22;
  27.     fontWidth = 8;
  28.  
  29.     MainMenuHeight* = K.fontHeight + 7;
  30.         MainMenuX* = 3;
  31.  
  32.     RIGHT = 16;
  33.     LEFT = 16;
  34.     TOP = 1;
  35.  
  36.     maxLEVEL = 1;
  37.  
  38.     backColor = 0F0F0F0H;
  39.     foreColor = 0;
  40.     selBackColor = 091C9F7H;
  41.     selForeColor = 0;
  42.     disBackColor = backColor;
  43.     disForeColor = 808080H;
  44.     disSelBackColor = 0E4E4E4H;
  45.     disSelForeColor = disForeColor;
  46.  
  47.         SHIFT* = 256;
  48.         CTRL*  = 512;
  49.  
  50.  
  51. TYPE
  52.  
  53.         tMainItem* = POINTER TO descMainItem;
  54.  
  55.     tMain* = POINTER TO RECORD (List.tList)
  56.         id: INTEGER
  57.     END;
  58.  
  59.     tMenu* = POINTER TO RECORD
  60.         tid*: INTEGER;
  61.         active*, keyboard: BOOLEAN;
  62.         parent*, child: tMenu;
  63.         mainID: INTEGER;
  64.         winX, winY, width*, height*: INTEGER;
  65.         selItem, cliItem: INTEGER;
  66.  
  67.         font: G.tFont;
  68.         canvas: G.tCanvas;
  69.  
  70.         items: List.tList
  71.     END;
  72.  
  73.     tItem* = POINTER TO RECORD (List.tItem)
  74.         id*, check: INTEGER;
  75.         text: ARRAY 32 OF WCHAR;
  76.         enabled, delim: BOOLEAN;
  77.         child: tMenu
  78.     END;
  79.  
  80.     descMainItem = RECORD (List.tItem)
  81.         id*, x: INTEGER;
  82.         text: ARRAY 32 OF WCHAR;
  83.         menu*: tMenu;
  84.         main: tMain
  85.     END;
  86.  
  87.     tShortcut = POINTER TO RECORD (List.tItem)
  88.         key, MenuItem: INTEGER
  89.     END;
  90.  
  91.     tClick = PROCEDURE (menu: tMenu; id: INTEGER);
  92.     tProc = PROCEDURE;
  93.  
  94. VAR
  95.         MenuItem*: INTEGER;
  96.     stack: ARRAY maxLEVEL + 1, 2500 OF INTEGER;
  97.     TIDs: ARRAY maxLEVEL + 1 OF INTEGER;
  98.     resetTimer: tProc;
  99.     _open: PROCEDURE (m: tMenu; x, y: INTEGER);
  100.     shortcuts: List.tList;
  101. (*
  102.     backColor, foreColor, selBackColor, selForeColor,
  103.     disBackColor, disForeColor, disSelBackColor, disSelForeColor: INTEGER;
  104. *)
  105.  
  106. PROCEDURE AddMainItem* (main: tMain; text: ARRAY OF WCHAR; menu: tMenu);
  107. VAR
  108.         item, prev: tMainItem;
  109. BEGIN
  110.         NEW(item);
  111.         item.id := main.id + main.count;
  112.         COPY(text, item.text);
  113.         item.menu := menu;
  114.         item.main := main;
  115.         menu.mainID := item.id;
  116.         List.append(main, item);
  117.         prev := item.prev(tMainItem);
  118.         IF prev # NIL THEN
  119.                 item.x := prev.x + LENGTH(prev.text)*fontWidth + 9
  120.         ELSE
  121.                 item.x := MainMenuX
  122.         END
  123. END AddMainItem;
  124.  
  125.  
  126. PROCEDURE CreateMain* (id: INTEGER): tMain;
  127. VAR
  128.         res: tMain;
  129.         list: List.tList;
  130. BEGIN
  131.         NEW(res);
  132.         res.id := id;
  133.         list := List.create(res)
  134.         RETURN res
  135. END CreateMain;
  136.  
  137.  
  138. PROCEDURE drawMainItem (item: tMainItem);
  139. VAR
  140.     menuColor, textColor, n: INTEGER;
  141. BEGIN
  142.     IF item.menu.tid # 0 THEN
  143.         menuColor := K.colors.work_text;
  144.         textColor := K.colors.work
  145.     ELSE
  146.         menuColor := K.colors.work;
  147.         textColor := K.colors.work_text
  148.     END;
  149.     n := LENGTH(item.text);
  150.     K.DrawRect(item.x, 0, n*fontWidth + 2, MainMenuHeight, menuColor);
  151.     K.CreateButton(item.id + ORD({30}), item.x, 0, n*fontWidth + 2, MainMenuHeight, K.colors.button, "");
  152.     K.DrawText(item.x + 1, (MainMenuHeight - K.fontHeight) DIV 2 + 1, textColor, item.text)
  153. END drawMainItem;
  154.  
  155.  
  156. PROCEDURE DrawMain* (main: tMain);
  157. VAR
  158.         item: List.tItem;
  159. BEGIN
  160.         item := main.first;
  161.         WHILE item # NIL DO
  162.                 drawMainItem(item(tMainItem));
  163.                 item := item.next
  164.         END
  165. END DrawMain;
  166.  
  167.  
  168. PROCEDURE getMainID (m: tMenu): INTEGER;
  169. BEGIN
  170.         WHILE m.parent # NIL DO
  171.                 m := m.parent
  172.         END
  173.         RETURN m.mainID
  174. END getMainID;
  175.  
  176.  
  177. PROCEDURE ClickMain* (main: tMain; btn: INTEGER): tMenu;
  178. VAR
  179.         item: List.tItem;
  180.         res: tMenu;
  181. BEGIN
  182.         item := List.getItem(main, btn - main.id);
  183.         IF item # NIL THEN
  184.                 res := item(tMainItem).menu
  185.         ELSE
  186.                 res := NIL
  187.         END
  188.         RETURN res
  189. END ClickMain;
  190.  
  191.  
  192. PROCEDURE isSender* (tid: INTEGER): BOOLEAN;
  193. VAR
  194.         i: INTEGER;
  195. BEGIN
  196.         i := 0;
  197.         WHILE (i <= maxLEVEL) & (TIDs[i] # tid) DO
  198.                 INC(i)
  199.         END
  200.         RETURN i <= maxLEVEL
  201. END isSender;
  202.  
  203.  
  204. PROCEDURE exit (m: tMenu);
  205. BEGIN
  206.     m.active := FALSE;
  207.         resetTimer;
  208.     m.tid := 0;
  209.     K.Exit
  210. END exit;
  211.  
  212.  
  213. PROCEDURE escape (m: tMenu);
  214. BEGIN
  215.     m.active := FALSE;
  216.     IF m.parent = NIL THEN
  217.         resetTimer
  218.     END;
  219.     m.tid := 0;
  220.     K.Exit
  221. END escape;
  222.  
  223.  
  224. PROCEDURE repaint (m: tMenu);
  225. VAR
  226.     y, i, X, Y, Y1: INTEGER;
  227.     item: tItem;
  228.     BkColor, TextColor: INTEGER;
  229.     canvas: G.tCanvas;
  230.  
  231. BEGIN
  232. (*
  233.     backColor := K.winColor;
  234.     foreColor := K.textColor;
  235.     selBackColor := K.btnColor;
  236.     selForeColor := K.btnTextColor;
  237.  
  238.     disBackColor := backColor;
  239.     disForeColor := K.darkColor;
  240.     disSelBackColor := K.lightColor;
  241.     disSelForeColor := disForeColor;
  242. *)
  243.     canvas := m.canvas;
  244.     G.SetColor(canvas, backColor);
  245.     G.clear(canvas);
  246.     G.SetColor(canvas, foreColor);
  247.     G.Rect(canvas, 0, 0, m.width, m.height);
  248.     y := TOP;
  249.     i := 0;
  250.     item := m.items.first(tItem);
  251.     WHILE item # NIL DO
  252.         IF item.enabled THEN
  253.             IF i # m.selItem THEN
  254.                 BkColor := backColor;
  255.                 TextColor := foreColor
  256.             ELSE
  257.                 BkColor := selBackColor;
  258.                 TextColor := selForeColor
  259.             END
  260.         ELSE
  261.             IF i # m.selItem THEN
  262.                 BkColor := disBackColor;
  263.                 TextColor := disForeColor
  264.             ELSE
  265.                 BkColor := disSelBackColor;
  266.                 TextColor := disSelForeColor
  267.             END
  268.         END;
  269.         Y := y + (fontHeight - 16) DIV 2;
  270.         G.SetColor(canvas, BkColor);
  271.         G.FillRect(canvas, 1, y, m.width - 1, y + fontHeight - 4);
  272.         G.SetTextColor(canvas, TextColor);
  273.         G.SetBkColor(canvas, BkColor);
  274.         G.TextOut2(canvas, LEFT, Y - 2, item.text, LENGTH(item.text));
  275.  
  276.         G.SetColor(canvas, TextColor);
  277.         IF item.check = 1 THEN
  278.             G.DLine(canvas, 4,  7, Y + 5, -1);
  279.             G.DLine(canvas, 4,  7, Y + 6, -1);
  280.             G.DLine(canvas, 7, 12, Y + 8,  1);
  281.             G.DLine(canvas, 7, 12, Y + 9,  1)
  282.         ELSIF item.check = 2 THEN
  283.             Y1 := y + fontHeight DIV 2 - 2;
  284.             G.FillRect(canvas, 7, Y1 - 2,  9, Y1 + 2);
  285.             G.FillRect(canvas, 6, Y1 - 1, 10, Y1 + 1)
  286.         END;
  287.  
  288.         IF item.child # NIL THEN
  289.             X := m.width - 9;
  290.                 G.Triangle(canvas, X, Y + 2, X, Y + 10, G.triRight)
  291.         END;
  292.  
  293.         INC(y, fontHeight);
  294.         IF item.delim THEN
  295.             G.SetColor(canvas, foreColor);
  296.             G.HLine(canvas, y - 2, 1, m.width - 1)
  297.         END;
  298.         INC(i);
  299.         item := item.next(tItem)
  300.     END;
  301.     G.DrawCanvas(canvas, 0, 0)
  302. END repaint;
  303.  
  304.  
  305. PROCEDURE draw_window (m: tMenu);
  306. BEGIN
  307.     K.BeginDraw;
  308.     K.CreateWindow(m.winX, m.winY, m.width, m.height, 0, 61H, 0, 1, "");
  309.     repaint(m);
  310.     K.EndDraw
  311. END draw_window;
  312.  
  313.  
  314. PROCEDURE close* (m: tMenu);
  315. VAR
  316.         temp: INTEGER;
  317. BEGIN
  318.     IF (m # NIL) & (m.tid # 0) THEN
  319.         IF m.child # NIL THEN
  320.                 close(m.child);
  321.                 m.child := NIL
  322.         END;
  323.         temp := m.tid;
  324.         m.tid := 0;
  325.         K.ExitID(temp);
  326.         m.active := FALSE
  327.     END
  328. END close;
  329.  
  330.  
  331. PROCEDURE SetItem* (id: INTEGER);
  332. BEGIN
  333.         MenuItem := id
  334. END SetItem;
  335.  
  336.  
  337. PROCEDURE click (m: tMenu; i: INTEGER);
  338. VAR
  339.     item: List.tItem;
  340.     p: tMenu;
  341.     id: INTEGER;
  342. BEGIN
  343.         id := -1;
  344.         IF i < 0 THEN
  345.                 id := i
  346.         ELSE
  347.             item := List.getItem(m.items, i);
  348.             IF (item # NIL) & item(tItem).enabled & (item(tItem).child = NIL) THEN
  349.                 id := item(tItem).id
  350.             END
  351.     END;
  352.     IF id # -1 THEN
  353.         SetItem(id);
  354.             p := m.parent;
  355.                 WHILE p # NIL DO
  356.                         p.child := NIL;
  357.                         close(p);
  358.                         p := p.parent
  359.                 END;
  360.                 exit(m)
  361.         END
  362. END click;
  363.  
  364.  
  365. PROCEDURE opened* (m: tMenu): BOOLEAN;
  366.     RETURN m.tid # 0
  367. END opened;
  368.  
  369.  
  370. PROCEDURE isActive (m: tMenu): BOOLEAN;
  371.         RETURN (m # NIL) & ((m.tid # 0) & m.active OR isActive(m.child))
  372. END isActive;
  373.  
  374.  
  375. PROCEDURE closeChild (m: tMenu);
  376. BEGIN
  377.         IF m.child # NIL THEN
  378.                 close(m.child);
  379.                 m.child := NIL
  380.         END
  381. END closeChild;
  382.  
  383.  
  384. PROCEDURE submenu (m: tMenu; keyboard: BOOLEAN): BOOLEAN;
  385. VAR
  386.         item: List.tItem;
  387.         res: BOOLEAN;
  388. BEGIN
  389.         res := FALSE;
  390.     item := List.getItem(m.items, m.selItem);
  391.     IF (item # NIL) & item(tItem).enabled & (item(tItem).child # NIL) THEN
  392.         res := TRUE;
  393.         IF ~opened(item(tItem).child) THEN
  394.                 closeChild(m);
  395.                 item(tItem).child.keyboard := keyboard;
  396.                 _open(item(tItem).child, m.winX + m.width - 2, m.winY + m.selItem*fontHeight);
  397.                 m.child := item(tItem).child;
  398.         END
  399.     ELSE
  400.         closeChild(m)
  401.     END
  402.     RETURN res
  403. END submenu;
  404.  
  405.  
  406. PROCEDURE getShortcut (key: INTEGER): BOOLEAN;
  407. VAR
  408.         item: tShortcut;
  409.         res: INTEGER;
  410. BEGIN
  411.         item := shortcuts.first(tShortcut);
  412.         WHILE (item # NIL) & (item.key # key) DO
  413.                 item := item.next(tShortcut)
  414.         END;
  415.         IF item # NIL THEN
  416.                 res := item.MenuItem
  417.         ELSE
  418.                 res := 0
  419.         END;
  420.         SetItem(res)
  421.         RETURN res # 0
  422. END getShortcut;
  423.  
  424.  
  425. PROCEDURE [stdcall] window (m: tMenu);
  426. VAR
  427.     x, y: INTEGER;
  428.     msState: SET;
  429.     key, temp: INTEGER;
  430.     shift, ctrl: BOOLEAN;
  431. BEGIN
  432.     m.selItem := ORD(m.keyboard) - 1;
  433.     m.cliItem := -1;
  434.     m.keyboard := FALSE;
  435.     K.SetEventsMask({0, 1, 5});
  436.     WHILE TRUE DO
  437.         CASE K.EventTimeout(100) OF
  438.         |0:
  439.         |1:
  440.             draw_window(m)
  441.         |2:
  442.                 K.getKBState(shift, ctrl);
  443.             key := K.GetKey() DIV 65536;
  444.                     IF shift THEN
  445.                         INC(key, SHIFT)
  446.                     END;
  447.                     IF ctrl THEN
  448.                         INC(key, CTRL)
  449.                     END;
  450.  
  451.                 CASE key OF
  452.             |72:
  453.                 m.selItem := (m.selItem - 1) MOD m.items.count
  454.             |80:
  455.                 m.selItem := (m.selItem + 1) MOD m.items.count
  456.             |28:
  457.                 IF m.selItem >= 0 THEN
  458.                     click(m, m.selItem)
  459.                 END;
  460.                 m.cliItem := -1
  461.             |77:
  462.                 IF ~submenu(m, TRUE) THEN
  463.                         click(m, -(getMainID(m) + 1))
  464.                 END;
  465.                 m.cliItem := -1
  466.             |75:
  467.                 IF m.parent # NIL THEN
  468.                         escape(m)
  469.                 ELSE
  470.                         click(m, -(getMainID(m) - 1))
  471.                 END;
  472.                 m.cliItem := -1
  473.             |1:
  474.                 escape(m)
  475.             ELSE
  476.                     IF getShortcut(key) THEN
  477.                         IF m.parent # NIL THEN
  478.                                 temp := m.parent.tid;
  479.                                 m.parent.tid := 0;
  480.                                 K.ExitID(temp)
  481.                         END;
  482.                         exit(m)
  483.                     END
  484.             END;
  485.             repaint(m)
  486.         |6:
  487.                 K.mouse(msState, x, y);
  488.             IF (0 <= x) & (x < m.width) & (0 <= y) & (y < m.height) THEN
  489.                 m.active := TRUE;
  490.                 m.selItem := (y - TOP) DIV fontHeight;
  491.                 IF 8 IN msState THEN
  492.                     m.cliItem := (y - TOP) DIV fontHeight
  493.                 END;
  494.                 IF 16 IN msState THEN
  495.                     IF m.cliItem = m.selItem THEN
  496.                         click(m, m.cliItem)
  497.                     END;
  498.                     m.cliItem := -1
  499.                 END
  500.             ELSE
  501.                 m.active := FALSE;
  502.                 m.cliItem := -1;
  503.                 IF ({8, 9, 10, 16} * msState # {}) & ~isActive(m.child) THEN
  504.                     exit(m)
  505.                 END
  506.             END;
  507.             repaint(m);
  508.             IF submenu(m, FALSE) THEN END
  509.         END
  510.     END
  511. END window;
  512.  
  513.  
  514. PROCEDURE level (m: tMenu): INTEGER;
  515. VAR
  516.         res: INTEGER;
  517. BEGIN
  518.         res := 0;
  519.         WHILE m.parent # NIL DO
  520.                 INC(res);
  521.                 m := m.parent
  522.         END
  523.         RETURN res
  524. END level;
  525.  
  526.  
  527. PROCEDURE open* (m: tMenu; x, y: INTEGER);
  528. VAR
  529.         L: INTEGER;
  530. BEGIN
  531.     IF m.tid = 0 THEN
  532.         L := level(m);
  533.         IF K.GetThreadSlot(TIDs[L]) = 0 THEN
  534.                 m.winX := x;
  535.                 m.winY := y;
  536.                 SYSTEM.PUT(SYSTEM.ADR(stack[L][LEN(stack[0]) - 1]), m);
  537.             m.tid := K.CreateThread(SYSTEM.ADR(window), stack[L]);
  538.                 TIDs[L] := m.tid
  539.         END
  540.     END
  541. END open;
  542.  
  543.  
  544. PROCEDURE AddMenuItem* (items: List.tList; id: INTEGER; s: ARRAY OF WCHAR);
  545. VAR
  546.     item: tItem;
  547. BEGIN
  548.     NEW(item);
  549.     item.id := id;
  550.     item.text := s;
  551.     item.enabled := TRUE;
  552.     item.delim := FALSE;
  553.     item.child := NIL;
  554.     List.append(items, item);
  555. END AddMenuItem;
  556.  
  557.  
  558. PROCEDURE delimiter* (items: List.tList);
  559. BEGIN
  560.     items.last(tItem).delim := TRUE
  561. END delimiter;
  562.  
  563.  
  564. PROCEDURE child* (items: List.tList; menu: tMenu);
  565. BEGIN
  566.     items.last(tItem).child := menu
  567. END child;
  568.  
  569.  
  570. PROCEDURE getItem (m: tMenu; id: INTEGER): tItem;
  571. VAR
  572.     item: tItem;
  573. BEGIN
  574.     item := m.items.first(tItem);
  575.     WHILE (item # NIL) & (item.id # id) DO
  576.         item := item.next(tItem)
  577.     END
  578.     RETURN item
  579. END getItem;
  580.  
  581.  
  582. PROCEDURE setEnabled* (m: tMenu; id: INTEGER; value: BOOLEAN);
  583. VAR
  584.     item: tItem;
  585. BEGIN
  586.     item := getItem(m, id);
  587.     IF item # NIL THEN
  588.         item.enabled := value
  589.     END
  590. END setEnabled;
  591.  
  592.  
  593. PROCEDURE check* (m: tMenu; id: INTEGER; value: BOOLEAN);
  594. VAR
  595.     item: tItem;
  596. BEGIN
  597.     item := getItem(m, id);
  598.     IF item # NIL THEN
  599.         item.check := ORD(value)
  600.     END
  601. END check;
  602.  
  603.  
  604. PROCEDURE option* (m: tMenu; id: INTEGER; value: BOOLEAN);
  605. VAR
  606.     item: tItem;
  607. BEGIN
  608.     item := getItem(m, id);
  609.     IF item # NIL THEN
  610.         item.check := ORD(value)*2
  611.     END
  612. END option;
  613.  
  614.  
  615. PROCEDURE isEnabled* (m: tMenu; id: INTEGER): BOOLEAN;
  616. VAR
  617.     item: tItem;
  618. BEGIN
  619.     item := getItem(m, id)
  620.     RETURN (item # NIL) & item.enabled
  621. END isEnabled;
  622.  
  623.  
  624. PROCEDURE create* (items: List.tList): tMenu;
  625. VAR
  626.     m: tMenu;
  627.     maxLength: INTEGER;
  628.     item: tItem;
  629. BEGIN
  630.     NEW(m);
  631.     m.tid := 0;
  632.     m.active := FALSE;
  633.     m.parent := NIL;
  634.     m.child := NIL;
  635.     m.mainID := 0;
  636.     m.items  := items;
  637.     maxLength := 0;
  638.     item := items.first(tItem);
  639.     WHILE item # NIL DO
  640.         maxLength := MAX(maxLength, LENGTH(item.text));
  641.         item := item.next(tItem)
  642.     END;
  643.     m.width  := maxLength*fontWidth + LEFT + RIGHT;
  644.     m.height := items.count*fontHeight - 2;
  645.     m.font := G.CreateFont(1, "", {});
  646.     m.canvas := G.CreateCanvas(m.width + 1, m.height + 1);
  647.     G.SetFont(m.canvas, m.font);
  648.     RETURN m
  649. END create;
  650.  
  651.  
  652. PROCEDURE AddShortcut* (key, MenuItem: INTEGER);
  653. VAR
  654.         item: tShortcut;
  655. BEGIN
  656.         NEW(item);
  657.         item.key := key;
  658.         item.MenuItem := MenuItem;
  659.         List.append(shortcuts, item)
  660. END AddShortcut;
  661.  
  662.  
  663. PROCEDURE init* (_resetTimer: tProc);
  664. VAR
  665.         i: INTEGER;
  666. BEGIN
  667.         SetItem(0);
  668.         shortcuts := List.create(NIL);
  669.         resetTimer := _resetTimer;
  670.         _open := open;
  671.         FOR i := 0 TO maxLEVEL DO
  672.                 TIDs[i] := 0
  673.         END
  674. END init;
  675.  
  676.  
  677. END Menu.