1 /*$
2 Copyright (C) 2013-2020 Azel.
3
4 This file is part of AzPainter.
5
6 AzPainter 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 AzPainter 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 this program. If not, see <http://www.gnu.org/licenses/>.
18 $*/
19
20 /*****************************************
21 * mMenuWindow [メニューウィンドウ]
22 *****************************************/
23
24 #include <string.h>
25
26 #include "mDef.h"
27
28 #include "mMenu.h"
29 #include "mMenu_pv.h"
30 #include "mMenuWindow.h"
31 #include "mMenuBar.h"
32
33 #include "mGui.h"
34 #include "mWidget.h"
35 #include "mWindow.h"
36 #include "mEvent.h"
37
38 #include "mSysCol.h"
39 #include "mKeyDef.h"
40 #include "mPixbuf.h"
41 #include "mFont.h"
42
43
44 //------------------------
45
46 #define _FRAME_H 1
47 #define _FRAME_V 1
48 #define _LEFT_SPACE 18 //左枠とテキストの間の余白
49 #define _RIGHT_SPACE 12 //テキストと右枠の間の余白
50 #define _ACCEL_SEPW 11 //テキストとアクセラレータの間の余白
51 #define _SCROLL_H 12 //スクロール部分の高さ
52
53 #define _TIMERID_SUBMENU 0
54 #define _TIMERID_SCROLL 1
55
56 #define _MENUITEM_TOP(p) M_MENUITEM((p)->menu.dat->list.top)
57 #define _IS_ITEMFLAG(p,f) ((p)->item.flags & MMENUITEM_F_ ## f)
58
59 //------------------------
60
61 mMenuItem *__mMenuBar_motion(mMenuBar *mbar,mMenuWindow *topmenu,int x,int y);
62
63 static int _event_handle(mWidget *wg,mEvent *ev);
64 static void _draw_handle(mWidget *wg,mPixbuf *pixbuf);
65
66 //------------------------
67
68 /*
69 * トップのポップアップ表示と同時にポインタをグラブする。
70 * 表示中は常にトップのポップアップがグラブ状態。
71 *
72 * itemCur : 現在選択されているアイテム
73 * itemRet : [top のみ] 戻り値
74 *
75 * winTop : トップのポップアップ
76 * winSub : 表示されているサブメニュー (NULL でなし)
77 * winParent : サブメニュー時、親のポップアップ
78 * winFocus : [top のみ] フォーカスのあるポップアップ
79 * winScroll : [top のみ] スクロール中の場合、スクロール対象のポップアップ
80 *
81 * menubar : [top のみ] メニューバー
82 *
83 */
84
85
86 //==============================
87 // sub : mMenuWindow
88 //==============================
89
90
91 /** メニューウィンドウ破棄 */
92
_destroy_menuwindow(mMenuWindow * menuwin)93 static void _destroy_menuwindow(mMenuWindow *menuwin)
94 {
95 mMenuWindow *p,*sub;
96
97 //下層のサブメニューウィンドウも削除
98
99 for(p = menuwin; p; p = sub)
100 {
101 sub = p->menu.winSub;
102
103 mWidgetDestroy(M_WIDGET(p));
104 }
105 }
106
107 /** ウィンドウサイズセット */
108
_setWindowSize(mMenuWindow * menuwin)109 static void _setWindowSize(mMenuWindow *menuwin)
110 {
111 mMenuItem *pi;
112 int left_w = 0,right_w = 0,h = 0,draw_w = 0,w;
113 mBox box;
114
115 //幅と高さ
116
117 for(pi = _MENUITEM_TOP(menuwin); pi; pi = M_MENUITEM(pi->i.next))
118 {
119 if(left_w < pi->labelLeftW) left_w = pi->labelLeftW;
120 if(right_w < pi->labelRightW) right_w = pi->labelRightW;
121 if(pi->item.draw && draw_w < pi->item.width) draw_w = pi->item.width;
122
123 h += pi->item.height;
124 }
125
126 //
127
128 menuwin->menu.labelLeftMaxW = left_w;
129 menuwin->menu.maxH = h;
130
131 w = left_w + right_w;
132 if(right_w) w += _ACCEL_SEPW;
133
134 if(w) w += _LEFT_SPACE + _RIGHT_SPACE;
135
136 if(w < draw_w) w = draw_w;
137
138 w += _FRAME_H * 2;
139 h += _FRAME_V * 2;
140
141 //高さが画面より大きい場合、スクロールあり
142
143 mGetDesktopWorkBox(&box);
144
145 if(h > box.h)
146 {
147 menuwin->menu.flags |= MMENUWINDOW_F_HAVE_SCROLL;
148
149 h = box.h;
150 }
151
152 //サイズ変更
153
154 mWidgetResize(M_WIDGET(menuwin), w, h);
155 }
156
157 /** アイテムの表示位置取得 */
158
_getItemVisiblePos(mMenuWindow * menuwin,mMenuItem * item,mPoint * pt)159 static void _getItemVisiblePos(mMenuWindow *menuwin,
160 mMenuItem *item,mPoint *pt)
161 {
162 mMenuItem *p;
163 int y;
164
165 y = _FRAME_V;
166
167 if(menuwin->menu.flags & MMENUWINDOW_F_HAVE_SCROLL)
168 y += _SCROLL_H - menuwin->menu.scrY;
169
170 for(p = _MENUITEM_TOP(menuwin); p && p != item;
171 y += p->item.height, p = M_MENUITEM(p->i.next));
172
173 pt->x = 0;
174 pt->y = y;
175 }
176
177 /** MEVENT_MENU_POPUP : ポップアップ表示イベント (直接実行) */
178
_runEventPopup(mMenuWindow * p,int itemid,mBool menubar)179 static void _runEventPopup(mMenuWindow *p,int itemid,mBool menubar)
180 {
181 mEvent ev;
182
183 if(p->menu.widgetNotify)
184 {
185 memset(&ev, 0, sizeof(mEvent));
186
187 ev.widget = p->menu.widgetNotify;
188 ev.type = MEVENT_MENU_POPUP;
189 ev.popup.menu = p->menu.dat;
190 ev.popup.itemID = itemid;
191 ev.popup.bMenuBar = menubar;
192
193 (ev.widget->event)(ev.widget, &ev);
194 }
195 }
196
197 /** ポップアップ終了 [top] */
198
_endPopup(mMenuWindow * p,mMenuItem * ret)199 static void _endPopup(mMenuWindow *p,mMenuItem *ret)
200 {
201 p->menu.itemRet = ret;
202
203 mWidgetUngrabPointer(M_WIDGET(p));
204
205 mAppQuit();
206 }
207
208 /** メニューバーの選択移動
209 *
210 * @param type [0]item [1]item の前 [2]item の次 */
211
_menubar_motion(mMenuWindow * p,mMenuItem * item,int type)212 static void _menubar_motion(mMenuWindow *p,mMenuItem *item,int type)
213 {
214 if(type >= 1)
215 {
216 do
217 {
218 item = (mMenuItem *)((type == 1)? item->i.prev: item->i.next);
219 } while(item && !__mMenuItemIsEnableSubmenu(item));
220 }
221
222 //
223
224 if(item)
225 {
226 p = p->menu.winTop;
227
228 p->menu.flags |= MMENUWINDOW_F_MENUBAR_CHANGE;
229
230 _endPopup(p, item);
231 }
232 }
233
234 /** ポップアップ表示 (トップ) */
235
_showPopupTop(mMenuWindow * p,int rootx,int rooty,uint32_t flags)236 static void _showPopupTop(mMenuWindow *p,int rootx,int rooty,uint32_t flags)
237 {
238 p->menu.winTop = p->menu.winFocus = p;
239
240 _setWindowSize(p);
241
242 //表示
243
244 if(flags & MMENU_POPUP_F_RIGHT)
245 rootx -= p->wg.w;
246
247 mWindowMoveAdjust(M_WINDOW(p), rootx, rooty, TRUE);
248 mWidgetShow(M_WIDGET(p), 1);
249
250 //実行
251
252 mAppRunPopup(M_WINDOW(p));
253 }
254
255 /** ポップアップ表示 (サブメニュー)
256 *
257 * parent のカレントアイテムのサブメニューを表示 */
258
_showPopupSubmenu(mMenuWindow * parent)259 static void _showPopupSubmenu(mMenuWindow *parent)
260 {
261 mMenuWindow *p;
262 mMenuItem *item;
263 mPoint pt;
264
265 item = parent->menu.itemCur;
266
267 //作成
268
269 p = mMenuWindowNew(item->item.submenu);
270 if(!p) return;
271
272 parent->menu.winSub = p;
273
274 p->menu.winTop = parent->menu.winTop;
275 p->menu.winParent = parent;
276 p->menu.widgetNotify = parent->menu.widgetNotify;
277
278 //ポップアップ表示イベント
279
280 _runEventPopup(p, item->item.id, ((p->menu.winTop)->menu.menubar != 0));
281
282 //サイズ
283
284 _setWindowSize(p);
285
286 //位置
287
288 _getItemVisiblePos(parent, item, &pt);
289 mWidgetMapPoint(M_WIDGET(parent), NULL, &pt);
290
291 mWindowMoveAdjust(M_WINDOW(p),
292 pt.x + parent->wg.w - 3, pt.y - _FRAME_V - 1, TRUE);
293
294 //表示
295
296 mWidgetShow(M_WIDGET(p), 1);
297 }
298
299
300 //=============================
301 //
302 //=============================
303
304
305 /** メニューウィンドウ作成 */
306
mMenuWindowNew(mMenu * menu)307 mMenuWindow *mMenuWindowNew(mMenu *menu)
308 {
309 mMenuWindow *p;
310
311 p = (mMenuWindow *)mWindowNew(sizeof(mMenuWindow), NULL,
312 MWINDOW_S_POPUP | MWINDOW_S_NO_IM);
313
314 if(!p) return NULL;
315
316 p->wg.event = _event_handle;
317 p->wg.draw = _draw_handle;
318 p->wg.fEventFilter |= MWIDGET_EVENTFILTER_POINTER | MWIDGET_EVENTFILTER_KEY;
319
320 p->menu.dat = menu;
321
322 //アイテム初期化
323
324 __mMenuInit(menu, mWidgetGetFont(M_WIDGET(p)));
325
326 return p;
327 }
328
329 /** 独立ポップアップ表示
330 *
331 * @param notify イベント通知先ウィジェット (NULL でなし)
332 * @return 選択項目 (NULL でキャンセル) */
333
mMenuWindowShowPopup(mMenuWindow * menuwin,mWidget * notify,int rootx,int rooty,uint32_t flags)334 mMenuItem *mMenuWindowShowPopup(mMenuWindow *menuwin,
335 mWidget *notify,int rootx,int rooty,uint32_t flags)
336 {
337 mMenuItem *item;
338
339 menuwin->menu.widgetNotify = notify;
340
341 _showPopupTop(menuwin, rootx, rooty, flags);
342
343 //----- 終了
344
345 item = menuwin->menu.itemRet;
346
347 //MEVENT_COMMAND
348
349 if(notify && item && !(flags & MMENU_POPUP_F_NOCOMMAND))
350 {
351 mWidgetAppendEvent_command(notify, item->item.id,
352 item->item.param1, MEVENT_COMMAND_BY_MENU);
353 }
354
355 _destroy_menuwindow(menuwin);
356
357 return item;
358 }
359
360 /** メニュバーから表示
361 *
362 * @return カーソル移動による選択アイテム変更があった場合、新しいアイテム。
363 * NULL で終了。 */
364
mMenuWindowShowMenuBar(mMenuWindow * win,mMenuBar * menubar,int rootx,int rooty,int itemid)365 mMenuItem *mMenuWindowShowMenuBar(mMenuWindow *win,
366 mMenuBar *menubar,int rootx,int rooty,int itemid)
367 {
368 mMenuItem *item;
369 int bchange;
370
371 win->menu.menubar = menubar;
372 win->menu.widgetNotify = mWidgetGetNotifyWidget(M_WIDGET(menubar));
373
374 //MEVENT_MENU_POPUP
375
376 _runEventPopup(win, itemid, TRUE);
377
378 //実行
379
380 _showPopupTop(win, rootx - _FRAME_H, rooty, 0);
381
382 //------- 終了
383
384 item = win->menu.itemRet;
385 bchange = win->menu.flags & MMENUWINDOW_F_MENUBAR_CHANGE;
386
387 //MEVENT_COMMAND
388
389 if(item && !bchange)
390 {
391 mWidgetAppendEvent_command(win->menu.widgetNotify,
392 item->item.id, item->item.param1, MEVENT_COMMAND_BY_MENU);
393 }
394
395 //削除
396
397 _destroy_menuwindow(win);
398
399 return (bchange)? item: NULL;
400 }
401
402
403 //=============================
404 // sub
405 //=============================
406
407
408 /** 項目決定 */
409
_enterItem(mMenuWindow * menuwin,mMenuItem * item)410 static void _enterItem(mMenuWindow *menuwin,mMenuItem *item)
411 {
412 if(item && !item->item.submenu && !__mMenuItemIsDisableItem(item))
413 {
414 //自動チェック
415
416 if(_IS_ITEMFLAG(item, AUTOCHECK))
417 {
418 if(_IS_ITEMFLAG(item, RADIO))
419 __mMenuItemCheckRadio(item, TRUE);
420 else
421 item->item.flags ^= MMENUITEM_F_CHECKED;
422 }
423
424 //終了 (top)
425
426 _endPopup(menuwin->menu.winTop, item);
427 }
428 }
429
430 /** カレントアイテム変更 [top/sub]
431 *
432 * @param item NULL で解除
433 * @param scritem スクロールあり時、指定アイテムが見えるようにスクロール */
434
_setCurrentItem(mMenuWindow * menuwin,mMenuItem * item,mBool scritem)435 static void _setCurrentItem(mMenuWindow *menuwin,
436 mMenuItem *item,mBool scritem)
437 {
438 if(menuwin->menu.itemCur == item) return;
439
440 //サブメニュー用タイマー削除
441
442 mWidgetTimerDelete(M_WIDGET(menuwin->menu.winTop), _TIMERID_SUBMENU);
443
444 //現在のサブメニューを削除
445
446 if(menuwin->menu.winSub)
447 {
448 _destroy_menuwindow(menuwin->menu.winSub);
449
450 menuwin->menu.winSub = NULL;
451 }
452
453 //変更
454
455 menuwin->menu.itemCur = item;
456
457 mWidgetUpdate(M_WIDGET(menuwin));
458
459 //
460
461 if(item)
462 {
463 //項目が見える位置までスクロール
464
465 if(scritem && (menuwin->menu.flags & MMENUWINDOW_F_HAVE_SCROLL))
466 {
467 mPoint pt;
468
469 _getItemVisiblePos(menuwin, item, &pt);
470
471 pt.y -= _FRAME_V + _SCROLL_H;
472
473 if(pt.y < 0)
474 menuwin->menu.scrY += pt.y;
475 else if(pt.y + item->item.height > menuwin->wg.h - _FRAME_V * 2 - _SCROLL_H * 2)
476 {
477 menuwin->menu.scrY += pt.y + item->item.height -
478 (menuwin->wg.h - _FRAME_V * 2 - _SCROLL_H * 2);
479 }
480 }
481
482 //サブメニューの場合、タイマーで遅延して表示
483
484 if(__mMenuItemIsEnableSubmenu(item))
485 {
486 mWidgetTimerAdd(M_WIDGET(menuwin->menu.winTop),
487 _TIMERID_SUBMENU, 200, (intptr_t)menuwin);
488 }
489 }
490 }
491
492 /** スクロール処理 */
493
_scroll(mMenuWindow * menuwin)494 static void _scroll(mMenuWindow *menuwin)
495 {
496 int scr = menuwin->menu.scrY;
497
498 if(menuwin->menu.flags & MMENUWINDOW_F_SCROLL_DOWN)
499 {
500 scr += 20;
501
502 if(scr + menuwin->wg.h - _FRAME_V * 2 - _SCROLL_H * 2 > menuwin->menu.maxH)
503 scr = menuwin->menu.maxH - (menuwin->wg.h - _FRAME_V * 2 - _SCROLL_H * 2);
504 }
505 else
506 {
507 scr -= 20;
508 if(scr < 0) scr = 0;
509 }
510
511 if(scr != menuwin->menu.scrY)
512 {
513 menuwin->menu.scrY = scr;
514 mWidgetUpdate(M_WIDGET(menuwin));
515 }
516 }
517
518
519 //=============================
520 // キーボード関連
521 //=============================
522
523
524 /** カレント位置を上下に移動 */
525
_moveCurUpdown(mMenuWindow * menuwin,int up)526 static void _moveCurUpdown(mMenuWindow *menuwin,int up)
527 {
528 mMenuItem *p;
529
530 p = menuwin->menu.itemCur;
531
532 if(up)
533 {
534 //上
535
536 if(p)
537 p = M_MENUITEM(p->i.prev);
538 else
539 p = _MENUITEM_TOP(menuwin);
540
541 for(; p && __mMenuItemIsDisableItem(p); p = M_MENUITEM(p->i.prev));
542 }
543 else
544 {
545 //下
546
547 if(p)
548 p = M_MENUITEM(p->i.next);
549 else
550 p = _MENUITEM_TOP(menuwin);
551
552 for(; p && __mMenuItemIsDisableItem(p); p = M_MENUITEM(p->i.next));
553 }
554
555 if(p) _setCurrentItem(menuwin, p, TRUE);
556 }
557
558 /** キー押し [top/sub] */
559
_event_keydown(mMenuWindow * menuwin,uint32_t key)560 static void _event_keydown(mMenuWindow *menuwin,uint32_t key)
561 {
562 mMenuItem *item;
563 mMenuWindow *win;
564 mMenuBar *mbar;
565
566 //ホットキー
567
568 if((key >= '0' && key <= '9') || (key >= 'A' && key <= 'Z'))
569 {
570 item = __mMenuFindByHotkey(menuwin->menu.dat, key);
571
572 if(item)
573 {
574 if(item->item.submenu)
575 _setCurrentItem(menuwin, item, TRUE);
576 else
577 _enterItem(menuwin, item);
578 }
579
580 return;
581 }
582
583 //
584
585 switch(key)
586 {
587 //上
588 case MKEY_UP:
589 _moveCurUpdown(menuwin, TRUE);
590 break;
591 //下
592 case MKEY_DOWN:
593 _moveCurUpdown(menuwin, FALSE);
594 break;
595 //左
596 case MKEY_LEFT:
597 if(menuwin->menu.winParent)
598 {
599 //親へ戻る
600
601 (menuwin->menu.winTop)->menu.winFocus = menuwin->menu.winParent;
602
603 _setCurrentItem(menuwin, NULL, FALSE);
604 }
605 else
606 {
607 //メニューバーの一つ左へ
608
609 mbar = (menuwin->menu.winTop)->menu.menubar;
610
611 if(mbar)
612 _menubar_motion(menuwin, mbar->mb.itemCur, 1);
613 }
614 break;
615 //右
616 case MKEY_RIGHT:
617 win = menuwin->menu.winSub;
618
619 if(win)
620 {
621 //サブメニューへ
622
623 (menuwin->menu.winTop)->menu.winFocus = win;
624
625 _setCurrentItem(win,
626 _MENUITEM_TOP(win), FALSE);
627 }
628 else
629 {
630 //メニューバーの一つ右へ
631
632 mbar = (menuwin->menu.winTop)->menu.menubar;
633
634 if(mbar)
635 _menubar_motion(menuwin, mbar->mb.itemCur, 2);
636 }
637 break;
638 //SPACE/ENTER
639 case MKEY_SPACE:
640 case MKEY_ENTER:
641 _enterItem(menuwin, menuwin->menu.itemCur);
642 break;
643 }
644 }
645
646
647 //=============================
648 // ポインタ関連
649 //=============================
650
651
652 /** 指定位置下のウィンドウ取得 [top]
653 *
654 * @param pt 相対座標が入る (NULL 可) */
655
_getPopupByPos(mMenuWindow * topwin,int rootx,int rooty,mPoint * pt_rel)656 static mMenuWindow *_getPopupByPos(mMenuWindow *topwin,
657 int rootx,int rooty,mPoint *pt_rel)
658 {
659 mMenuWindow *win,*win_ret = NULL;
660 mPoint pt,pt_ret;
661
662 win = topwin;
663
664 for(win = topwin; win; win = win->menu.winSub)
665 {
666 pt.x = rootx, pt.y = rooty;
667 mWidgetMapPoint(NULL, M_WIDGET(win), &pt);
668
669 if(mWidgetIsContain(M_WIDGET(win), pt.x, pt.y))
670 {
671 win_ret = win;
672 pt_ret = pt;
673 }
674 }
675
676 if(win_ret && pt_rel)
677 {
678 pt_rel->x = pt_ret.x;
679 pt_rel->y = pt_ret.y;
680 }
681
682 return win_ret;
683 }
684
685 /** カーソル位置からアイテム取得 */
686
_getItemByPos(mMenuWindow * win,int y)687 mMenuItem *_getItemByPos(mMenuWindow *win,int y)
688 {
689 mMenuItem *p;
690 int topy,bottom;
691
692 topy = _FRAME_V;
693 bottom = win->wg.h - _FRAME_V;
694
695 if(win->menu.flags & MMENUWINDOW_F_HAVE_SCROLL)
696 {
697 if(y < _FRAME_V + _SCROLL_H) return NULL;
698
699 topy += _SCROLL_H - win->menu.scrY;
700 bottom -= _SCROLL_H;
701 }
702
703 //
704
705 for(p = _MENUITEM_TOP(win); p; p = M_MENUITEM(p->i.next))
706 {
707 if(y < topy || topy >= bottom) break;
708
709 if(topy <= y && y < topy + p->item.height)
710 return p;
711
712 topy += p->item.height;
713 }
714
715 return NULL;
716 }
717
718 /** カーソル位置がスクロールボタン上か */
719
_judgePosScroll(mMenuWindow * p,int rooty)720 static mBool _judgePosScroll(mMenuWindow *p,int rooty)
721 {
722 mPoint pt;
723
724 if(p->menu.flags & MMENUWINDOW_F_HAVE_SCROLL)
725 {
726 pt.x = 0, pt.y = rooty;
727
728 mWidgetMapPoint(NULL, M_WIDGET(p), &pt);
729
730 if(pt.y < _FRAME_V + _SCROLL_H)
731 {
732 p->menu.flags &= ~MMENUWINDOW_F_SCROLL_DOWN;
733 return TRUE;
734 }
735 else if(pt.y >= p->wg.h - _FRAME_V - _SCROLL_H)
736 {
737 p->menu.flags |= MMENUWINDOW_F_SCROLL_DOWN;
738 return TRUE;
739 }
740 }
741
742 return FALSE;
743 }
744
745 /** ボタン押し [top] */
746
_event_btt_press(mMenuWindow * topwin,mEvent * ev)747 static void _event_btt_press(mMenuWindow *topwin,mEvent *ev)
748 {
749 mMenuWindow *win;
750
751 win = _getPopupByPos(topwin, ev->pt.rootx, ev->pt.rooty, NULL);
752
753 if(!win)
754 //ポップアップ部分以外のクリックで終了
755 _endPopup(topwin, NULL);
756 else if(ev->pt.btt == M_BTT_LEFT)
757 {
758 //スクロール部分 -> スクロール開始
759
760 if(_judgePosScroll(win, ev->pt.rooty))
761 {
762 mWidgetTimerAdd(M_WIDGET(topwin), _TIMERID_SCROLL, 60, 0);
763
764 topwin->menu.winScroll = win;
765 }
766 }
767 }
768
769 /** ボタン離し [top] */
770
_event_btt_release(mMenuWindow * topwin,mEvent * ev)771 static void _event_btt_release(mMenuWindow *topwin,mEvent *ev)
772 {
773 mMenuWindow *win;
774
775 if(ev->pt.btt == M_BTT_LEFT)
776 {
777 if(topwin->menu.winScroll)
778 {
779 //スクロール中
780
781 topwin->menu.winScroll = NULL;
782
783 mWidgetTimerDelete(M_WIDGET(topwin), _TIMERID_SCROLL);
784 }
785 else
786 {
787 //項目決定 (離した時点でのアイテム)
788
789 win = _getPopupByPos(topwin, ev->pt.rootx, ev->pt.rooty, NULL);
790
791 if(win && win->menu.itemCur)
792 _enterItem(topwin, win->menu.itemCur);
793 }
794 }
795 }
796
797 /** ポインタ移動 [top] */
798
_event_motion(mMenuWindow * topwin,mEvent * ev)799 static void _event_motion(mMenuWindow *topwin,mEvent *ev)
800 {
801 mMenuWindow *win;
802 mPoint pt;
803
804 //スクロール中は除く
805
806 if(topwin->menu.winScroll) return;
807
808 //
809
810 win = _getPopupByPos(topwin, ev->pt.rootx, ev->pt.rooty, &pt);
811
812 if(win)
813 {
814 /* ポップアップ内
815 * - 無効なアイテムもカレントとして選択させる(表示上は選択されない) */
816
817 topwin->menu.winFocus = win;
818
819 _setCurrentItem(win, _getItemByPos(win, pt.y), FALSE);
820 }
821 else
822 {
823 /* ポップアップ外
824 * - ボタン押されている間でも選択解除する */
825
826 _setCurrentItem(topwin->menu.winFocus, NULL, FALSE);
827
828 /* メニューバーの別の項目の上に乗った場合は、一度終了させて新たに表示する */
829
830 if(topwin->menu.menubar)
831 {
832 mMenuItem *item;
833
834 item = __mMenuBar_motion(topwin->menu.menubar, topwin,
835 ev->pt.x, ev->pt.y);
836
837 if(item)
838 _menubar_motion(topwin, item, 0);
839 }
840 }
841 }
842
843
844 //=============================
845 // ハンドラ
846 //=============================
847
848
849 /** イベント */
850
_event_handle(mWidget * wg,mEvent * ev)851 int _event_handle(mWidget *wg,mEvent *ev)
852 {
853 mMenuWindow *menuwin = M_MENUWINDOW(wg);
854
855 switch(ev->type)
856 {
857 //[!] 常にトップがグラブされているので、トップにしか来ない
858 case MEVENT_POINTER:
859 if(ev->pt.type == MEVENT_POINTER_TYPE_MOTION)
860 _event_motion(menuwin, ev);
861 else if(ev->pt.type == MEVENT_POINTER_TYPE_PRESS)
862 _event_btt_press(menuwin, ev);
863 else if(ev->pt.type == MEVENT_POINTER_TYPE_RELEASE)
864 _event_btt_release(menuwin, ev);
865 break;
866
867 //キー押し [top]
868 case MEVENT_KEYDOWN:
869 if(ev->key.code == MKEY_ESCAPE)
870 _endPopup(menuwin, NULL);
871 else if(menuwin->menu.winFocus)
872 _event_keydown(menuwin->menu.winFocus, ev->key.code);
873 break;
874
875 //タイマー [top]
876 case MEVENT_TIMER:
877 if(ev->timer.id == _TIMERID_SCROLL)
878 {
879 //スクロール中
880
881 if(menuwin->menu.winScroll)
882 _scroll(menuwin->menu.winScroll);
883 }
884 else if(ev->timer.id == _TIMERID_SUBMENU)
885 {
886 //サブメニュー表示
887
888 mWidgetTimerDelete(wg, _TIMERID_SUBMENU);
889
890 _showPopupSubmenu(M_MENUWINDOW(ev->timer.param));
891 }
892 break;
893
894 //表示 (トップの場合、グラブ開始)
895 case MEVENT_MAP:
896 if(!menuwin->menu.winParent)
897 mWidgetGrabPointer(wg);
898 break;
899 //グラブ解放時など
900 case MEVENT_FOCUS:
901 if(ev->focus.bOut)
902 _endPopup(menuwin->menu.winTop, NULL);
903 break;
904 }
905
906 return 1;
907 }
908
909 /** 描画 */
910
_draw_handle(mWidget * wg,mPixbuf * pixbuf)911 void _draw_handle(mWidget *wg,mPixbuf *pixbuf)
912 {
913 mMenuWindow *menuwin;
914 mFont *font;
915 mMenuItem *item;
916 int y,itemh;
917 uint32_t col;
918 mBool bsel,bscr,bDisable;
919 mMenuItemDraw drawi;
920
921 menuwin = M_MENUWINDOW(wg);
922 font = mWidgetGetFont(wg);
923
924 bscr = ((menuwin->menu.flags & MMENUWINDOW_F_HAVE_SCROLL) != 0);
925
926 //枠
927
928 mPixbufBox(pixbuf, 0, 0, wg->w, wg->h, MSYSCOL(MENU_FRAME));
929
930 //背景
931
932 mPixbufFillBox(pixbuf, 1, 1, wg->w - 2, wg->h - 2, MSYSCOL(MENU_FACE));
933
934 //スクロール部分
935
936 if(bscr)
937 {
938 mPixbufDrawArrowUp(pixbuf,
939 wg->w >> 1, _FRAME_V + _SCROLL_H / 2,
940 (menuwin->menu.scrY == 0)? MSYSCOL(MENU_TEXT_DISABLE): MSYSCOL(MENU_TEXT));
941
942 mPixbufDrawArrowDown(pixbuf,
943 wg->w >> 1, wg->h - _FRAME_V - _SCROLL_H + _SCROLL_H / 2,
944 (menuwin->menu.scrY >= menuwin->menu.maxH - (wg->h - _FRAME_V * 2 - _SCROLL_H * 2))
945 ? MSYSCOL(MENU_TEXT_DISABLE): MSYSCOL(MENU_TEXT));
946 }
947
948 //--------- 項目
949
950 y = _FRAME_V;
951
952 if(bscr)
953 {
954 y += _SCROLL_H - menuwin->menu.scrY;
955
956 //クリッピング
957 mPixbufSetClipBox_d(pixbuf,
958 0, _FRAME_V + _SCROLL_H,
959 wg->w, wg->h - _FRAME_V * 2 - _SCROLL_H * 2);
960 }
961
962 //
963
964 for(item = _MENUITEM_TOP(menuwin); item;
965 y += item->item.height, item = M_MENUITEM(item->i.next))
966 {
967 itemh = item->item.height;
968
969 if(bscr)
970 {
971 if(y + itemh <= _FRAME_V + _SCROLL_H) continue;
972 if(y >= wg->h - _FRAME_V - _SCROLL_H) break;
973 }
974
975 bsel = (item == menuwin->menu.itemCur);
976
977 if(_IS_ITEMFLAG(item, SEP))
978 {
979 //セパレータ
980
981 mPixbufLineH(pixbuf,
982 _FRAME_H + 1, y + itemh / 2,
983 wg->w - _FRAME_H * 2 - 2, MSYSCOL(MENU_SEP));
984 }
985 else if(item->item.draw)
986 {
987 //独自描画
988
989 drawi.flags = 0;
990 if(bsel) drawi.flags |= MMENUITEM_DRAW_F_SELECTED;
991
992 drawi.box.x = _FRAME_H;
993 drawi.box.y = y;
994 drawi.box.w = wg->w - _FRAME_H * 2;
995 drawi.box.h = itemh;
996
997 (item->item.draw)(pixbuf, &item->item, &drawi);
998 }
999 else if(item->labelsrc)
1000 {
1001 //------- 通常・ラベルあり
1002
1003 bDisable = _IS_ITEMFLAG(item, DISABLE);
1004
1005 //カーソル
1006
1007 if(bsel && !bDisable)
1008 {
1009 mPixbufFillBox(pixbuf,
1010 _FRAME_H, y, wg->w - _FRAME_H * 2, itemh,
1011 MSYSCOL(MENU_SELECT));
1012 }
1013
1014 //テキスト色 (pix)
1015
1016 col = (bDisable)? MSYSCOL(MENU_TEXT_DISABLE): MSYSCOL(MENU_TEXT);
1017
1018 //チェック
1019
1020 if(_IS_ITEMFLAG(item, CHECKED))
1021 {
1022 if(_IS_ITEMFLAG(item, RADIO))
1023 {
1024 mPixbufDrawMenuRadio(pixbuf,
1025 _FRAME_H + 6, y + (itemh - 5) / 2, col);
1026 }
1027 else
1028 {
1029 mPixbufDrawMenuChecked(pixbuf,
1030 _FRAME_H + 5, y + (itemh - 7) / 2, col);
1031 }
1032 }
1033
1034 //サブメニュー矢印
1035
1036 if(item->item.submenu)
1037 {
1038 mPixbufDrawArrowRight(pixbuf,
1039 wg->w - _FRAME_H - 6, y + (itemh >> 1), col);
1040 }
1041
1042 //ラベル
1043
1044 mFontDrawTextHotkey(font, pixbuf,
1045 _FRAME_H + _LEFT_SPACE,
1046 y + ((itemh - font->height) >> 1),
1047 item->labelsrc, -1,
1048 (bDisable)? MSYSCOL_RGB(MENU_TEXT_DISABLE): MSYSCOL_RGB(MENU_TEXT));
1049
1050 //ショートカットキー文字列
1051
1052 if(item->sctext)
1053 {
1054 mFontDrawText(font, pixbuf,
1055 wg->w - _FRAME_H - _RIGHT_SPACE - item->labelRightW,
1056 y + ((itemh - font->height) >> 1),
1057 item->sctext, -1,
1058 (bsel && !bDisable)? MSYSCOL_RGB(MENU_TEXT): MSYSCOL_RGB(MENU_TEXT_SHORTCUT));
1059 }
1060
1061 }
1062 }
1063 }
1064