1456 lines
43 KiB
C++
1456 lines
43 KiB
C++
/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX)
|
|
Copyright (C) 2006 and later, Cockos, Inc.
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
|
|
This file provides basic windows menu API
|
|
|
|
*/
|
|
|
|
#ifndef SWELL_PROVIDED_BY_APP
|
|
|
|
#include "swell.h"
|
|
#include "swell-menugen.h"
|
|
|
|
#include "swell-internal.h"
|
|
|
|
#include "../ptrlist.h"
|
|
#include "../wdlcstring.h"
|
|
|
|
static bool MenuIsStringType(const MENUITEMINFO *inf)
|
|
{
|
|
return inf->fType == MFT_STRING || inf->fType == MFT_RADIOCHECK;
|
|
}
|
|
|
|
HMENU__ *HMENU__::Duplicate()
|
|
{
|
|
HMENU__ *p = new HMENU__;
|
|
int x;
|
|
for (x = 0; x < items.GetSize(); x ++)
|
|
{
|
|
MENUITEMINFO *s = items.Get(x);
|
|
MENUITEMINFO *inf = (MENUITEMINFO*)calloc(sizeof(MENUITEMINFO),1);
|
|
|
|
*inf = *s;
|
|
if (inf->dwTypeData && MenuIsStringType(inf)) inf->dwTypeData=strdup(inf->dwTypeData);
|
|
if (inf->hSubMenu) inf->hSubMenu = inf->hSubMenu->Duplicate();
|
|
|
|
p->items.Add(inf);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
void HMENU__::freeMenuItem(void *p)
|
|
{
|
|
MENUITEMINFO *inf = (MENUITEMINFO *)p;
|
|
if (!inf) return;
|
|
if (inf->hSubMenu) inf->hSubMenu->Release();
|
|
if (MenuIsStringType(inf)) free(inf->dwTypeData);
|
|
free(inf);
|
|
}
|
|
|
|
static MENUITEMINFO *GetMenuItemByID(HMENU menu, int id, bool searchChildren=true)
|
|
{
|
|
if (WDL_NOT_NORMALLY(!menu)) return 0;
|
|
int x;
|
|
for (x = 0; x < menu->items.GetSize(); x ++)
|
|
if (menu->items.Get(x)->wID == (UINT)id) return menu->items.Get(x);
|
|
|
|
if (searchChildren) for (x = 0; x < menu->items.GetSize(); x ++)
|
|
{
|
|
if (menu->items.Get(x)->hSubMenu)
|
|
{
|
|
MENUITEMINFO *ret = GetMenuItemByID(menu->items.Get(x)->hSubMenu,id,true);
|
|
if (ret) return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool SetMenuItemText(HMENU hMenu, int idx, int flag, const char *text)
|
|
{
|
|
MENUITEMINFO *item = WDL_NORMALLY(hMenu) ? ((flag & MF_BYPOSITION) ? hMenu->items.Get(idx) : GetMenuItemByID(hMenu,idx)) : NULL;
|
|
if (!item) return false;
|
|
|
|
if (MenuIsStringType(item)) free(item->dwTypeData);
|
|
else item->fType = MFT_STRING;
|
|
item->dwTypeData=strdup(text?text:"");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool EnableMenuItem(HMENU hMenu, int idx, int en)
|
|
{
|
|
MENUITEMINFO *item = WDL_NORMALLY(hMenu) ? ((en & MF_BYPOSITION) ? hMenu->items.Get(idx) : GetMenuItemByID(hMenu,idx)) : NULL;
|
|
if (!item) return false;
|
|
|
|
int mask = MF_ENABLED|MF_DISABLED|MF_GRAYED;
|
|
item->fState &= ~mask;
|
|
item->fState |= en&mask;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CheckMenuItem(HMENU hMenu, int idx, int chk)
|
|
{
|
|
MENUITEMINFO *item = WDL_NORMALLY(hMenu) ? ((chk & MF_BYPOSITION) ? hMenu->items.Get(idx) : GetMenuItemByID(hMenu,idx)) : NULL;
|
|
if (!item) return false;
|
|
|
|
int mask = MF_CHECKED;
|
|
item->fState &= ~mask;
|
|
item->fState |= chk&mask;
|
|
|
|
return true;
|
|
}
|
|
HMENU SWELL_GetCurrentMenu()
|
|
{
|
|
return NULL; // not osx
|
|
}
|
|
void SWELL_SetCurrentMenu(HMENU hmenu)
|
|
{
|
|
}
|
|
|
|
HMENU GetSubMenu(HMENU hMenu, int pos)
|
|
{
|
|
MENUITEMINFO *item = WDL_NORMALLY(hMenu) ? hMenu->items.Get(pos) : NULL;
|
|
if (item) return item->hSubMenu;
|
|
return 0;
|
|
}
|
|
|
|
int GetMenuItemCount(HMENU hMenu)
|
|
{
|
|
if (WDL_NORMALLY(hMenu)) return hMenu->items.GetSize();
|
|
return 0;
|
|
}
|
|
|
|
int GetMenuItemID(HMENU hMenu, int pos)
|
|
{
|
|
MENUITEMINFO *item = WDL_NORMALLY(hMenu) ? hMenu->items.Get(pos) : NULL;
|
|
if (!item)
|
|
{
|
|
WDL_ASSERT(pos==0); // do not assert if GetMenuItemID(0) is called on an empty menu
|
|
return -1;
|
|
}
|
|
if (item->hSubMenu) return -1;
|
|
return item->wID;
|
|
}
|
|
|
|
bool SetMenuItemModifier(HMENU hMenu, int idx, int flag, int code, unsigned int mask)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
HMENU CreatePopupMenu()
|
|
{
|
|
return new HMENU__;
|
|
}
|
|
HMENU CreatePopupMenuEx(const char *title)
|
|
{
|
|
return CreatePopupMenu();
|
|
}
|
|
|
|
void DestroyMenu(HMENU hMenu)
|
|
{
|
|
if (WDL_NORMALLY(hMenu)) hMenu->Release();
|
|
}
|
|
|
|
int AddMenuItem(HMENU hMenu, int pos, const char *name, int tagid)
|
|
{
|
|
if (WDL_NOT_NORMALLY(!hMenu)) return -1;
|
|
MENUITEMINFO *inf = (MENUITEMINFO*)calloc(1,sizeof(MENUITEMINFO));
|
|
inf->wID = tagid;
|
|
inf->fType = MFT_STRING;
|
|
inf->dwTypeData = strdup(name?name:"");
|
|
hMenu->items.Insert(pos,inf);
|
|
return 0;
|
|
}
|
|
|
|
bool DeleteMenu(HMENU hMenu, int idx, int flag)
|
|
{
|
|
if (WDL_NOT_NORMALLY(!hMenu)) return false;
|
|
if (flag&MF_BYPOSITION)
|
|
{
|
|
if (hMenu->items.Get(idx))
|
|
{
|
|
hMenu->items.Delete(idx,true,HMENU__::freeMenuItem);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
int x;
|
|
int cnt=0;
|
|
for (x=0;x<hMenu->items.GetSize(); x ++)
|
|
{
|
|
if (!hMenu->items.Get(x)->hSubMenu && hMenu->items.Get(x)->wID == (UINT)idx)
|
|
{
|
|
hMenu->items.Delete(x--,true,HMENU__::freeMenuItem);
|
|
cnt++;
|
|
}
|
|
}
|
|
if (!cnt)
|
|
{
|
|
for (x=0;x<hMenu->items.GetSize(); x ++)
|
|
{
|
|
if (hMenu->items.Get(x)->hSubMenu) cnt += DeleteMenu(hMenu->items.Get(x)->hSubMenu,idx,flag)?1:0;
|
|
}
|
|
}
|
|
return !!cnt;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL SetMenuItemInfo(HMENU hMenu, int pos, BOOL byPos, MENUITEMINFO *mi)
|
|
{
|
|
if (WDL_NOT_NORMALLY(!hMenu)) return 0;
|
|
MENUITEMINFO *item = byPos ? hMenu->items.Get(pos) : GetMenuItemByID(hMenu, pos, true);
|
|
if (!item) return 0;
|
|
|
|
if ((mi->fMask & MIIM_SUBMENU) && mi->hSubMenu != item->hSubMenu)
|
|
{
|
|
if (item->hSubMenu) item->hSubMenu->Release();
|
|
item->hSubMenu = mi->hSubMenu;
|
|
}
|
|
if (mi->fMask & MIIM_TYPE)
|
|
{
|
|
const bool wasString = MenuIsStringType(item), isString = MenuIsStringType(mi);
|
|
if (wasString != isString)
|
|
{
|
|
if (wasString) free(item->dwTypeData);
|
|
item->dwTypeData=NULL;
|
|
}
|
|
|
|
if (mi->fType == MFT_BITMAP) item->dwTypeData = mi->dwTypeData;
|
|
else if (isString && mi->dwTypeData)
|
|
{
|
|
free(item->dwTypeData);
|
|
item->dwTypeData = strdup(mi->dwTypeData);
|
|
}
|
|
item->fType = mi->fType;
|
|
}
|
|
|
|
if (mi->fMask & MIIM_STATE) item->fState = mi->fState;
|
|
if (mi->fMask & MIIM_ID) item->wID = mi->wID;
|
|
if (mi->fMask & MIIM_DATA) item->dwItemData = mi->dwItemData;
|
|
if ((mi->fMask & MIIM_BITMAP) && mi->cbSize >= sizeof(*mi)) item->hbmpItem = mi->hbmpItem;
|
|
|
|
return true;
|
|
}
|
|
|
|
BOOL GetMenuItemInfo(HMENU hMenu, int pos, BOOL byPos, MENUITEMINFO *mi)
|
|
{
|
|
if (WDL_NOT_NORMALLY(!hMenu)) return 0;
|
|
MENUITEMINFO *item = byPos ? hMenu->items.Get(pos) : GetMenuItemByID(hMenu, pos, true);
|
|
if (!item) return 0;
|
|
|
|
if (mi->fMask & MIIM_TYPE)
|
|
{
|
|
mi->fType = item->fType;
|
|
if (MenuIsStringType(mi) && mi->dwTypeData && mi->cch)
|
|
{
|
|
lstrcpyn_safe(mi->dwTypeData,item->dwTypeData?item->dwTypeData:"",mi->cch);
|
|
}
|
|
else if (item->fType == MFT_BITMAP) mi->dwTypeData = item->dwTypeData;
|
|
}
|
|
|
|
if (mi->fMask & MIIM_DATA) mi->dwItemData = item->dwItemData;
|
|
if (mi->fMask & MIIM_STATE) mi->fState = item->fState;
|
|
if (mi->fMask & MIIM_ID) mi->wID = item->wID;
|
|
if (mi->fMask & MIIM_SUBMENU) mi->hSubMenu = item->hSubMenu;
|
|
if ((mi->fMask & MIIM_BITMAP) && mi->cbSize >= sizeof(*mi)) mi->hbmpItem = item->hbmpItem;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
void SWELL_InsertMenu(HMENU menu, int pos, unsigned int flag, UINT_PTR idx, const char *str)
|
|
{
|
|
MENUITEMINFO mi={sizeof(mi),MIIM_ID|MIIM_STATE|MIIM_TYPE,MFT_STRING,
|
|
(flag & ~MF_BYPOSITION),(flag&MF_POPUP) ? 0 : (UINT)idx,NULL,NULL,NULL,0,(char *)str};
|
|
|
|
if (flag&MF_POPUP)
|
|
{
|
|
mi.hSubMenu = (HMENU)idx;
|
|
mi.fMask |= MIIM_SUBMENU;
|
|
mi.fState &= ~MF_POPUP;
|
|
}
|
|
|
|
if (flag&MF_SEPARATOR)
|
|
{
|
|
mi.fMask=MIIM_TYPE;
|
|
mi.fType=MFT_SEPARATOR;
|
|
mi.fState &= ~MF_SEPARATOR;
|
|
}
|
|
|
|
if (flag&MF_BITMAP)
|
|
{
|
|
mi.fType=MFT_BITMAP;
|
|
mi.fState &= ~MF_BITMAP;
|
|
}
|
|
|
|
InsertMenuItem(menu,pos,(flag&MF_BYPOSITION) ? TRUE : FALSE, &mi);
|
|
}
|
|
|
|
void InsertMenuItem(HMENU hMenu, int pos, BOOL byPos, MENUITEMINFO *mi)
|
|
{
|
|
if (WDL_NOT_NORMALLY(!hMenu)) return;
|
|
int ni=hMenu->items.GetSize();
|
|
|
|
if (!byPos)
|
|
{
|
|
int x;
|
|
for (x=0;x<ni && hMenu->items.Get(x)->wID != (UINT)pos; x++);
|
|
pos = x;
|
|
}
|
|
if (pos < 0 || pos > ni) pos=ni;
|
|
|
|
MENUITEMINFO *inf = (MENUITEMINFO*)calloc(sizeof(MENUITEMINFO),1);
|
|
inf->fType = mi->fType;
|
|
if (MenuIsStringType(inf))
|
|
{
|
|
inf->dwTypeData = strdup(mi->dwTypeData?mi->dwTypeData:"");
|
|
}
|
|
else if (mi->fType == MFT_BITMAP)
|
|
{
|
|
inf->dwTypeData = mi->dwTypeData;
|
|
}
|
|
else if (mi->fType == MFT_SEPARATOR)
|
|
{
|
|
}
|
|
if (mi->fMask&MIIM_SUBMENU) inf->hSubMenu = mi->hSubMenu;
|
|
if (mi->fMask & MIIM_STATE) inf->fState = mi->fState;
|
|
if (mi->fMask & MIIM_DATA) inf->dwItemData = mi->dwItemData;
|
|
if (mi->fMask & MIIM_ID) inf->wID = mi->wID;
|
|
if ((mi->fMask & MIIM_BITMAP) && mi->cbSize >= sizeof(*mi)) inf->hbmpItem = mi->hbmpItem;
|
|
|
|
hMenu->items.Insert(pos,inf);
|
|
}
|
|
|
|
|
|
void SWELL_SetMenuDestination(HMENU menu, HWND hwnd)
|
|
{
|
|
// only needed for Cocoa
|
|
}
|
|
|
|
extern RECT g_trackpopup_yroot;
|
|
static POINT m_trackingPt, m_trackingPt2;
|
|
static int m_trackingMouseFlag;
|
|
static int m_trackingFlags,m_trackingRet;
|
|
static HWND m_trackingPar;
|
|
static WDL_PtrList<HWND__> m_trackingMenus; // each HWND as userdata = HMENU
|
|
|
|
int swell_delegate_menu_message(HWND src, LPARAM lParam, int msg, bool screencoords)
|
|
{
|
|
static bool _reent;
|
|
if (_reent) return 0;
|
|
|
|
_reent = true;
|
|
|
|
POINT sp = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
if (!screencoords) ClientToScreen(src,&sp);
|
|
|
|
for (int x = m_trackingMenus.GetSize()-1; x>=0; x--)
|
|
{
|
|
HWND sw = m_trackingMenus.Get(x);
|
|
if (!sw) continue;
|
|
|
|
if (sw == src) break; // stop searching (don't delegate to parent)
|
|
|
|
RECT r;
|
|
GetWindowRect(sw,&r);
|
|
if (PtInRect(&r,sp))
|
|
{
|
|
POINT p = sp;
|
|
ScreenToClient(sw,&p);
|
|
SendMessage(sw,msg,0,MAKELPARAM(p.x,p.y));
|
|
_reent = false;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
_reent = false;
|
|
return 0;
|
|
}
|
|
|
|
bool swell_isOSwindowmenu(SWELL_OSWINDOW osw)
|
|
{
|
|
int x = m_trackingMenus.GetSize();
|
|
if (osw) while (--x>=0)
|
|
{
|
|
HWND__ *p = m_trackingMenus.Get(x);
|
|
if (p->m_oswindow == osw) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int menuBarNavigate(int dir); // -1 if no menu bar active, 0 if did nothing, 1 if navigated
|
|
HWND GetFocusIncludeMenus(void);
|
|
static DWORD swell_menu_ignore_mousemove_from;
|
|
|
|
static LRESULT WINAPI submenuWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static int lcol, rcol, mcol, top_margin, separator_ht, text_ht_pad, bitmap_ht_pad, scroll_margin, item_bm_pad;
|
|
if (!lcol)
|
|
{
|
|
lcol=SWELL_UI_SCALE(30); rcol=SWELL_UI_SCALE(18); mcol=SWELL_UI_SCALE(10);
|
|
top_margin=SWELL_UI_SCALE(4); separator_ht=SWELL_UI_SCALE(8);
|
|
text_ht_pad=SWELL_UI_SCALE(4); bitmap_ht_pad=SWELL_UI_SCALE(4);
|
|
scroll_margin=SWELL_UI_SCALE(10);
|
|
item_bm_pad = SWELL_UI_SCALE(4);
|
|
}
|
|
switch (uMsg)
|
|
{
|
|
case WM_CREATE:
|
|
hwnd->m_classname = "__SWELL_MENU";
|
|
hwnd->m_style = WS_CHILD;
|
|
m_trackingMenus.Add(hwnd);
|
|
SetWindowLongPtr(hwnd,GWLP_USERDATA,lParam);
|
|
|
|
if (m_trackingPar && !(m_trackingFlags&TPM_NONOTIFY))
|
|
SendMessage(m_trackingPar,WM_INITMENUPOPUP,(WPARAM)lParam,0);
|
|
|
|
{
|
|
HDC hdc = GetDC(hwnd);
|
|
HMENU__ *menu = (HMENU__*)lParam;
|
|
int ht = 0, wid=SWELL_UI_SCALE(100),wid2=0;
|
|
int x;
|
|
for (x=0; x < menu->items.GetSize(); x++)
|
|
{
|
|
MENUITEMINFO *inf = menu->items.Get(x);
|
|
BITMAP bm2={0,};
|
|
if (inf->hbmpItem)
|
|
GetObject(inf->hbmpItem,sizeof(bm2),&bm2);
|
|
|
|
if (MenuIsStringType(inf))
|
|
{
|
|
RECT r={0,};
|
|
const char *str = inf->dwTypeData;
|
|
if (!str || !*str) str="XXXXX";
|
|
const char *pt2 = strstr(str,"\t");
|
|
DrawText(hdc,str,pt2 ? (int)(pt2-str) : -1,&r,DT_CALCRECT|DT_SINGLELINE);
|
|
if (r.bottom < bm2.bmHeight) r.bottom = bm2.bmHeight;
|
|
if (bm2.bmWidth) r.right += bm2.bmWidth + item_bm_pad;
|
|
|
|
if (r.right > wid) wid=r.right;
|
|
ht += r.bottom + text_ht_pad;
|
|
|
|
if (pt2)
|
|
{
|
|
r.right=r.left;
|
|
DrawText(hdc,pt2+1,-1,&r,DT_CALCRECT|DT_SINGLELINE);
|
|
if (r.right > wid2) wid2=r.right;
|
|
}
|
|
}
|
|
else if (inf->fType == MFT_BITMAP)
|
|
{
|
|
BITMAP bm={16,16};
|
|
if (inf->dwTypeData) GetObject((HBITMAP)inf->dwTypeData,sizeof(bm),&bm);
|
|
if (bm.bmHeight < bm2.bmHeight) bm.bmHeight = bm2.bmHeight;
|
|
if (bm2.bmWidth) bm.bmWidth += bm2.bmWidth + item_bm_pad;
|
|
if (bm.bmWidth > wid) wid = bm.bmWidth;
|
|
|
|
ht += bm.bmHeight + bitmap_ht_pad;
|
|
}
|
|
else
|
|
{
|
|
// treat as separator, ignore bm2
|
|
ht += separator_ht;
|
|
}
|
|
}
|
|
wid+=lcol+rcol + (wid2?wid2+mcol:0);
|
|
ReleaseDC(hwnd,hdc);
|
|
|
|
const RECT ref={m_trackingPt.x, m_trackingPt.y, m_trackingPt.x, m_trackingPt.y };
|
|
RECT vp, tr={m_trackingPt.x,m_trackingPt.y, m_trackingPt.x+wid+SWELL_UI_SCALE(4),m_trackingPt.y+ht+top_margin * 2};
|
|
SWELL_GetViewPort(&vp,&ref,true);
|
|
vp.bottom -= 8;
|
|
|
|
if (g_trackpopup_yroot.bottom > g_trackpopup_yroot.top &&
|
|
g_trackpopup_yroot.bottom > vp.top &&
|
|
g_trackpopup_yroot.top < vp.bottom)
|
|
{
|
|
const int req_h = ht*9/8+32;
|
|
if (vp.bottom - g_trackpopup_yroot.bottom < req_h && g_trackpopup_yroot.top - vp.top >= req_h)
|
|
{
|
|
vp.bottom = g_trackpopup_yroot.top;
|
|
}
|
|
}
|
|
|
|
if (tr.bottom > vp.bottom) { tr.top += vp.bottom-tr.bottom; tr.bottom=vp.bottom; }
|
|
if (tr.right > vp.right)
|
|
{
|
|
if ((vp.right - m_trackingPt2.x) < (m_trackingPt2.x - vp.left))
|
|
{
|
|
tr.left = m_trackingPt2.x - (tr.right-tr.left);
|
|
tr.right = m_trackingPt2.x;
|
|
}
|
|
else
|
|
{
|
|
tr.left += vp.right-tr.right; tr.right=vp.right;
|
|
}
|
|
}
|
|
|
|
if (tr.left < vp.left) { tr.right += vp.left-tr.left; tr.left=vp.left; }
|
|
if (tr.top < vp.top) { tr.bottom += vp.top-tr.top; tr.top=vp.top; }
|
|
if (tr.bottom > vp.bottom) tr.bottom=vp.bottom;
|
|
if (tr.right > vp.right) tr.right=vp.right;
|
|
|
|
SetWindowPos(hwnd,NULL,tr.left,tr.top,tr.right-tr.left,tr.bottom-tr.top,SWP_NOZORDER);
|
|
|
|
hwnd->m_extra[0] = 0; // Y scroll offset
|
|
hwnd->m_extra[1] = 0; // is currently autoscrolling to sel_vis
|
|
hwnd->m_extra[2] = 0; // remaining items out of view, if any
|
|
}
|
|
|
|
ShowWindow(hwnd,SW_SHOW);
|
|
SetFocus(hwnd);
|
|
SetTimer(hwnd,1,100,NULL);
|
|
SetTimer(hwnd,2,15,NULL);
|
|
break;
|
|
case WM_PAINT:
|
|
{
|
|
PAINTSTRUCT ps;
|
|
if (BeginPaint(hwnd,&ps))
|
|
{
|
|
RECT cr;
|
|
GetClientRect(hwnd,&cr);
|
|
HBRUSH br = CreateSolidBrush(g_swell_ctheme.menu_bg);
|
|
HBRUSH br2 = CreateSolidBrushAlpha(g_swell_ctheme.menu_scroll,0.5f);
|
|
HBRUSH br3 = CreateSolidBrush(g_swell_ctheme.menu_scroll_arrow);
|
|
HBRUSH br_submenu_arrow = CreateSolidBrush(g_swell_ctheme.menu_submenu_arrow);
|
|
HBRUSH br_submenu_arrow_sel = CreateSolidBrush(g_swell_ctheme.menu_submenu_arrow_sel);
|
|
HPEN pen = CreatePen(PS_SOLID,0,g_swell_ctheme.menu_shadow);
|
|
HPEN pen2 = CreatePen(PS_SOLID,0,g_swell_ctheme.menu_hilight);
|
|
HGDIOBJ oldbr = SelectObject(ps.hdc,br);
|
|
HGDIOBJ oldpen = SelectObject(ps.hdc,pen2);
|
|
Rectangle(ps.hdc,cr.left,cr.top,cr.right,cr.bottom);
|
|
SetBkMode(ps.hdc,TRANSPARENT);
|
|
HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
|
|
int ypos = top_margin;
|
|
|
|
int extra_left = 0;
|
|
bool want_autoscroll_down = false;
|
|
const int ni = menu->items.GetSize();
|
|
int x;
|
|
for (x=wdl_max((int)hwnd->m_extra[0],0); x < ni; x++)
|
|
{
|
|
if (ypos >= cr.bottom)
|
|
{
|
|
extra_left = ni-x;
|
|
break;
|
|
}
|
|
MENUITEMINFO *inf = menu->items.Get(x);
|
|
if (WDL_NOT_NORMALLY(!inf)) break;
|
|
|
|
RECT r={lcol,ypos,cr.right, };
|
|
bool dis = !!(inf->fState & MF_GRAYED);
|
|
BITMAP bm={16,16}, bm2={0,};
|
|
if (inf->hbmpItem)
|
|
GetObject(inf->hbmpItem,sizeof(bm2),&bm2);
|
|
|
|
if (MenuIsStringType(inf))
|
|
{
|
|
const char *str = inf->dwTypeData;
|
|
if (!str || !*str) str="XXXXX";
|
|
RECT mr={0,};
|
|
DrawText(ps.hdc,str,-1,&mr,DT_CALCRECT|DT_SINGLELINE);
|
|
|
|
ypos += wdl_max(mr.bottom,bm2.bmHeight) + text_ht_pad;
|
|
r.bottom = ypos;
|
|
}
|
|
else if (inf->fType == MFT_BITMAP)
|
|
{
|
|
if (inf->dwTypeData) GetObject((HBITMAP)inf->dwTypeData,sizeof(bm),&bm);
|
|
|
|
ypos += wdl_max(bm.bmHeight,bm2.bmHeight) + bitmap_ht_pad;
|
|
r.bottom = ypos;
|
|
|
|
}
|
|
else
|
|
{
|
|
dis=true;
|
|
ypos += separator_ht;
|
|
r.bottom = ypos;
|
|
}
|
|
|
|
if (x == menu->sel_vis && !dis)
|
|
{
|
|
HBRUSH brs=CreateSolidBrush(g_swell_ctheme.menu_bg_sel);
|
|
RECT r2=r;
|
|
r2.left = cr.left + 1;
|
|
r2.right = r2.right - 1;
|
|
FillRect(ps.hdc,&r2,brs);
|
|
DeleteObject(brs);
|
|
SetTextColor(ps.hdc,g_swell_ctheme.menu_text_sel);
|
|
}
|
|
else
|
|
{
|
|
SetTextColor(ps.hdc,
|
|
dis ? g_swell_ctheme.menu_text_disabled :
|
|
g_swell_ctheme.menu_text);
|
|
}
|
|
|
|
if (bm2.bmWidth)
|
|
{
|
|
RECT tr = r;
|
|
tr.right = tr.left + bm2.bmWidth;
|
|
DrawImageInRect(ps.hdc,inf->hbmpItem,&tr);
|
|
|
|
r.left += bm2.bmWidth + item_bm_pad;
|
|
}
|
|
|
|
if (MenuIsStringType(inf))
|
|
{
|
|
const char *str = inf->dwTypeData;
|
|
if (!str) str="";
|
|
const char *pt2 = strstr(str,"\t");
|
|
|
|
if (*str)
|
|
{
|
|
DrawText(ps.hdc,str,pt2 ? (int)(pt2-str) : -1,&r,DT_VCENTER|DT_SINGLELINE);
|
|
if (pt2)
|
|
{
|
|
RECT tr=r; tr.right-=rcol;
|
|
DrawText(ps.hdc,pt2+1,-1,&tr,DT_VCENTER|DT_SINGLELINE|DT_RIGHT);
|
|
}
|
|
}
|
|
}
|
|
else if (inf->fType == MFT_BITMAP)
|
|
{
|
|
if (inf->dwTypeData)
|
|
{
|
|
RECT tr = r;
|
|
tr.top += bitmap_ht_pad/2;
|
|
tr.right = tr.left + bm.bmWidth;
|
|
tr.bottom = tr.top + bm.bmHeight;
|
|
DrawImageInRect(ps.hdc,(HBITMAP)inf->dwTypeData,&tr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SelectObject(ps.hdc,pen2);
|
|
int margin = rcol / 2;
|
|
int y = r.top/2+r.bottom/2, left = cr.left+margin, right = r.right-margin;
|
|
MoveToEx(ps.hdc,left,y,NULL);
|
|
LineTo(ps.hdc,right,y);
|
|
SelectObject(ps.hdc,pen);
|
|
|
|
y++;
|
|
MoveToEx(ps.hdc,left,y,NULL);
|
|
LineTo(ps.hdc,right,y);
|
|
}
|
|
if (inf->hSubMenu)
|
|
{
|
|
const int sz = (r.bottom-r.top)/5, xp = r.right - rcol/2 - sz, yp = (r.top + r.bottom)/2;
|
|
|
|
POINT pts[3] = {
|
|
{xp, yp-sz},
|
|
{xp, yp+sz},
|
|
{xp + sz,yp}
|
|
};
|
|
HGDIOBJ oldPen = SelectObject(ps.hdc,GetStockObject(NULL_PEN));
|
|
|
|
if (x == menu->sel_vis && !dis)
|
|
SelectObject(ps.hdc,br_submenu_arrow_sel);
|
|
else
|
|
SelectObject(ps.hdc,br_submenu_arrow);
|
|
|
|
Polygon(ps.hdc,pts,3);
|
|
|
|
SelectObject(ps.hdc,oldPen);
|
|
|
|
|
|
}
|
|
if (inf->fState&MF_CHECKED)
|
|
{
|
|
int col;
|
|
if (x == menu->sel_vis && !dis)
|
|
col = g_swell_ctheme.menu_text_sel;
|
|
else
|
|
col = dis ? g_swell_ctheme.menu_text_disabled : g_swell_ctheme.menu_text;
|
|
|
|
HPEN tpen = CreatePen(PS_SOLID,0, col);
|
|
HBRUSH tbr = CreateSolidBrush(col);
|
|
HGDIOBJ oldBrush = SelectObject(ps.hdc,tbr);
|
|
HGDIOBJ oldPen = SelectObject(ps.hdc,tpen);
|
|
const int sz = (wdl_min(lcol, r.bottom-r.top) - SWELL_UI_SCALE(10));
|
|
const int xo = SWELL_UI_SCALE(4), yo = (r.bottom+r.top)/2 - sz/2;
|
|
if (inf->fType&MFT_RADIOCHECK)
|
|
{
|
|
Ellipse(ps.hdc, xo, yo, xo+sz, yo+sz);
|
|
}
|
|
else
|
|
{
|
|
static const unsigned char coords[12] = { 0, 76, 12, 64, 40, 92, 40, 118, 116, 16, 128, 28, };
|
|
for (int pass=0;pass<2;pass++)
|
|
{
|
|
POINT pts[4];
|
|
for (int i=0;i<4; i ++)
|
|
{
|
|
pts[i].x = lcol/2 + sz * ((int)coords[i*2+pass*4] - 64) / 128;
|
|
pts[i].y = (r.bottom+r.top)/2 + sz * ((int)coords[i*2+pass*4+1] - 64) / 128;
|
|
}
|
|
Polygon(ps.hdc,pts,4);
|
|
}
|
|
}
|
|
|
|
SelectObject(ps.hdc,oldPen);
|
|
SelectObject(ps.hdc,oldBrush);
|
|
DeleteObject(tpen);
|
|
DeleteObject(tbr);
|
|
}
|
|
if ((r.top+ypos)/2 > cr.bottom)
|
|
{
|
|
extra_left = ni-x;
|
|
}
|
|
if (ypos > cr.bottom && x == menu->sel_vis)
|
|
{
|
|
want_autoscroll_down = true;
|
|
extra_left = ni-x;
|
|
}
|
|
}
|
|
hwnd->m_extra[1] = want_autoscroll_down || x <= menu->sel_vis;
|
|
hwnd->m_extra[2] = extra_left;
|
|
|
|
|
|
// lower scroll indicator
|
|
int mid=(cr.right-cr.left)/2;
|
|
SelectObject(ps.hdc,GetStockObject(NULL_PEN));
|
|
SelectObject(ps.hdc,br3);
|
|
POINT pts[3];
|
|
const int smm = SWELL_UI_SCALE(2);
|
|
const int smh = scroll_margin-smm*2;
|
|
if (extra_left>0)
|
|
{
|
|
RECT fr = {cr.left, cr.bottom-scroll_margin, cr.right,cr.bottom};
|
|
FillRect(ps.hdc,&fr,br2);
|
|
pts[0].x = mid; pts[0].y = cr.bottom - smm;
|
|
pts[1].x = mid-smh; pts[1].y = pts[0].y - smh;
|
|
pts[2].x = mid+smh; pts[2].y = pts[1].y;
|
|
Polygon(ps.hdc,pts,3);
|
|
}
|
|
// upper scroll indicator
|
|
if (hwnd->m_extra[0] > 0)
|
|
{
|
|
RECT fr = {cr.left, cr.top, cr.right, cr.top+scroll_margin};
|
|
FillRect(ps.hdc,&fr,br2);
|
|
|
|
pts[0].x = mid; pts[0].y = cr.top + smm;
|
|
pts[1].x = mid-smh; pts[1].y = pts[0].y + smh;
|
|
pts[2].x = mid+smh; pts[2].y = pts[1].y;
|
|
Polygon(ps.hdc,pts,3);
|
|
}
|
|
|
|
SelectObject(ps.hdc,oldbr);
|
|
SelectObject(ps.hdc,oldpen);
|
|
DeleteObject(br);
|
|
DeleteObject(br2);
|
|
DeleteObject(br3);
|
|
DeleteObject(br_submenu_arrow);
|
|
DeleteObject(br_submenu_arrow_sel);
|
|
DeleteObject(pen);
|
|
DeleteObject(pen2);
|
|
EndPaint(hwnd,&ps);
|
|
}
|
|
}
|
|
break;
|
|
case WM_TIMER:
|
|
if (wParam==1)
|
|
{
|
|
HWND h = GetFocusIncludeMenus();
|
|
if (h!=hwnd)
|
|
{
|
|
int a = h ? m_trackingMenus.Find(h) : -1;
|
|
if (a<0 || a < m_trackingMenus.Find(hwnd))
|
|
{
|
|
if (m_trackingMouseFlag && m_trackingMenus.Get(0))
|
|
{
|
|
SetFocus(m_trackingMenus.Get(0));
|
|
m_trackingMouseFlag=0;
|
|
}
|
|
else DestroyWindow(hwnd);
|
|
}
|
|
}
|
|
}
|
|
else if (wParam == 2)
|
|
{
|
|
// menu scroll
|
|
RECT tr;
|
|
GetWindowRect(hwnd,&tr);
|
|
|
|
POINT curM;
|
|
GetCursorPos(&curM);
|
|
const bool xmatch = (curM.x >= tr.left && curM.x < tr.right);
|
|
if (xmatch || (hwnd->m_extra[1] && hwnd->m_extra[2]))
|
|
{
|
|
HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
|
|
int xFirst = wdl_max(hwnd->m_extra[0],0);
|
|
const bool ymatch = curM.y >= tr.bottom-scroll_margin && curM.y < tr.bottom+scroll_margin;
|
|
if (hwnd->m_extra[2] && (hwnd->m_extra[1] || ymatch))
|
|
{
|
|
if (hwnd->m_extra[1] && menu->sel_vis > 0)
|
|
{
|
|
const int li = menu->items.GetSize() - (int)hwnd->m_extra[2];
|
|
const int incr = (menu->sel_vis - li)/2 + 2;
|
|
xFirst += wdl_max(incr,1);
|
|
}
|
|
else
|
|
xFirst++;
|
|
hwnd->m_extra[0]=xFirst;
|
|
hwnd->m_extra[1]=hwnd->m_extra[2]=0;
|
|
if (ymatch) menu->sel_vis=-1;
|
|
InvalidateRect(hwnd,NULL,FALSE);
|
|
}
|
|
else if (xFirst > 0 && curM.y >= tr.top-scroll_margin && curM.y < tr.top+scroll_margin)
|
|
{
|
|
hwnd->m_extra[0]=--xFirst;
|
|
menu->sel_vis=-1;
|
|
InvalidateRect(hwnd,NULL,FALSE);
|
|
}
|
|
}
|
|
}
|
|
else if (wParam == 5)
|
|
{
|
|
KillTimer(hwnd,5);
|
|
|
|
RECT r;
|
|
POINT pt;
|
|
GetCursorPos(&pt);
|
|
ScreenToClient(hwnd,&pt);
|
|
GetClientRect(hwnd,&r);
|
|
if (PtInRect(&r,pt))
|
|
SendMessage(hwnd,WM_USER+100,4,pt.y);
|
|
}
|
|
break;
|
|
case WM_KEYUP:
|
|
return 1;
|
|
case WM_MOUSEWHEEL:
|
|
{
|
|
RECT tr;
|
|
GetWindowRect(hwnd,&tr);
|
|
|
|
POINT curM;
|
|
GetCursorPos(&curM);
|
|
const bool xmatch = (curM.x >= tr.left && curM.x < tr.right);
|
|
const bool ymatch = curM.y >= tr.bottom-scroll_margin && curM.y < tr.bottom+scroll_margin;
|
|
if (!xmatch || !ymatch) // if mouse is over scroll area, ignore
|
|
{
|
|
int amt = ((short)HIWORD(wParam))/-40;
|
|
const int xFirst = wdl_max(hwnd->m_extra[0],0);
|
|
if (amt > hwnd->m_extra[2]) amt = (int) hwnd->m_extra[2];
|
|
if (amt < 0 ? xFirst > 0 : amt > 0)
|
|
{
|
|
hwnd->m_extra[0]=wdl_max(xFirst+amt,0);
|
|
if (amt>0)
|
|
{
|
|
hwnd->m_extra[2] -= amt;
|
|
if (hwnd->m_extra[2]<0) hwnd->m_extra[2]=0;
|
|
}
|
|
HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
|
|
menu->sel_vis=-1; // clear selection
|
|
InvalidateRect(hwnd,NULL,FALSE);
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
|
|
case WM_KEYDOWN:
|
|
|
|
if (wParam >= VK_SPACE && wParam < 0x80)
|
|
{
|
|
// ignore mousemoves immediately after most keys
|
|
swell_menu_ignore_mousemove_from = GetTickCount();
|
|
}
|
|
|
|
if (wParam == VK_ESCAPE || wParam == VK_LEFT)
|
|
{
|
|
HWND l = m_trackingMenus.Get(m_trackingMenus.Find(hwnd)-1);
|
|
if (l) SetFocus(l);
|
|
else
|
|
{
|
|
if (wParam != VK_LEFT || menuBarNavigate(-1) < 0)
|
|
DestroyWindow(hwnd);
|
|
}
|
|
}
|
|
else if (wParam == VK_RETURN || wParam == VK_RIGHT)
|
|
{
|
|
HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
|
|
if (wParam == VK_RIGHT)
|
|
{
|
|
MENUITEMINFO *inf = menu->items.Get(menu->sel_vis);
|
|
if (!inf || !inf->hSubMenu)
|
|
{
|
|
menuBarNavigate(1);
|
|
return 1;
|
|
}
|
|
}
|
|
SendMessage(hwnd,WM_USER+100,1,menu->sel_vis);
|
|
}
|
|
else if (wParam == VK_UP || wParam == VK_PRIOR)
|
|
{
|
|
HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
|
|
int l = menu->sel_vis;
|
|
for (int i= wParam == VK_UP ? 0 : 9; i>=0; i--)
|
|
{
|
|
int mc = menu->items.GetSize();
|
|
while (mc--)
|
|
{
|
|
if (l<1)
|
|
{
|
|
if (wParam != VK_UP) break;
|
|
l = menu->items.GetSize();
|
|
}
|
|
MENUITEMINFO *inf = menu->items.Get(--l);
|
|
if (!inf) break;
|
|
if (!(inf->fState & MF_GRAYED) && inf->fType != MFT_SEPARATOR)
|
|
{
|
|
menu->sel_vis=l;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (menu->sel_vis < hwnd->m_extra[0])
|
|
hwnd->m_extra[0] = menu->sel_vis;
|
|
InvalidateRect(hwnd,NULL,FALSE);
|
|
}
|
|
else if (wParam == VK_DOWN || wParam == VK_NEXT)
|
|
{
|
|
HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
|
|
int l = menu->sel_vis;
|
|
const int n =menu->items.GetSize()-1;
|
|
for (int i = wParam == VK_DOWN ? 0 : 9; i>=0; i--)
|
|
{
|
|
int mc = n+1;
|
|
while (mc--)
|
|
{
|
|
if (l>=n)
|
|
{
|
|
if (wParam != VK_DOWN) break;
|
|
l=-1;
|
|
hwnd->m_extra[0]=0;
|
|
}
|
|
MENUITEMINFO *inf = menu->items.Get(++l);
|
|
if (!inf) break;
|
|
if (!(inf->fState & MF_GRAYED) && inf->fType != MFT_SEPARATOR)
|
|
{
|
|
menu->sel_vis=l;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
InvalidateRect(hwnd,NULL,FALSE);
|
|
}
|
|
else if (wParam == VK_END)
|
|
{
|
|
HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
|
|
int l = menu->items.GetSize();
|
|
while (l > 0)
|
|
{
|
|
MENUITEMINFO *inf = menu->items.Get(--l);
|
|
if (!inf) break;
|
|
if (!(inf->fState & MF_GRAYED) && inf->fType != MFT_SEPARATOR)
|
|
{
|
|
menu->sel_vis=l;
|
|
break;
|
|
}
|
|
}
|
|
if (menu->sel_vis < hwnd->m_extra[0])
|
|
hwnd->m_extra[0] = menu->sel_vis;
|
|
InvalidateRect(hwnd,NULL,FALSE);
|
|
}
|
|
else if (wParam == VK_HOME)
|
|
{
|
|
HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
|
|
int l = 0;
|
|
while (l < menu->items.GetSize())
|
|
{
|
|
MENUITEMINFO *inf = menu->items.Get(l++);
|
|
if (!inf) break;
|
|
if (!(inf->fState & MF_GRAYED) && inf->fType != MFT_SEPARATOR)
|
|
{
|
|
menu->sel_vis=l-1;
|
|
break;
|
|
}
|
|
}
|
|
if (menu->sel_vis < hwnd->m_extra[0])
|
|
hwnd->m_extra[0] = menu->sel_vis;
|
|
InvalidateRect(hwnd,NULL,FALSE);
|
|
}
|
|
else if ((lParam & FVIRTKEY) && (
|
|
(wParam >= 'A' && wParam <= 'Z') ||
|
|
(wParam >= '0' && wParam <= '9')))
|
|
{
|
|
HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
|
|
const int n=menu->items.GetSize();
|
|
|
|
int offs = menu->sel_vis+1;
|
|
if (offs<0||offs>=n) offs=0;
|
|
int matchcnt=0;
|
|
for(int x=0;x<n+n;x++)
|
|
{
|
|
MENUITEMINFO *inf = menu->items.Get(offs);
|
|
if (MenuIsStringType(inf) &&
|
|
!(inf->fState & MF_GRAYED) &&
|
|
inf->dwTypeData)
|
|
{
|
|
const char *p = inf->dwTypeData;
|
|
bool is_prefix_mode = x<n;
|
|
if (!is_prefix_mode && matchcnt)
|
|
{
|
|
if (matchcnt == 1)
|
|
{
|
|
// implies prefix mode, only one matching item
|
|
SendMessage(hwnd,WM_USER+100,1,menu->sel_vis);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (is_prefix_mode) while (*p)
|
|
{
|
|
if (*p++ == '&')
|
|
{
|
|
if (*p != '&') break;
|
|
p++;
|
|
}
|
|
}
|
|
|
|
if (*p > 0 && (WPARAM)toupper(*p) == wParam)
|
|
{
|
|
if (!matchcnt++)
|
|
{
|
|
menu->sel_vis = offs;
|
|
if (menu->sel_vis < hwnd->m_extra[0])
|
|
hwnd->m_extra[0] = menu->sel_vis;
|
|
InvalidateRect(hwnd,NULL,FALSE);
|
|
}
|
|
if (!is_prefix_mode) break;
|
|
}
|
|
}
|
|
if (++offs >= n) offs=0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
case WM_DESTROY:
|
|
{
|
|
int a = m_trackingMenus.Find(hwnd);
|
|
m_trackingMenus.Delete(a);
|
|
if (m_trackingMenus.Get(a)) DestroyWindow(m_trackingMenus.Get(a));
|
|
RemoveProp(hwnd,"SWELL_MenuOwner");
|
|
}
|
|
break;
|
|
case WM_USER+100:
|
|
if (wParam == 1 || wParam == 2 || wParam == 3 || wParam == 4)
|
|
{
|
|
int which = (int) lParam;
|
|
int item_ypos = which;
|
|
|
|
HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
|
|
|
|
int ht = top_margin;
|
|
HDC hdc=GetDC(hwnd);
|
|
if (wParam > 1) which = -1;
|
|
else item_ypos = 0;
|
|
for (int x=wdl_max(hwnd->m_extra[0],0); x < (menu->items.GetSize()); x++)
|
|
{
|
|
if (wParam == 1 && which == x) { item_ypos = ht; break; }
|
|
MENUITEMINFO *inf = menu->items.Get(x);
|
|
int lastht = ht;
|
|
BITMAP bm2={0,};
|
|
if (inf->hbmpItem)
|
|
GetObject(inf->hbmpItem,sizeof(bm2),&bm2);
|
|
|
|
if (MenuIsStringType(inf))
|
|
{
|
|
RECT r={0,};
|
|
const char *str = inf->dwTypeData;
|
|
if (!str || !*str) str="XXXXX";
|
|
const char *pt2 = strstr(str,"\t");
|
|
DrawText(hdc,str,pt2 ? (int)(pt2-str) : -1,&r,DT_CALCRECT|DT_SINGLELINE);
|
|
ht += wdl_max(r.bottom,bm2.bmHeight) + text_ht_pad;
|
|
}
|
|
else if (inf->fType == MFT_BITMAP)
|
|
{
|
|
BITMAP bm={16,16};
|
|
if (inf->dwTypeData) GetObject((HBITMAP)inf->dwTypeData,sizeof(bm),&bm);
|
|
ht += wdl_max(bm.bmHeight,bm2.bmHeight) + bitmap_ht_pad;
|
|
}
|
|
else
|
|
{
|
|
ht += separator_ht;
|
|
}
|
|
if (wParam > 1 && item_ypos < ht)
|
|
{
|
|
item_ypos = lastht;
|
|
which = x;
|
|
if (wParam == 4 && inf->hSubMenu)
|
|
{
|
|
HWND nextmenu = m_trackingMenus.Get(m_trackingMenus.Find(hwnd)+1);
|
|
if (!nextmenu || GetWindowLongPtr(nextmenu,GWLP_USERDATA) != (LPARAM)inf->hSubMenu)
|
|
{
|
|
wParam = 1; // activate if not already visible
|
|
menu->sel_vis = which;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
ReleaseDC(hwnd,hdc);
|
|
if (wParam == 4)
|
|
{
|
|
MENUITEMINFO *inf = menu->items.Get(which);
|
|
if (inf)
|
|
{
|
|
HWND next = m_trackingMenus.Get(m_trackingMenus.Find(hwnd)+1);
|
|
if (next && (!inf->hSubMenu || (LPARAM)inf->hSubMenu != GetWindowLongPtr(next,GWLP_USERDATA)))
|
|
DestroyWindow(next);
|
|
}
|
|
menu->sel_vis = which;
|
|
return 0;
|
|
}
|
|
if (wParam == 3)
|
|
{
|
|
if (menu->sel_vis != which)
|
|
{
|
|
SetTimer(hwnd,5,300,NULL);
|
|
menu->sel_vis = which;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
MENUITEMINFO *inf = menu->items.Get(which);
|
|
|
|
if (inf)
|
|
{
|
|
if (inf->fState&MF_GRAYED){ }
|
|
else if (inf->hSubMenu)
|
|
{
|
|
const int nextidx = m_trackingMenus.Find(hwnd)+1;
|
|
HWND hh = m_trackingMenus.Get(nextidx);
|
|
|
|
inf->hSubMenu->sel_vis=-1;
|
|
|
|
if (hh)
|
|
{
|
|
m_trackingMenus.Delete(nextidx);
|
|
int a = m_trackingMenus.GetSize();
|
|
while (a > nextidx) DestroyWindow(m_trackingMenus.Get(--a));
|
|
}
|
|
else
|
|
{
|
|
hh = new HWND__(NULL,0,NULL,"menu",false,submenuWndProc,NULL, hwnd);
|
|
SetProp(hh,"SWELL_MenuOwner",GetProp(hwnd,"SWELL_MenuOwner"));
|
|
}
|
|
|
|
RECT r;
|
|
GetClientRect(hwnd,&r);
|
|
m_trackingPt.x=r.right;
|
|
m_trackingPt.y=item_ypos;
|
|
m_trackingPt2.x=r.left;
|
|
m_trackingPt2.y=item_ypos;
|
|
ClientToScreen(hwnd,&m_trackingPt);
|
|
ClientToScreen(hwnd,&m_trackingPt2);
|
|
|
|
submenuWndProc(hh, WM_CREATE,0,(LPARAM)inf->hSubMenu);
|
|
InvalidateRect(hwnd,NULL,FALSE);
|
|
}
|
|
else if (inf->wID) m_trackingRet = inf->wID;
|
|
}
|
|
}
|
|
return 0;
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
if ((GetTickCount() - swell_menu_ignore_mousemove_from)<200) return 0;
|
|
|
|
if (swell_delegate_menu_message(hwnd, lParam,uMsg, false))
|
|
return 0;
|
|
|
|
RECT r;
|
|
GetClientRect(hwnd,&r);
|
|
HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
|
|
const int oldsel = menu->sel_vis;
|
|
if (GET_X_LPARAM(lParam)>=r.left &&
|
|
GET_X_LPARAM(lParam)<r.right &&
|
|
GET_Y_LPARAM(lParam)>top_margin
|
|
)
|
|
{
|
|
int a = m_trackingMenus.Find(hwnd);
|
|
if (a>=0)
|
|
{
|
|
// step back through and make sure each menu has the correct item selected
|
|
HMENU__ *m = menu;
|
|
while (--a>=0)
|
|
{
|
|
HWND h = m_trackingMenus.Get(a);
|
|
if (WDL_NOT_NORMALLY(!h)) break;
|
|
HMENU__ *m2 = (HMENU__*)GetWindowLongPtr(h,GWLP_USERDATA);
|
|
if (WDL_NOT_NORMALLY(!m2)) break;
|
|
|
|
MENUITEMINFO *s = m2->items.Get(m2->sel_vis);
|
|
if (!s || s->hSubMenu != m)
|
|
{
|
|
// find m in menu, select, invalidate
|
|
for (int x = 0; x < m2->items.GetSize(); x ++)
|
|
{
|
|
s = m2->items.Get(x);
|
|
if (s && s->hSubMenu == m)
|
|
{
|
|
m2->sel_vis = x;
|
|
InvalidateRect(h,NULL,FALSE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
m = m2;
|
|
}
|
|
}
|
|
int mode = 3; // 4 was old-style super-fast menus
|
|
SendMessage(hwnd,WM_USER+100,mode,GET_Y_LPARAM(lParam));
|
|
}
|
|
else menu->sel_vis = -1;
|
|
if (oldsel != menu->sel_vis) InvalidateRect(hwnd,NULL,FALSE);
|
|
}
|
|
return 0;
|
|
case WM_LBUTTONUP:
|
|
case WM_RBUTTONUP:
|
|
{
|
|
if (swell_delegate_menu_message(hwnd, lParam, uMsg, false))
|
|
return 0;
|
|
|
|
RECT r;
|
|
GetClientRect(hwnd,&r);
|
|
if (GET_X_LPARAM(lParam)>=r.left &&
|
|
GET_X_LPARAM(lParam)<r.right &&
|
|
GET_Y_LPARAM(lParam)>top_margin
|
|
)
|
|
{
|
|
SendMessage(hwnd,WM_USER+100,2,GET_Y_LPARAM(lParam));
|
|
return 0;
|
|
}
|
|
else DestroyWindow(hwnd);
|
|
}
|
|
return 0;
|
|
}
|
|
return DefWindowProc(hwnd,uMsg,wParam,lParam);
|
|
}
|
|
|
|
bool PopupMenuIsActive(void)
|
|
{
|
|
return m_trackingMenus.GetSize()>0;
|
|
}
|
|
|
|
bool DestroyPopupMenus()
|
|
{
|
|
if (!m_trackingMenus.GetSize()) return false;
|
|
DestroyWindow(m_trackingMenus.Get(0));
|
|
return true;
|
|
}
|
|
|
|
SWELL_OSWINDOW swell_ignore_focus_oswindow;
|
|
DWORD swell_ignore_focus_oswindow_until;
|
|
|
|
int TrackPopupMenu(HMENU hMenu, int flags, int xpos, int ypos, int resvd, HWND hwnd, const RECT *r)
|
|
{
|
|
if (WDL_NOT_NORMALLY(!hMenu) || m_trackingMenus.GetSize()) return 0;
|
|
|
|
ReleaseCapture();
|
|
|
|
hMenu->Retain();
|
|
m_trackingPar=hwnd;
|
|
m_trackingFlags=flags;
|
|
m_trackingRet=-1;
|
|
m_trackingPt2.x=m_trackingPt.x=xpos;
|
|
m_trackingPt2.y=m_trackingPt.y=ypos;
|
|
m_trackingMouseFlag = 0;
|
|
if (GetAsyncKeyState(VK_LBUTTON)) m_trackingMouseFlag |= 1;
|
|
if (GetAsyncKeyState(VK_RBUTTON)) m_trackingMouseFlag |= 2;
|
|
if (GetAsyncKeyState(VK_MBUTTON)) m_trackingMouseFlag |= 4;
|
|
|
|
// HWND oldFoc = GetFocus();
|
|
// bool oldFoc_child = oldFoc && (IsChild(hwnd,oldFoc) || oldFoc == hwnd || oldFoc==GetParent(hwnd));
|
|
|
|
if (hwnd)
|
|
{
|
|
hwnd->Retain();
|
|
swell_ignore_focus_oswindow = hwnd->m_oswindow;
|
|
swell_ignore_focus_oswindow_until = GetTickCount()+500;
|
|
}
|
|
|
|
|
|
if (r && r->left == (1<<30) && r->top == (1<<30) && !r->right)
|
|
hMenu->sel_vis = r->bottom;
|
|
else
|
|
hMenu->sel_vis=-1;
|
|
|
|
if (!resvd || resvd == 0xbeee) swell_menu_ignore_mousemove_from = GetTickCount();
|
|
|
|
HWND hh=new HWND__(NULL,0,NULL,"menu",false,submenuWndProc,NULL, hwnd);
|
|
|
|
submenuWndProc(hh,WM_CREATE,0,(LPARAM)hMenu);
|
|
|
|
SetProp(hh,"SWELL_MenuOwner",(HANDLE)hwnd);
|
|
|
|
while (m_trackingRet<0 && m_trackingMenus.GetSize())
|
|
{
|
|
void SWELL_RunMessageLoop();
|
|
SWELL_RunMessageLoop();
|
|
Sleep(10);
|
|
}
|
|
|
|
int x=m_trackingMenus.GetSize()-1;
|
|
while (x>=0)
|
|
{
|
|
HWND h = m_trackingMenus.Get(x);
|
|
m_trackingMenus.Delete(x);
|
|
if (h) DestroyWindow(h);
|
|
x--;
|
|
}
|
|
|
|
// if (oldFoc_child) SetFocus(oldFoc);
|
|
|
|
if (!(flags&TPM_RETURNCMD) && m_trackingRet>0)
|
|
SendMessage(hwnd,WM_COMMAND,m_trackingRet,0);
|
|
|
|
if (hwnd) hwnd->Release();
|
|
|
|
swell_ignore_focus_oswindow = NULL;
|
|
hMenu->Release();
|
|
|
|
m_trackingPar = NULL;
|
|
|
|
if (flags & TPM_RETURNCMD) return m_trackingRet>0?m_trackingRet:0;
|
|
|
|
return (resvd|1)!=0xbeef || m_trackingRet>0;
|
|
}
|
|
|
|
|
|
|
|
|
|
void SWELL_Menu_AddMenuItem(HMENU hMenu, const char *name, int idx, unsigned int flags)
|
|
{
|
|
MENUITEMINFO mi={sizeof(mi),MIIM_ID|MIIM_STATE|MIIM_TYPE,MFT_STRING,
|
|
(UINT)((flags)?MFS_GRAYED:0),(UINT)idx,NULL,NULL,NULL,0,(char *)name};
|
|
if (!name)
|
|
{
|
|
mi.fType = MFT_SEPARATOR;
|
|
mi.fMask&=~(MIIM_STATE|MIIM_ID);
|
|
}
|
|
InsertMenuItem(hMenu,GetMenuItemCount(hMenu),TRUE,&mi);
|
|
}
|
|
|
|
|
|
SWELL_MenuResourceIndex *SWELL_curmodule_menuresource_head; // todo: move to per-module thingy
|
|
|
|
static SWELL_MenuResourceIndex *resById(SWELL_MenuResourceIndex *head, const char *resid)
|
|
{
|
|
SWELL_MenuResourceIndex *p=head;
|
|
while (p)
|
|
{
|
|
if (p->resid == resid) return p;
|
|
p=p->_next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
HMENU SWELL_LoadMenu(SWELL_MenuResourceIndex *head, const char *resid)
|
|
{
|
|
SWELL_MenuResourceIndex *p;
|
|
|
|
if (!(p=resById(head,resid))) return 0;
|
|
HMENU hMenu=CreatePopupMenu();
|
|
if (hMenu) p->createFunc(hMenu);
|
|
return hMenu;
|
|
}
|
|
|
|
HMENU SWELL_DuplicateMenu(HMENU menu)
|
|
{
|
|
if (WDL_NOT_NORMALLY(!menu)) return 0;
|
|
return menu->Duplicate();
|
|
}
|
|
|
|
BOOL SetMenu(HWND hwnd, HMENU menu)
|
|
{
|
|
if (WDL_NOT_NORMALLY(!hwnd)) return 0;
|
|
HMENU oldmenu = hwnd->m_menu;
|
|
|
|
hwnd->m_menu = menu;
|
|
|
|
if (!hwnd->m_parent && !!hwnd->m_menu != !!oldmenu)
|
|
{
|
|
WNDPROC oldwc = hwnd->m_wndproc;
|
|
hwnd->m_wndproc = DefWindowProc;
|
|
RECT r;
|
|
GetWindowRect(hwnd,&r);
|
|
|
|
if (oldmenu) r.bottom -= g_swell_ctheme.menubar_height; // hack: we should WM_NCCALCSIZE before and after, really
|
|
else r.bottom += g_swell_ctheme.menubar_height;
|
|
|
|
SetWindowPos(hwnd,NULL,0,0,r.right-r.left,r.bottom-r.top,SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE);
|
|
hwnd->m_wndproc = oldwc;
|
|
// resize
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HMENU GetMenu(HWND hwnd)
|
|
{
|
|
if (WDL_NOT_NORMALLY(!hwnd)) return 0;
|
|
return hwnd->m_menu;
|
|
}
|
|
|
|
void DrawMenuBar(HWND hwnd)
|
|
{
|
|
if (WDL_NORMALLY(hwnd) && hwnd->m_menu)
|
|
{
|
|
RECT r;
|
|
GetClientRect(hwnd,&r);
|
|
r.top = - g_swell_ctheme.menubar_height;
|
|
r.bottom=0;
|
|
InvalidateRect(hwnd,&r,FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
// copied from swell-menu.mm, can have a common impl someday
|
|
int SWELL_GenerateMenuFromList(HMENU hMenu, const void *_list, int listsz)
|
|
{
|
|
SWELL_MenuGen_Entry *list = (SWELL_MenuGen_Entry *)_list;
|
|
const int l1=strlen(SWELL_MENUGEN_POPUP_PREFIX);
|
|
while (listsz>0)
|
|
{
|
|
int cnt=1;
|
|
if (!list->name) SWELL_Menu_AddMenuItem(hMenu,NULL,-1,0);
|
|
else if (!strcmp(list->name,SWELL_MENUGEN_ENDPOPUP)) return list + 1 - (SWELL_MenuGen_Entry *)_list;
|
|
else if (!strncmp(list->name,SWELL_MENUGEN_POPUP_PREFIX,l1))
|
|
{
|
|
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};
|
|
cnt += SWELL_GenerateMenuFromList(mi.hSubMenu,list+1,listsz-1);
|
|
InsertMenuItem(hMenu,GetMenuItemCount(hMenu),TRUE,&mi);
|
|
}
|
|
else SWELL_Menu_AddMenuItem(hMenu,list->name,list->idx,list->flags);
|
|
|
|
list+=cnt;
|
|
listsz -= cnt;
|
|
}
|
|
return list + 1 - (SWELL_MenuGen_Entry *)_list;
|
|
}
|
|
#endif
|