Subversion Repositories Kolibri OS

Rev

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