Subversion Repositories Kolibri OS

Rev

Rev 9648 | Rev 9668 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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