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