Subversion Repositories Kolibri OS

Rev

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

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