Subversion Repositories Kolibri OS

Rev

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