Subversion Repositories Kolibri OS

Rev

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