1 /* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX)
2 Copyright (C) 2006 and later, Cockos, Inc.
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
12 1. The origin of this software must not be misrepresented; you must not
13 claim that you wrote the original software. If you use this software
14 in a product, an acknowledgment in the product documentation would be
15 appreciated but is not required.
16 2. Altered source versions must be plainly marked as such, and must not be
17 misrepresented as being the original software.
18 3. This notice may not be removed or altered from any source distribution.
21 This file provides basic windows menu API
25 #ifndef SWELL_PROVIDED_BY_APP
28 #include "swell-menugen.h"
30 #include "swell-internal.h"
32 #include "../ptrlist.h"
33 #include "../wdlcstring.h"
35 HMENU__
*HMENU__::Duplicate()
37 HMENU__
*p
= new HMENU__
;
39 for (x
= 0; x
< items
.GetSize(); x
++)
41 MENUITEMINFO
*s
= items
.Get(x
);
42 MENUITEMINFO
*inf
= (MENUITEMINFO
*)calloc(sizeof(MENUITEMINFO
),1);
45 if (inf
->dwTypeData
&& inf
->fType
== MFT_STRING
) inf
->dwTypeData
=strdup(inf
->dwTypeData
);
46 if (inf
->hSubMenu
) inf
->hSubMenu
= inf
->hSubMenu
->Duplicate();
53 void HMENU__::freeMenuItem(void *p
)
55 MENUITEMINFO
*inf
= (MENUITEMINFO
*)p
;
57 if (inf
->hSubMenu
) inf
->hSubMenu
->Release();
58 if (inf
->fType
== MFT_STRING
) free(inf
->dwTypeData
);
62 static MENUITEMINFO
*GetMenuItemByID(HMENU menu
, int id
, bool searchChildren
=true)
66 for (x
= 0; x
< menu
->items
.GetSize(); x
++)
67 if (menu
->items
.Get(x
)->wID
== (UINT
)id
) return menu
->items
.Get(x
);
69 if (searchChildren
) for (x
= 0; x
< menu
->items
.GetSize(); x
++)
71 if (menu
->items
.Get(x
)->hSubMenu
)
73 MENUITEMINFO
*ret
= GetMenuItemByID(menu
->items
.Get(x
)->hSubMenu
,id
,true);
81 bool SetMenuItemText(HMENU hMenu
, int idx
, int flag
, const char *text
)
83 MENUITEMINFO
*item
= hMenu
? ((flag
& MF_BYPOSITION
) ? hMenu
->items
.Get(idx
) : GetMenuItemByID(hMenu
,idx
)) : NULL
;
84 if (!item
) return false;
86 if (item
->fType
== MFT_STRING
) free(item
->dwTypeData
);
87 item
->fType
= MFT_STRING
;
88 item
->dwTypeData
=strdup(text
?text
:"");
93 bool EnableMenuItem(HMENU hMenu
, int idx
, int en
)
95 MENUITEMINFO
*item
= hMenu
? ((en
& MF_BYPOSITION
) ? hMenu
->items
.Get(idx
) : GetMenuItemByID(hMenu
,idx
)) : NULL
;
96 if (!item
) return false;
98 int mask
= MF_ENABLED
|MF_DISABLED
|MF_GRAYED
;
99 item
->fState
&= ~mask
;
100 item
->fState
|= en
&mask
;
105 bool CheckMenuItem(HMENU hMenu
, int idx
, int chk
)
107 MENUITEMINFO
*item
= hMenu
? ((chk
& MF_BYPOSITION
) ? hMenu
->items
.Get(idx
) : GetMenuItemByID(hMenu
,idx
)) : NULL
;
108 if (!item
) return false;
110 int mask
= MF_CHECKED
;
111 item
->fState
&= ~mask
;
112 item
->fState
|= chk
&mask
;
116 HMENU
SWELL_GetCurrentMenu()
118 return NULL
; // not osx
120 void SWELL_SetCurrentMenu(HMENU hmenu
)
124 HMENU
GetSubMenu(HMENU hMenu
, int pos
)
126 MENUITEMINFO
*item
= hMenu
? hMenu
->items
.Get(pos
) : NULL
;
127 if (item
) return item
->hSubMenu
;
131 int GetMenuItemCount(HMENU hMenu
)
133 if (hMenu
) return hMenu
->items
.GetSize();
137 int GetMenuItemID(HMENU hMenu
, int pos
)
139 MENUITEMINFO
*item
= hMenu
? hMenu
->items
.Get(pos
) : NULL
;
140 if (!item
|| item
->hSubMenu
) return -1;
144 bool SetMenuItemModifier(HMENU hMenu
, int idx
, int flag
, int code
, unsigned int mask
)
149 HMENU
CreatePopupMenu()
153 HMENU
CreatePopupMenuEx(const char *title
)
155 return CreatePopupMenu();
158 void DestroyMenu(HMENU hMenu
)
160 if (hMenu
) hMenu
->Release();
163 int AddMenuItem(HMENU hMenu
, int pos
, const char *name
, int tagid
)
165 if (!hMenu
) return -1;
166 MENUITEMINFO
*inf
= (MENUITEMINFO
*)calloc(1,sizeof(MENUITEMINFO
));
168 inf
->fType
= MFT_STRING
;
169 inf
->dwTypeData
= strdup(name
?name
:"");
170 hMenu
->items
.Insert(pos
,inf
);
174 bool DeleteMenu(HMENU hMenu
, int idx
, int flag
)
176 if (!hMenu
) return false;
177 if (flag
&MF_BYPOSITION
)
179 if (hMenu
->items
.Get(idx
))
181 hMenu
->items
.Delete(idx
,true,HMENU__::freeMenuItem
);
190 for (x
=0;x
<hMenu
->items
.GetSize(); x
++)
192 if (!hMenu
->items
.Get(x
)->hSubMenu
&& hMenu
->items
.Get(x
)->wID
== (UINT
)idx
)
194 hMenu
->items
.Delete(x
--,true,HMENU__::freeMenuItem
);
200 for (x
=0;x
<hMenu
->items
.GetSize(); x
++)
202 if (hMenu
->items
.Get(x
)->hSubMenu
) cnt
+= DeleteMenu(hMenu
->items
.Get(x
)->hSubMenu
,idx
,flag
)?1:0;
210 BOOL
SetMenuItemInfo(HMENU hMenu
, int pos
, BOOL byPos
, MENUITEMINFO
*mi
)
212 if (!hMenu
) return 0;
213 MENUITEMINFO
*item
= byPos
? hMenu
->items
.Get(pos
) : GetMenuItemByID(hMenu
, pos
, true);
216 if ((mi
->fMask
& MIIM_SUBMENU
) && mi
->hSubMenu
!= item
->hSubMenu
)
218 if (item
->hSubMenu
) item
->hSubMenu
->Release();
219 item
->hSubMenu
= mi
->hSubMenu
;
221 if (mi
->fMask
& MIIM_TYPE
)
223 if (item
->fType
== MFT_STRING
) free(item
->dwTypeData
);
226 if (mi
->fType
== MFT_STRING
&& mi
->dwTypeData
) item
->dwTypeData
= strdup( mi
->dwTypeData
);
227 else if (mi
->fType
== MFT_BITMAP
) item
->dwTypeData
= mi
->dwTypeData
;
228 item
->fType
= mi
->fType
;
231 if (mi
->fMask
& MIIM_STATE
) item
->fState
= mi
->fState
;
232 if (mi
->fMask
& MIIM_ID
) item
->wID
= mi
->wID
;
233 if (mi
->fMask
& MIIM_DATA
) item
->dwItemData
= mi
->dwItemData
;
238 BOOL
GetMenuItemInfo(HMENU hMenu
, int pos
, BOOL byPos
, MENUITEMINFO
*mi
)
240 if (!hMenu
) return 0;
241 MENUITEMINFO
*item
= byPos
? hMenu
->items
.Get(pos
) : GetMenuItemByID(hMenu
, pos
, true);
244 if (mi
->fMask
& MIIM_TYPE
)
246 mi
->fType
= item
->fType
;
247 if (item
->fType
== MFT_STRING
&& mi
->dwTypeData
&& mi
->cch
)
249 lstrcpyn_safe(mi
->dwTypeData
,item
->dwTypeData
?item
->dwTypeData
:"",mi
->cch
);
251 else if (item
->fType
== MFT_BITMAP
) mi
->dwTypeData
= item
->dwTypeData
;
254 if (mi
->fMask
& MIIM_DATA
) mi
->dwItemData
= item
->dwItemData
;
255 if (mi
->fMask
& MIIM_STATE
) mi
->fState
= item
->fState
;
256 if (mi
->fMask
& MIIM_ID
) mi
->wID
= item
->wID
;
257 if (mi
->fMask
& MIIM_SUBMENU
) mi
->hSubMenu
= item
->hSubMenu
;
263 void SWELL_InsertMenu(HMENU menu
, int pos
, unsigned int flag
, UINT_PTR idx
, const char *str
)
265 MENUITEMINFO mi
={sizeof(mi
),MIIM_ID
|MIIM_STATE
|MIIM_TYPE
,MFT_STRING
,
266 (flag
& ~MF_BYPOSITION
),(flag
&MF_POPUP
) ? 0 : (UINT
)idx
,NULL
,NULL
,NULL
,0,(char *)str
};
270 mi
.hSubMenu
= (HMENU
)idx
;
271 mi
.fMask
|= MIIM_SUBMENU
;
272 mi
.fState
&= ~MF_POPUP
;
275 if (flag
&MF_SEPARATOR
)
278 mi
.fType
=MFT_SEPARATOR
;
279 mi
.fState
&= ~MF_SEPARATOR
;
285 mi
.fState
&= ~MF_BITMAP
;
288 InsertMenuItem(menu
,pos
,(flag
&MF_BYPOSITION
) ? TRUE
: FALSE
, &mi
);
291 void InsertMenuItem(HMENU hMenu
, int pos
, BOOL byPos
, MENUITEMINFO
*mi
)
294 int ni
=hMenu
->items
.GetSize();
299 for (x
=0;x
<ni
&& hMenu
->items
.Get(x
)->wID
!= (UINT
)pos
; x
++);
302 if (pos
< 0 || pos
> ni
) pos
=ni
;
304 MENUITEMINFO
*inf
= (MENUITEMINFO
*)calloc(sizeof(MENUITEMINFO
),1);
305 inf
->fType
= mi
->fType
;
306 if (mi
->fType
== MFT_STRING
)
308 inf
->dwTypeData
= strdup(mi
->dwTypeData
?mi
->dwTypeData
:"");
310 else if (mi
->fType
== MFT_BITMAP
)
312 inf
->dwTypeData
= mi
->dwTypeData
;
314 else if (mi
->fType
== MFT_SEPARATOR
)
317 if (mi
->fMask
&MIIM_SUBMENU
) inf
->hSubMenu
= mi
->hSubMenu
;
318 if (mi
->fMask
& MIIM_STATE
) inf
->fState
= mi
->fState
;
319 if (mi
->fMask
& MIIM_DATA
) inf
->dwItemData
= mi
->dwItemData
;
320 if (mi
->fMask
& MIIM_ID
) inf
->wID
= mi
->wID
;
322 hMenu
->items
.Insert(pos
,inf
);
326 void SWELL_SetMenuDestination(HMENU menu
, HWND hwnd
)
328 // only needed for Cocoa
331 extern RECT g_trackpopup_yroot
;
332 static POINT m_trackingPt
, m_trackingPt2
;
333 static int m_trackingMouseFlag
;
334 static int m_trackingFlags
,m_trackingRet
;
335 static HWND m_trackingPar
;
336 static WDL_PtrList
<HWND__
> m_trackingMenus
; // each HWND as userdata = HMENU
338 int swell_delegate_menu_message(HWND src
, LPARAM lParam
, int msg
, bool screencoords
)
341 if (_reent
) return 0;
345 POINT sp
= { GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
) };
346 if (!screencoords
) ClientToScreen(src
,&sp
);
348 for (int x
= m_trackingMenus
.GetSize()-1; x
>=0; x
--)
350 HWND sw
= m_trackingMenus
.Get(x
);
353 if (sw
== src
) break; // stop searching (don't delegate to parent)
356 GetWindowRect(sw
,&r
);
360 ScreenToClient(sw
,&p
);
361 SendMessage(sw
,msg
,0,MAKELPARAM(p
.x
,p
.y
));
371 bool swell_isOSwindowmenu(SWELL_OSWINDOW osw
)
373 int x
= m_trackingMenus
.GetSize();
374 if (osw
) while (--x
>=0)
376 HWND__
*p
= m_trackingMenus
.Get(x
);
377 if (p
->m_oswindow
== osw
) return true;
382 int menuBarNavigate(int dir
); // -1 if no menu bar active, 0 if did nothing, 1 if navigated
383 HWND
GetFocusIncludeMenus(void);
385 static LRESULT WINAPI
submenuWndProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
387 static int lcol
, rcol
, mcol
, top_margin
, separator_ht
, text_ht_pad
, bitmap_ht_pad
, scroll_margin
;
390 lcol
=SWELL_UI_SCALE(24); rcol
=SWELL_UI_SCALE(12); mcol
=SWELL_UI_SCALE(10);
391 top_margin
=SWELL_UI_SCALE(4); separator_ht
=SWELL_UI_SCALE(8);
392 text_ht_pad
=SWELL_UI_SCALE(4); bitmap_ht_pad
=SWELL_UI_SCALE(4);
393 scroll_margin
=SWELL_UI_SCALE(10);
398 hwnd
->m_classname
= "__SWELL_MENU";
399 m_trackingMenus
.Add(hwnd
);
400 SetWindowLongPtr(hwnd
,GWLP_USERDATA
,lParam
);
402 if (m_trackingPar
&& !(m_trackingFlags
&TPM_NONOTIFY
))
403 SendMessage(m_trackingPar
,WM_INITMENUPOPUP
,(WPARAM
)lParam
,0);
406 HDC hdc
= GetDC(hwnd
);
407 HMENU__
*menu
= (HMENU__
*)lParam
;
408 int ht
= 0, wid
=SWELL_UI_SCALE(100),wid2
=0;
410 for (x
=0; x
< menu
->items
.GetSize(); x
++)
412 MENUITEMINFO
*inf
= menu
->items
.Get(x
);
413 if (inf
->fType
== MFT_STRING
)
416 const char *str
= inf
->dwTypeData
;
417 if (!str
|| !*str
) str
="XXXXX";
418 const char *pt2
= strstr(str
,"\t");
419 DrawText(hdc
,str
,pt2
? (int)(pt2
-str
) : -1,&r
,DT_CALCRECT
|DT_SINGLELINE
);
420 if (r
.right
> wid
) wid
=r
.right
;
421 ht
+= r
.bottom
+ text_ht_pad
;
426 DrawText(hdc
,pt2
+1,-1,&r
,DT_CALCRECT
|DT_SINGLELINE
);
427 if (r
.right
> wid2
) wid2
=r
.right
;
430 else if (inf
->fType
== MFT_BITMAP
)
433 if (inf
->dwTypeData
) GetObject((HBITMAP
)inf
->dwTypeData
,sizeof(bm
),&bm
);
434 if (bm
.bmWidth
> wid
) wid
= bm
.bmWidth
;
436 ht
+= bm
.bmHeight
+ bitmap_ht_pad
;
440 // treat as separator
444 wid
+=lcol
+rcol
+ (wid2
?wid2
+mcol
:0);
447 RECT tr
={m_trackingPt
.x
,m_trackingPt
.y
,
448 m_trackingPt
.x
+wid
+SWELL_UI_SCALE(4),m_trackingPt
.y
+ht
+top_margin
* 2}, vp
;
449 SWELL_GetViewPort(&vp
,&tr
,true);
452 if (g_trackpopup_yroot
.bottom
> g_trackpopup_yroot
.top
&&
453 g_trackpopup_yroot
.bottom
> vp
.top
&&
454 g_trackpopup_yroot
.top
< vp
.bottom
)
456 if (vp
.bottom
- g_trackpopup_yroot
.bottom
< g_trackpopup_yroot
.top
- vp
.top
)
457 vp
.bottom
= g_trackpopup_yroot
.top
;
459 vp
.top
= g_trackpopup_yroot
.bottom
;
462 if (tr
.bottom
> vp
.bottom
) { tr
.top
+= vp
.bottom
-tr
.bottom
; tr
.bottom
=vp
.bottom
; }
463 if (tr
.right
> vp
.right
)
465 if ((vp
.right
- m_trackingPt2
.x
) < (m_trackingPt2
.x
- vp
.left
))
467 tr
.left
= m_trackingPt2
.x
- (tr
.right
-tr
.left
);
468 tr
.right
= m_trackingPt2
.x
;
472 tr
.left
+= vp
.right
-tr
.right
; tr
.right
=vp
.right
;
476 if (tr
.left
< vp
.left
) { tr
.right
+= vp
.left
-tr
.left
; tr
.left
=vp
.left
; }
477 if (tr
.top
< vp
.top
) { tr
.bottom
+= vp
.top
-tr
.top
; tr
.top
=vp
.top
; }
478 if (tr
.bottom
> vp
.bottom
) tr
.bottom
=vp
.bottom
;
479 if (tr
.right
> vp
.right
) tr
.right
=vp
.right
;
481 SetWindowPos(hwnd
,NULL
,tr
.left
,tr
.top
,tr
.right
-tr
.left
,tr
.bottom
-tr
.top
,SWP_NOZORDER
);
483 hwnd
->m_extra
[0] = 0; // Y scroll offset
484 hwnd
->m_extra
[1] = 0; // &1=allow scroll flag (set from paint), &2=force scroll down (if sel_vis is offscreen positive)
487 SetWindowLong(hwnd
,GWL_STYLE
,GetWindowLong(hwnd
,GWL_STYLE
)&~WS_CAPTION
);
488 ShowWindow(hwnd
,SW_SHOW
);
490 SetTimer(hwnd
,1,100,NULL
);
491 SetTimer(hwnd
,2,15,NULL
);
496 if (BeginPaint(hwnd
,&ps
))
499 GetClientRect(hwnd
,&cr
);
500 HBRUSH br
=CreateSolidBrush(g_swell_ctheme
.menu_bg
);
501 HBRUSH br2
= CreateSolidBrushAlpha(g_swell_ctheme
.menu_scroll
,0.5f
);
502 HBRUSH br3
= CreateSolidBrush(g_swell_ctheme
.menu_scroll_arrow
);
503 HBRUSH br_submenu_arrow
= CreateSolidBrush(g_swell_ctheme
.menu_submenu_arrow
);
504 HPEN pen
=CreatePen(PS_SOLID
,0,g_swell_ctheme
.menu_shadow
);
505 HPEN pen2
=CreatePen(PS_SOLID
,0,g_swell_ctheme
.menu_hilight
);
506 HGDIOBJ oldbr
= SelectObject(ps
.hdc
,br
);
507 HGDIOBJ oldpen
= SelectObject(ps
.hdc
,pen2
);
508 Rectangle(ps
.hdc
,cr
.left
,cr
.top
,cr
.right
,cr
.bottom
);
509 SetBkMode(ps
.hdc
,TRANSPARENT
);
510 HMENU__
*menu
= (HMENU__
*)GetWindowLongPtr(hwnd
,GWLP_USERDATA
);
512 int ypos
= top_margin
;
514 MoveToEx(ps
.hdc
,cr
.left
+lcol
-SWELL_UI_SCALE(4),cr
.top
,NULL
);
515 LineTo(ps
.hdc
,cr
.left
+lcol
-SWELL_UI_SCALE(4),cr
.bottom
);
516 SelectObject(ps
.hdc
,pen
);
517 MoveToEx(ps
.hdc
,cr
.left
+lcol
-SWELL_UI_SCALE(5),cr
.top
,NULL
);
518 LineTo(ps
.hdc
,cr
.left
+lcol
-SWELL_UI_SCALE(5),cr
.bottom
);
521 for (x
=wdl_max(hwnd
->m_extra
[0],0); x
< (menu
->items
.GetSize()); x
++)
523 if (ypos
>= cr
.bottom
)
525 hwnd
->m_extra
[1] = 1; // allow scrolling down
528 MENUITEMINFO
*inf
= menu
->items
.Get(x
);
529 RECT r
={lcol
,ypos
,cr
.right
, };
530 bool dis
= !!(inf
->fState
& MF_GRAYED
);
533 if (inf
->fType
== MFT_STRING
)
535 const char *str
= inf
->dwTypeData
;
536 if (!str
|| !*str
) str
="XXXXX";
538 DrawText(ps
.hdc
,str
,-1,&mr
,DT_CALCRECT
|DT_SINGLELINE
);
540 ypos
+= mr
.bottom
+ text_ht_pad
;
543 else if (inf
->fType
== MFT_BITMAP
)
545 if (inf
->dwTypeData
) GetObject((HBITMAP
)inf
->dwTypeData
,sizeof(bm
),&bm
);
547 ypos
+= bm
.bmHeight
+ bitmap_ht_pad
;
554 ypos
+= separator_ht
;
558 if (x
== menu
->sel_vis
&& !dis
)
560 HBRUSH brs
=CreateSolidBrush(g_swell_ctheme
.menu_bg_sel
);
562 FillRect(ps
.hdc
,&r2
,brs
);
564 SetTextColor(ps
.hdc
,g_swell_ctheme
.menu_text_sel
);
569 dis
? g_swell_ctheme
.menu_text_disabled
:
570 g_swell_ctheme
.menu_text
);
573 if (inf
->fType
== MFT_STRING
)
575 const char *str
= inf
->dwTypeData
;
577 const char *pt2
= strstr(str
,"\t");
581 DrawText(ps
.hdc
,str
,pt2
? (int)(pt2
-str
) : -1,&r
,DT_VCENTER
|DT_SINGLELINE
);
584 RECT tr
=r
; tr
.right
-=rcol
;
585 DrawText(ps
.hdc
,pt2
+1,-1,&tr
,DT_VCENTER
|DT_SINGLELINE
|DT_RIGHT
);
589 else if (inf
->fType
== MFT_BITMAP
)
594 tr
.top
+= bitmap_ht_pad
/2;
595 tr
.right
= tr
.left
+ bm
.bmWidth
;
596 tr
.bottom
= tr
.top
+ bm
.bmHeight
;
597 DrawImageInRect(ps
.hdc
,(HBITMAP
)inf
->dwTypeData
,&tr
);
602 SelectObject(ps
.hdc
,pen2
);
603 int y
= r
.top
/2+r
.bottom
/2, right
= r
.right
-rcol
*3/2;
604 MoveToEx(ps
.hdc
,r
.left
,y
,NULL
);
605 LineTo(ps
.hdc
,right
,y
);
606 SelectObject(ps
.hdc
,pen
);
609 MoveToEx(ps
.hdc
,r
.left
,y
,NULL
);
610 LineTo(ps
.hdc
,right
,y
);
614 const int sz
= (r
.bottom
-r
.top
)/4, xp
= r
.right
- sz
*2, yp
= (r
.top
+ r
.bottom
)/2;
621 HGDIOBJ oldPen
= SelectObject(ps
.hdc
,GetStockObject(NULL_PEN
));
622 SelectObject(ps
.hdc
,br_submenu_arrow
);
623 Polygon(ps
.hdc
,pts
,3);
625 SelectObject(ps
.hdc
,oldPen
);
627 if (inf
->fState
&MF_CHECKED
)
630 dis
? g_swell_ctheme
.menu_text_disabled
:
631 g_swell_ctheme
.menu_text
);
632 RECT r2
=r
; r2
.left
= 0; r2
.right
=lcol
;
633 DrawText(ps
.hdc
,"X",-1,&r2
,DT_VCENTER
|DT_CENTER
|DT_SINGLELINE
);
635 if ((r
.top
+ypos
)/2 > cr
.bottom
)
637 hwnd
->m_extra
[1] = 1; // allow scrolling down if last item was halfway off
640 if (x
<= menu
->sel_vis
) hwnd
->m_extra
[1]|=2;
643 // lower scroll indicator
644 int mid
=(cr
.right
-cr
.left
)/2;
645 SelectObject(ps
.hdc
,GetStockObject(NULL_PEN
));
646 SelectObject(ps
.hdc
,br3
);
648 const int smm
= SWELL_UI_SCALE(2);
649 const int smh
= scroll_margin
-smm
*2;
650 if (hwnd
->m_extra
[1]&1)
652 RECT fr
= {cr
.left
, cr
.bottom
-scroll_margin
, cr
.right
,cr
.bottom
};
653 FillRect(ps
.hdc
,&fr
,br2
);
654 pts
[0].x
= mid
; pts
[0].y
= cr
.bottom
- smm
;
655 pts
[1].x
= mid
-smh
; pts
[1].y
= pts
[0].y
- smh
;
656 pts
[2].x
= mid
+smh
; pts
[2].y
= pts
[1].y
;
657 Polygon(ps
.hdc
,pts
,3);
659 // upper scroll indicator
660 if (hwnd
->m_extra
[0] > 0)
662 RECT fr
= {cr
.left
, cr
.top
, cr
.right
, cr
.top
+scroll_margin
};
663 FillRect(ps
.hdc
,&fr
,br2
);
665 pts
[0].x
= mid
; pts
[0].y
= cr
.top
+ smm
;
666 pts
[1].x
= mid
-smh
; pts
[1].y
= pts
[0].y
+ smh
;
667 pts
[2].x
= mid
+smh
; pts
[2].y
= pts
[1].y
;
668 Polygon(ps
.hdc
,pts
,3);
671 SelectObject(ps
.hdc
,oldbr
);
672 SelectObject(ps
.hdc
,oldpen
);
676 DeleteObject(br_submenu_arrow
);
686 HWND h
= GetFocusIncludeMenus();
689 int a
= h
? m_trackingMenus
.Find(h
) : -1;
690 if (a
<0 || a
< m_trackingMenus
.Find(hwnd
))
692 if (m_trackingMouseFlag
&& m_trackingMenus
.Get(0))
694 SetFocus(m_trackingMenus
.Get(0));
695 m_trackingMouseFlag
=0;
697 else DestroyWindow(hwnd
);
701 else if (wParam
== 2)
705 GetWindowRect(hwnd
,&tr
);
709 const bool xmatch
= (curM
.x
>= tr
.left
&& curM
.x
< tr
.right
);
710 if (xmatch
|| (hwnd
->m_extra
[1]&3)==3)
712 HMENU__
*menu
= (HMENU__
*)GetWindowLongPtr(hwnd
,GWLP_USERDATA
);
713 int xFirst
= wdl_max(hwnd
->m_extra
[0],0);
714 const bool ymatch
= curM
.y
>= tr
.bottom
-scroll_margin
&& curM
.y
< tr
.bottom
+scroll_margin
;
715 if ((hwnd
->m_extra
[1]&1) && ((hwnd
->m_extra
[1]&2) || ymatch
))
717 hwnd
->m_extra
[0]=++xFirst
;
719 if (ymatch
) menu
->sel_vis
=-1;
720 InvalidateRect(hwnd
,NULL
,FALSE
);
722 else if (xFirst
> 0 && curM
.y
>= tr
.top
-scroll_margin
&& curM
.y
< tr
.top
+scroll_margin
)
724 hwnd
->m_extra
[0]=--xFirst
;
726 InvalidateRect(hwnd
,NULL
,FALSE
);
734 if (wParam
== VK_ESCAPE
|| wParam
== VK_LEFT
)
736 HWND l
= m_trackingMenus
.Get(m_trackingMenus
.Find(hwnd
)-1);
740 if (wParam
!= VK_LEFT
|| menuBarNavigate(-1) < 0)
744 else if (wParam
== VK_RETURN
|| wParam
== VK_RIGHT
)
746 HMENU__
*menu
= (HMENU__
*)GetWindowLongPtr(hwnd
,GWLP_USERDATA
);
747 if (wParam
== VK_RIGHT
)
749 MENUITEMINFO
*inf
= menu
->items
.Get(menu
->sel_vis
);
750 if (!inf
|| !inf
->hSubMenu
)
756 SendMessage(hwnd
,WM_USER
+100,1,menu
->sel_vis
);
758 else if (wParam
== VK_UP
|| wParam
== VK_PRIOR
)
760 HMENU__
*menu
= (HMENU__
*)GetWindowLongPtr(hwnd
,GWLP_USERDATA
);
761 int l
= menu
->sel_vis
;
762 for (int i
= wParam
== VK_UP
? 0 : 9; i
>=0; i
--)
764 int mc
= menu
->items
.GetSize();
769 if (wParam
!= VK_UP
) break;
770 l
= menu
->items
.GetSize();
772 MENUITEMINFO
*inf
= menu
->items
.Get(--l
);
774 if (!(inf
->fState
& MF_GRAYED
) && inf
->fType
!= MFT_SEPARATOR
)
781 if (menu
->sel_vis
< hwnd
->m_extra
[0])
782 hwnd
->m_extra
[0] = menu
->sel_vis
;
783 InvalidateRect(hwnd
,NULL
,FALSE
);
785 else if (wParam
== VK_DOWN
|| wParam
== VK_NEXT
)
787 HMENU__
*menu
= (HMENU__
*)GetWindowLongPtr(hwnd
,GWLP_USERDATA
);
788 int l
= menu
->sel_vis
;
789 const int n
=menu
->items
.GetSize()-1;
790 for (int i
= wParam
== VK_DOWN
? 0 : 9; i
>=0; i
--)
797 if (wParam
!= VK_DOWN
) break;
801 MENUITEMINFO
*inf
= menu
->items
.Get(++l
);
803 if (!(inf
->fState
& MF_GRAYED
) && inf
->fType
!= MFT_SEPARATOR
)
810 InvalidateRect(hwnd
,NULL
,FALSE
);
812 else if (wParam
== VK_END
)
814 HMENU__
*menu
= (HMENU__
*)GetWindowLongPtr(hwnd
,GWLP_USERDATA
);
815 int l
= menu
->items
.GetSize();
818 MENUITEMINFO
*inf
= menu
->items
.Get(--l
);
820 if (!(inf
->fState
& MF_GRAYED
) && inf
->fType
!= MFT_SEPARATOR
)
826 if (menu
->sel_vis
< hwnd
->m_extra
[0])
827 hwnd
->m_extra
[0] = menu
->sel_vis
;
828 InvalidateRect(hwnd
,NULL
,FALSE
);
830 else if (wParam
== VK_HOME
)
832 HMENU__
*menu
= (HMENU__
*)GetWindowLongPtr(hwnd
,GWLP_USERDATA
);
834 while (l
< menu
->items
.GetSize())
836 MENUITEMINFO
*inf
= menu
->items
.Get(l
++);
838 if (!(inf
->fState
& MF_GRAYED
) && inf
->fType
!= MFT_SEPARATOR
)
844 if (menu
->sel_vis
< hwnd
->m_extra
[0])
845 hwnd
->m_extra
[0] = menu
->sel_vis
;
846 InvalidateRect(hwnd
,NULL
,FALSE
);
848 else if ((lParam
& FVIRTKEY
) && (
849 (wParam
>= 'A' && wParam
<= 'Z') ||
850 (wParam
>= '0' && wParam
<= '9')))
852 HMENU__
*menu
= (HMENU__
*)GetWindowLongPtr(hwnd
,GWLP_USERDATA
);
853 const int n
=menu
->items
.GetSize();
855 int offs
= menu
->sel_vis
+1;
856 if (offs
<0||offs
>=n
) offs
=0;
858 for(int x
=0;x
<n
+n
;x
++)
860 MENUITEMINFO
*inf
= menu
->items
.Get(offs
);
861 if (inf
->fType
== MFT_STRING
&&
862 !(inf
->fState
& MF_GRAYED
) &&
865 const char *p
= inf
->dwTypeData
;
866 bool is_prefix_mode
= x
<n
;
867 if (!is_prefix_mode
&& matchcnt
)
871 // implies prefix mode, only one matching item
872 SendMessage(hwnd
,WM_USER
+100,1,menu
->sel_vis
);
877 if (is_prefix_mode
) while (*p
)
881 if (*p
!= '&') break;
886 if (*p
> 0 && (WPARAM
)toupper(*p
) == wParam
)
890 menu
->sel_vis
= offs
;
891 if (menu
->sel_vis
< hwnd
->m_extra
[0])
892 hwnd
->m_extra
[0] = menu
->sel_vis
;
893 InvalidateRect(hwnd
,NULL
,FALSE
);
895 if (!is_prefix_mode
) break;
898 if (++offs
>= n
) offs
=0;
905 int a
= m_trackingMenus
.Find(hwnd
);
906 m_trackingMenus
.Delete(a
);
907 if (m_trackingMenus
.Get(a
)) DestroyWindow(m_trackingMenus
.Get(a
));
908 RemoveProp(hwnd
,"SWELL_MenuOwner");
912 if (wParam
== 1 || wParam
== 2 || wParam
== 3 || wParam
== 4)
914 int which
= (int) lParam
;
915 int item_ypos
= which
;
917 HMENU__
*menu
= (HMENU__
*)GetWindowLongPtr(hwnd
,GWLP_USERDATA
);
921 if (wParam
> 1) which
= -1;
923 for (int x
=wdl_max(hwnd
->m_extra
[0],0); x
< (menu
->items
.GetSize()); x
++)
925 if (wParam
== 1 && which
== x
) { item_ypos
= ht
; break; }
926 MENUITEMINFO
*inf
= menu
->items
.Get(x
);
928 if (inf
->fType
== MFT_STRING
)
931 const char *str
= inf
->dwTypeData
;
932 if (!str
|| !*str
) str
="XXXXX";
933 const char *pt2
= strstr(str
,"\t");
934 DrawText(hdc
,str
,pt2
? (int)(pt2
-str
) : -1,&r
,DT_CALCRECT
|DT_SINGLELINE
);
935 ht
+= r
.bottom
+ text_ht_pad
;
937 else if (inf
->fType
== MFT_BITMAP
)
940 if (inf
->dwTypeData
) GetObject((HBITMAP
)inf
->dwTypeData
,sizeof(bm
),&bm
);
941 ht
+= bm
.bmHeight
+ bitmap_ht_pad
;
947 if (wParam
> 1 && item_ypos
< ht
)
951 if (wParam
== 4 && inf
->hSubMenu
)
953 HWND nextmenu
= m_trackingMenus
.Get(m_trackingMenus
.Find(hwnd
)+1);
954 if (!nextmenu
|| GetWindowLongPtr(nextmenu
,GWLP_USERDATA
) != (LPARAM
)inf
->hSubMenu
)
956 wParam
= 1; // activate if not already visible
957 menu
->sel_vis
= which
;
964 if (wParam
== 3 || wParam
== 4)
966 MENUITEMINFO
*inf
= menu
->items
.Get(which
);
967 HWND next
= m_trackingMenus
.Get(m_trackingMenus
.Find(hwnd
)+1);
968 if (next
&& inf
&& (!inf
->hSubMenu
|| (LPARAM
)inf
->hSubMenu
!= GetWindowLongPtr(next
,GWLP_USERDATA
))) DestroyWindow(next
);
969 menu
->sel_vis
= which
;
973 MENUITEMINFO
*inf
= menu
->items
.Get(which
);
977 if (inf
->fState
&MF_GRAYED
){ }
978 else if (inf
->hSubMenu
)
980 const int nextidx
= m_trackingMenus
.Find(hwnd
)+1;
981 HWND hh
= m_trackingMenus
.Get(nextidx
);
983 inf
->hSubMenu
->sel_vis
=-1;
987 m_trackingMenus
.Delete(nextidx
);
988 int a
= m_trackingMenus
.GetSize();
989 while (a
> nextidx
) DestroyWindow(m_trackingMenus
.Get(--a
));
993 hh
= new HWND__(NULL
,0,NULL
,"menu",false,submenuWndProc
,NULL
, hwnd
);
994 SetProp(hh
,"SWELL_MenuOwner",GetProp(hwnd
,"SWELL_MenuOwner"));
998 GetClientRect(hwnd
,&r
);
999 m_trackingPt
.x
=r
.right
- SWELL_UI_SCALE(3);
1000 m_trackingPt
.y
=item_ypos
;
1001 m_trackingPt2
.x
=r
.left
+ lcol
/4;
1002 m_trackingPt2
.y
=item_ypos
;
1003 ClientToScreen(hwnd
,&m_trackingPt
);
1004 ClientToScreen(hwnd
,&m_trackingPt2
);
1006 submenuWndProc(hh
, WM_CREATE
,0,(LPARAM
)inf
->hSubMenu
);
1007 InvalidateRect(hwnd
,NULL
,FALSE
);
1009 else if (inf
->wID
) m_trackingRet
= inf
->wID
;
1015 if (swell_delegate_menu_message(hwnd
, lParam
,uMsg
, false))
1019 GetClientRect(hwnd
,&r
);
1020 HMENU__
*menu
= (HMENU__
*)GetWindowLongPtr(hwnd
,GWLP_USERDATA
);
1021 const int oldsel
= menu
->sel_vis
;
1022 if (GET_X_LPARAM(lParam
)>=r
.left
&& GET_X_LPARAM(lParam
)<r
.right
)
1024 int mode
= 4;//GET_X_LPARAM(lParam) >= r.right - rcol*2 ? 4 : 3;
1025 SendMessage(hwnd
,WM_USER
+100,mode
,GET_Y_LPARAM(lParam
));
1027 else menu
->sel_vis
= -1;
1028 if (oldsel
!= menu
->sel_vis
) InvalidateRect(hwnd
,NULL
,FALSE
);
1034 if (swell_delegate_menu_message(hwnd
, lParam
, uMsg
, false))
1038 GetClientRect(hwnd
,&r
);
1039 if (GET_X_LPARAM(lParam
)>=r
.left
&& GET_X_LPARAM(lParam
)<r
.right
)
1041 SendMessage(hwnd
,WM_USER
+100,2,GET_Y_LPARAM(lParam
));
1044 else DestroyWindow(hwnd
);
1048 return DefWindowProc(hwnd
,uMsg
,wParam
,lParam
);
1051 void DestroyPopupMenus()
1053 if (m_trackingMenus
.GetSize()) DestroyWindow(m_trackingMenus
.Get(0));
1056 int TrackPopupMenu(HMENU hMenu
, int flags
, int xpos
, int ypos
, int resvd
, HWND hwnd
, const RECT
*r
)
1058 if (!hMenu
|| m_trackingMenus
.GetSize()) return 0;
1064 m_trackingFlags
=flags
;
1066 m_trackingPt2
.x
=m_trackingPt
.x
=xpos
;
1067 m_trackingPt2
.y
=m_trackingPt
.y
=ypos
;
1068 m_trackingMouseFlag
= 0;
1069 if (GetAsyncKeyState(VK_LBUTTON
)) m_trackingMouseFlag
|= 1;
1070 if (GetAsyncKeyState(VK_RBUTTON
)) m_trackingMouseFlag
|= 2;
1071 if (GetAsyncKeyState(VK_MBUTTON
)) m_trackingMouseFlag
|= 4;
1073 // HWND oldFoc = GetFocus();
1074 // bool oldFoc_child = oldFoc && (IsChild(hwnd,oldFoc) || oldFoc == hwnd || oldFoc==GetParent(hwnd));
1076 if (hwnd
) hwnd
->Retain();
1079 HWND hh
=new HWND__(NULL
,0,NULL
,"menu",false,submenuWndProc
,NULL
, hwnd
);
1081 submenuWndProc(hh
,WM_CREATE
,0,(LPARAM
)hMenu
);
1083 SetProp(hh
,"SWELL_MenuOwner",(HANDLE
)hwnd
);
1085 while (m_trackingRet
<0 && m_trackingMenus
.GetSize())
1087 void SWELL_RunMessageLoop();
1088 SWELL_RunMessageLoop();
1092 int x
=m_trackingMenus
.GetSize()-1;
1095 HWND h
= m_trackingMenus
.Get(x
);
1096 m_trackingMenus
.Delete(x
);
1097 if (h
) DestroyWindow(h
);
1101 // if (oldFoc_child) SetFocus(oldFoc);
1103 if (!(flags
&TPM_NONOTIFY
) && m_trackingRet
>0)
1104 SendMessage(hwnd
,WM_COMMAND
,m_trackingRet
,0);
1106 if (hwnd
) hwnd
->Release();
1110 return m_trackingRet
>0?m_trackingRet
:0;
1116 void SWELL_Menu_AddMenuItem(HMENU hMenu
, const char *name
, int idx
, unsigned int flags
)
1118 MENUITEMINFO mi
={sizeof(mi
),MIIM_ID
|MIIM_STATE
|MIIM_TYPE
,MFT_STRING
,
1119 (UINT
)((flags
)?MFS_GRAYED
:0),(UINT
)idx
,NULL
,NULL
,NULL
,0,(char *)name
};
1122 mi
.fType
= MFT_SEPARATOR
;
1123 mi
.fMask
&=~(MIIM_STATE
|MIIM_ID
);
1125 InsertMenuItem(hMenu
,GetMenuItemCount(hMenu
),TRUE
,&mi
);
1129 SWELL_MenuResourceIndex
*SWELL_curmodule_menuresource_head
; // todo: move to per-module thingy
1131 static SWELL_MenuResourceIndex
*resById(SWELL_MenuResourceIndex
*head
, const char *resid
)
1133 SWELL_MenuResourceIndex
*p
=head
;
1136 if (p
->resid
== resid
) return p
;
1142 HMENU
SWELL_LoadMenu(SWELL_MenuResourceIndex
*head
, const char *resid
)
1144 SWELL_MenuResourceIndex
*p
;
1146 if (!(p
=resById(head
,resid
))) return 0;
1147 HMENU hMenu
=CreatePopupMenu();
1148 if (hMenu
) p
->createFunc(hMenu
);
1152 HMENU
SWELL_DuplicateMenu(HMENU menu
)
1154 if (!menu
) return 0;
1155 return menu
->Duplicate();
1158 BOOL
SetMenu(HWND hwnd
, HMENU menu
)
1160 if (!hwnd
) return 0;
1161 HMENU oldmenu
= hwnd
->m_menu
;
1163 hwnd
->m_menu
= menu
;
1165 if (!hwnd
->m_parent
&& !!hwnd
->m_menu
!= !!oldmenu
)
1167 WNDPROC oldwc
= hwnd
->m_wndproc
;
1168 hwnd
->m_wndproc
= DefWindowProc
;
1170 GetWindowRect(hwnd
,&r
);
1172 if (oldmenu
) r
.bottom
-= g_swell_ctheme
.menubar_height
; // hack: we should WM_NCCALCSIZE before and after, really
1173 else r
.bottom
+= g_swell_ctheme
.menubar_height
;
1175 SetWindowPos(hwnd
,NULL
,0,0,r
.right
-r
.left
,r
.bottom
-r
.top
,SWP_NOZORDER
|SWP_NOMOVE
|SWP_NOACTIVATE
);
1176 hwnd
->m_wndproc
= oldwc
;
1183 HMENU
GetMenu(HWND hwnd
)
1185 if (!hwnd
) return 0;
1186 return hwnd
->m_menu
;
1189 void DrawMenuBar(HWND hwnd
)
1191 if (hwnd
&& hwnd
->m_menu
)
1194 GetClientRect(hwnd
,&r
);
1195 r
.top
= - g_swell_ctheme
.menubar_height
;
1197 InvalidateRect(hwnd
,&r
,FALSE
);
1202 // copied from swell-menu.mm, can have a common impl someday
1203 int SWELL_GenerateMenuFromList(HMENU hMenu
, const void *_list
, int listsz
)
1205 SWELL_MenuGen_Entry
*list
= (SWELL_MenuGen_Entry
*)_list
;
1206 const int l1
=strlen(SWELL_MENUGEN_POPUP_PREFIX
);
1210 if (!list
->name
) SWELL_Menu_AddMenuItem(hMenu
,NULL
,-1,0);
1211 else if (!strcmp(list
->name
,SWELL_MENUGEN_ENDPOPUP
)) return list
+ 1 - (SWELL_MenuGen_Entry
*)_list
;
1212 else if (!strncmp(list
->name
,SWELL_MENUGEN_POPUP_PREFIX
,l1
))
1214 MENUITEMINFO mi
={sizeof(mi
),MIIM_SUBMENU
|MIIM_STATE
|MIIM_TYPE
,MFT_STRING
,0,0,CreatePopupMenuEx(list
->name
+l1
),NULL
,NULL
,0,(char *)list
->name
+l1
};
1215 cnt
+= SWELL_GenerateMenuFromList(mi
.hSubMenu
,list
+1,listsz
-1);
1216 InsertMenuItem(hMenu
,GetMenuItemCount(hMenu
),TRUE
,&mi
);
1218 else SWELL_Menu_AddMenuItem(hMenu
,list
->name
,list
->idx
,list
->flags
);
1223 return list
+ 1 - (SWELL_MenuGen_Entry
*)_list
;