Subversion Repositories Kolibri OS

Rev

Rev 8762 | Rev 9175 | 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;
  24.  
  25. CONST
  26.     fontHeight = 22;
  27.     fontWidth = 8;
  28.  
  29.     RIGHT = 16;
  30.     LEFT = 16;
  31.     TOP = 1;
  32.  
  33.     maxLEVEL = 1;
  34.  
  35.     backColor = 0F0F0F0H;
  36.     foreColor = 0;
  37.     selBackColor = 091C9F7H;
  38.     selForeColor = 0;
  39.     disBackColor = backColor;
  40.     disForeColor = 808080H;
  41.     disSelBackColor = 0E4E4E4H;
  42.     disSelForeColor = disForeColor;
  43.  
  44.  
  45. TYPE
  46.  
  47.     tMenu* = POINTER TO RECORD
  48.         tid*: INTEGER;
  49.         active*: BOOLEAN;
  50.         parent*, child: tMenu;
  51.         winX, winY, width*, height*: INTEGER;
  52.         selItem, cliItem: INTEGER;
  53.  
  54.         font: G.tFont;
  55.         canvas: G.tCanvas;
  56.  
  57.         items: List.tList;
  58.         click: PROCEDURE (menu: tMenu; id: INTEGER);
  59.         key: PROCEDURE (menu: tMenu; key: INTEGER): BOOLEAN
  60.     END;
  61.  
  62.     tItem* = POINTER TO RECORD (List.tItem)
  63.         id*, check: INTEGER;
  64.         text: ARRAY 32 OF WCHAR;
  65.         enabled, delim: BOOLEAN;
  66.         child: tMenu
  67.     END;
  68.  
  69.     tClick = PROCEDURE (menu: tMenu; id: INTEGER);
  70.     tKey = PROCEDURE (menu: tMenu; key: INTEGER): BOOLEAN;
  71.     tProc = PROCEDURE;
  72.  
  73. VAR
  74.     stack: ARRAY maxLEVEL + 1, 250000 OF INTEGER;
  75.     TIDs: ARRAY maxLEVEL + 1 OF INTEGER;
  76.     resetTimer: tProc;
  77.     _open: PROCEDURE (m: tMenu; x, y: INTEGER);
  78.     redraw*: BOOLEAN;
  79.  
  80.  
  81. PROCEDURE isSender* (tid: INTEGER): BOOLEAN;
  82. VAR
  83.         i: INTEGER;
  84. BEGIN
  85.         i := 0;
  86.         WHILE (i <= maxLEVEL) & (TIDs[i] # tid) DO
  87.                 INC(i)
  88.         END
  89.         RETURN i <= maxLEVEL
  90. END isSender;
  91.  
  92.  
  93. PROCEDURE exit (m: tMenu);
  94. BEGIN
  95.     m.tid := 0;
  96.     m.active := FALSE;
  97.     resetTimer;
  98.     K.Exit
  99. END exit;
  100.  
  101.  
  102. PROCEDURE repaint (m: tMenu);
  103. VAR
  104.     y, i, X, Y1, Y2: INTEGER;
  105.     item: tItem;
  106.     BkColor, TextColor: INTEGER;
  107.     canvas: G.tCanvas;
  108.  
  109. BEGIN
  110.     canvas := m.canvas;
  111.     G.SetColor(canvas, backColor);
  112.     G.clear(canvas);
  113.     G.SetColor(canvas, ORD((-BITS(backColor))*{0..23}) );
  114.     G.Rect(canvas, 0, 0, m.width, m.height);
  115.     y := TOP;
  116.     i := 0;
  117.     item := m.items.first(tItem);
  118.     WHILE item # NIL DO
  119.         IF item.enabled THEN
  120.             IF i # m.selItem THEN
  121.                 BkColor := backColor;
  122.                 TextColor := foreColor
  123.             ELSE
  124.                 BkColor := selBackColor;
  125.                 TextColor := selForeColor
  126.             END
  127.         ELSE
  128.             IF i # m.selItem THEN
  129.                 BkColor := disBackColor;
  130.                 TextColor := disForeColor
  131.             ELSE
  132.                 BkColor := disSelBackColor;
  133.                 TextColor := disSelForeColor
  134.             END
  135.         END;
  136.         G.SetColor(canvas, BkColor);
  137.         G.FillRect(canvas, 1, y, m.width - 1, y + fontHeight - 4);
  138.         G.SetTextColor(canvas, TextColor);
  139.         G.SetBkColor(canvas, BkColor);
  140.         G.TextOut2(canvas, LEFT, y + (fontHeight - 16) DIV 2 - 2, item.text, LENGTH(item.text));
  141.  
  142.         G.SetColor(canvas, TextColor);
  143.         IF item.check = 1 THEN
  144.             G.DLine(canvas, 4, 7, y + (fontHeight - 16) DIV 2 + 5, -1);
  145.             G.DLine(canvas, 4, 7, y + (fontHeight - 16) DIV 2 + 6, -1);
  146.             G.DLine(canvas, 7, 12, y + (fontHeight - 16) DIV 2 + 8, 1);
  147.             G.DLine(canvas, 7, 12, y + (fontHeight - 16) DIV 2 + 9, 1);
  148.         ELSIF item.check = 2 THEN
  149.             G.FillRect(canvas, 6, y + fontHeight DIV 2 - 4, 10, y + fontHeight DIV 2)
  150.         END;
  151.  
  152.         IF item.child # NIL THEN
  153.             X := m.width - 9;
  154.             Y1 := y + (fontHeight - 16) DIV 2 + 2;
  155.             Y2 := Y1 + 8;
  156.                 G.Triangle(canvas, X, Y1, X, Y2, G.triRight)
  157.         END;
  158.  
  159.         INC(y, fontHeight);
  160.         IF item.delim THEN
  161.             G.SetColor(canvas, ORD((-BITS(backColor))*{0..23}));
  162.             G.HLine(canvas, y - 2, 1, m.width - 1)
  163.         END;
  164.         INC(i);
  165.         item := item.next(tItem)
  166.     END;
  167.     G.DrawCanvas(canvas, 0, 0)
  168. END repaint;
  169.  
  170.  
  171. PROCEDURE draw_window (m: tMenu);
  172. BEGIN
  173.     K.BeginDraw;
  174.     K.CreateWindow(m.winX, m.winY, m.width, m.height, 0, 61H, 0, 1, "");
  175.     repaint(m);
  176.     K.EndDraw
  177. END draw_window;
  178.  
  179.  
  180. PROCEDURE mouse (m: tMenu; VAR x, y: INTEGER);
  181. VAR
  182.     mouseX, mouseY: INTEGER;
  183. BEGIN
  184.     K.MousePos(mouseX, mouseY);
  185.     x := mouseX - m.winX;
  186.     y := mouseY - m.winY;
  187. END mouse;
  188.  
  189.  
  190. PROCEDURE close* (m: tMenu);
  191. BEGIN
  192.     IF (m # NIL) & (m.tid # 0) THEN
  193.         IF m.child # NIL THEN
  194.                 close(m.child);
  195.                 m.child := NIL
  196.         END;
  197.         K.ExitID(m.tid);
  198.         m.tid := 0;
  199.         m.active := FALSE
  200.     END
  201. END close;
  202.  
  203.  
  204. PROCEDURE click (m: tMenu; i: INTEGER);
  205. VAR
  206.     item: List.tItem;
  207.     p: tMenu;
  208. BEGIN
  209.     item := List.getItem(m.items, i);
  210.     IF (item # NIL) & item(tItem).enabled & (item(tItem).child = NIL) THEN
  211.         m.click(m, item(tItem).id);
  212.         p := m.parent;
  213.         WHILE p # NIL DO
  214.                 p.child := NIL;
  215.                 close(p);
  216.                 p := p.parent
  217.         END;
  218.         redraw := TRUE;
  219.         exit(m)
  220.     END
  221. END click;
  222.  
  223.  
  224. PROCEDURE opened* (m: tMenu): BOOLEAN;
  225.     RETURN m.tid # 0
  226. END opened;
  227.  
  228.  
  229. PROCEDURE isActive (m: tMenu): BOOLEAN;
  230.         RETURN (m # NIL) & ((m.tid # 0) & m.active OR isActive(m.child))
  231. END isActive;
  232.  
  233.  
  234. PROCEDURE closeChild (m: tMenu);
  235. BEGIN
  236.         IF m.child # NIL THEN
  237.                 redraw := FALSE;
  238.                 close(m.child);
  239.                 m.child := NIL
  240.         END
  241. END closeChild;
  242.  
  243.  
  244. PROCEDURE submenu (m: tMenu);
  245. VAR
  246.         item: List.tItem;
  247. BEGIN
  248.     item := List.getItem(m.items, m.selItem);
  249.     IF (item # NIL) & item(tItem).enabled & (item(tItem).child # NIL) THEN
  250.         IF ~opened(item(tItem).child) THEN
  251.                 closeChild(m);
  252.                 _open(item(tItem).child, m.winX + m.width - 2, m.winY + m.selItem*fontHeight);
  253.                 m.child := item(tItem).child
  254.         END
  255.     ELSE
  256.         closeChild(m)
  257.     END
  258. END submenu;
  259.  
  260.  
  261. PROCEDURE [stdcall] window (m: tMenu);
  262. VAR
  263.     x, y: INTEGER;
  264.     key: INTEGER;
  265.     msState: SET;
  266. BEGIN
  267.     m.selItem := -1;
  268.     m.cliItem := -1;
  269.     K.SetEventsMask({0, 1, 5});
  270.     WHILE TRUE DO
  271.         CASE K.WaitForEvent() OF
  272.         |1:
  273.             draw_window(m)
  274.         |2:
  275.             key := K.GetKey();
  276.             IF key DIV 65536 = 72 THEN
  277.                 DEC(m.selItem);
  278.                 IF m.selItem < 0 THEN
  279.                     m.selItem := 0
  280.                 END
  281.             ELSIF key DIV 65536 = 80 THEN
  282.                 INC(m.selItem);
  283.                 IF m.selItem >= m.items.count THEN
  284.                     m.selItem := m.items.count - 1
  285.                 END
  286.             ELSIF key DIV 65536 = 28 THEN
  287.                 IF m.selItem >= 0 THEN
  288.                     click(m, m.selItem)
  289.                 END;
  290.                 m.cliItem := -1
  291.             ELSIF key DIV 65536 = 77 THEN
  292.                 submenu(m)
  293.             ELSIF key DIV 65536 = 75 THEN
  294.                 IF m.parent # NIL THEN
  295.                         exit(m)
  296.                 END
  297.             ELSE
  298.                 IF m.key(m, key) THEN
  299.                     exit(m)
  300.                 END
  301.             END;
  302.             repaint(m)
  303.         |6:
  304.             msState := K.MouseState();
  305.             mouse(m, x, y);
  306.             IF (0 <= x) & (x < m.width) & (0 <= y) & (y < m.height) THEN
  307.                 m.active := TRUE;
  308.                 m.selItem := (y - TOP) DIV fontHeight;
  309.                 IF 8 IN msState THEN
  310.                     m.cliItem := (y - TOP) DIV fontHeight
  311.                 END;
  312.                 IF 16 IN msState THEN
  313.                     IF m.cliItem = m.selItem THEN
  314.                         click(m, m.cliItem)
  315.                     END;
  316.                     m.cliItem := -1
  317.                 END
  318.             ELSE
  319.                 m.active := FALSE;
  320.                 m.cliItem := -1;
  321.                 IF ({8, 9, 10, 16} * msState # {}) & ~isActive(m.child) THEN
  322.                     exit(m)
  323.                 END
  324.             END;
  325.             repaint(m);
  326.             submenu(m)
  327.         END
  328.     END
  329. END window;
  330.  
  331.  
  332. PROCEDURE level (m: tMenu): INTEGER;
  333. VAR
  334.         res: INTEGER;
  335. BEGIN
  336.         res := 0;
  337.         WHILE m.parent # NIL DO
  338.                 INC(res);
  339.                 m := m.parent
  340.         END
  341.         RETURN res
  342. END level;
  343.  
  344.  
  345. PROCEDURE open* (m: tMenu; x, y: INTEGER);
  346. VAR
  347.         L: INTEGER;
  348. BEGIN
  349.     IF m.tid = 0 THEN
  350.         m.winX := x;
  351.         m.winY := y;
  352.         L := level(m);
  353.         SYSTEM.PUT(SYSTEM.ADR(stack[L][LEN(stack[0]) - 1]), m);
  354.         m.tid := K.CreateThread(SYSTEM.ADR(window), stack[L]);
  355.         TIDs[L] := m.tid
  356.     END
  357. END open;
  358.  
  359.  
  360. PROCEDURE AddMenuItem* (items: List.tList; id: INTEGER; s: ARRAY OF WCHAR);
  361. VAR
  362.     item: tItem;
  363. BEGIN
  364.     NEW(item);
  365.     item.id := id;
  366.     item.text := s;
  367.     item.enabled := TRUE;
  368.     item.delim := FALSE;
  369.     item.child := NIL;
  370.     List.append(items, item);
  371. END AddMenuItem;
  372.  
  373.  
  374. PROCEDURE delimiter* (items: List.tList);
  375. BEGIN
  376.     items.last(tItem).delim := TRUE
  377. END delimiter;
  378.  
  379.  
  380. PROCEDURE child* (items: List.tList; menu: tMenu);
  381. BEGIN
  382.     items.last(tItem).child := menu
  383. END child;
  384.  
  385.  
  386. PROCEDURE getItem (m: tMenu; id: INTEGER): tItem;
  387. VAR
  388.     item: tItem;
  389. BEGIN
  390.     item := m.items.first(tItem);
  391.     WHILE (item # NIL) & (item.id # id) DO
  392.         item := item.next(tItem)
  393.     END
  394.     RETURN item
  395. END getItem;
  396.  
  397.  
  398. PROCEDURE setEnabled* (m: tMenu; id: INTEGER; value: BOOLEAN);
  399. VAR
  400.     item: tItem;
  401. BEGIN
  402.     item := getItem(m, id);
  403.     IF item # NIL THEN
  404.         item.enabled := value
  405.     END
  406. END setEnabled;
  407.  
  408.  
  409. PROCEDURE setCheck* (m: tMenu; id: INTEGER; value: INTEGER);
  410. VAR
  411.     item: tItem;
  412. BEGIN
  413.     item := getItem(m, id);
  414.     IF item # NIL THEN
  415.         item.check := value
  416.     END
  417. END setCheck;
  418.  
  419.  
  420. PROCEDURE isEnabled* (m: tMenu; id: INTEGER): BOOLEAN;
  421. VAR
  422.     item: tItem;
  423. BEGIN
  424.     item := getItem(m, id)
  425.     RETURN (item # NIL) & item.enabled
  426. END isEnabled;
  427.  
  428.  
  429. PROCEDURE create* (items: List.tList; click: tClick; key: tKey): tMenu;
  430. VAR
  431.     m: tMenu;
  432.     maxLength: INTEGER;
  433.     item: tItem;
  434. BEGIN
  435.     NEW(m);
  436.     m.tid := 0;
  437.     m.active := FALSE;
  438.     m.parent := NIL;
  439.     m.child := NIL;
  440.     m.items  := items;
  441.     m.click := click;
  442.     m.key := key;
  443.     maxLength := 0;
  444.     item := items.first(tItem);
  445.     WHILE item # NIL DO
  446.         maxLength := MAX(maxLength, LENGTH(item.text));
  447.         item := item.next(tItem)
  448.     END;
  449.     m.width  := maxLength*fontWidth + LEFT + RIGHT;
  450.     m.height := items.count*fontHeight - 2;
  451.     m.font := G.CreateFont(1, "", {});
  452.     m.canvas := G.CreateCanvas(m.width + 1, m.height + 1);
  453.     G.SetFont(m.canvas, m.font);
  454.     RETURN m
  455. END create;
  456.  
  457.  
  458. PROCEDURE Redraw*;
  459. BEGIN
  460.         redraw := TRUE
  461. END Redraw;
  462.  
  463.  
  464. PROCEDURE init* (_resetTimer: tProc);
  465. VAR
  466.         i: INTEGER;
  467. BEGIN
  468.         Redraw;
  469.         resetTimer := _resetTimer;
  470.         _open := open;
  471.         FOR i := 0 TO maxLEVEL DO
  472.                 TIDs[i] := 0
  473.         END
  474. END init;
  475.  
  476.  
  477. END Menu.