soui 5.0.0.1
Soui5 Doc
 
Loading...
Searching...
No Matches
SMenuBar.cpp
1#include <souistd.h>
2#include <control/SMenuBar.h>
3#include <control/SCmnCtrl.h>
4
5#define TIMER_POP 10
6
7SNSBEGIN
8
9HHOOK SMenuBar::m_hMsgHook = NULL;
11
12const wchar_t XmlBtnStyle[] = L"btnStyle";
13const wchar_t XmlMenus[] = L"menus";
14
15class SMenuBarItem
16 : public SButton
17 , public SMenu {
18 DEF_SOBJECT(SButton, L"menuItem")
19 friend class SMenuBar;
20
21 public:
22 SMenuBarItem(SMenuBar *pHostMenu);
23 ~SMenuBarItem();
24
25 void SetData(ULONG_PTR data)
26 {
27 m_data = data;
28 }
29 ULONG_PTR GetData()
30 {
31 return m_data;
32 }
33
34 bool IsMenuLoaded() const;
35
36 protected:
37 UINT PopMenu();
38
39 HRESULT OnAttrSrc(const SStringW &strValue, BOOL bLoading);
40
41 virtual void WINAPI OnFinalRelease()
42 {
43 delete this;
44 }
45 STDMETHOD_(void, GetDesiredSize)(THIS_ SIZE *psz, int nParentWid, int nParentHei);
46 BOOL OnCmd(IEvtArgs *e);
47
48 void OnTimer(char timerID);
49
50 SOUI_MSG_MAP_BEGIN()
51 MSG_WM_TIMER_EX(OnTimer)
52 SOUI_MSG_MAP_END()
53
54 SOUI_ATTRS_BEGIN()
55 ATTR_CUSTOM(L"src", OnAttrSrc)
56 SOUI_ATTRS_END()
57
58 ULONG_PTR m_data;
59 SMenuBar *m_pHostMenu;
60 BOOL m_bIsRegHotKey;
61 int m_iIndex;
62 TCHAR m_cAccessKey;
63};
64
65SMenuBarItem::SMenuBarItem(SMenuBar *pHostMenu)
66 : m_data(0)
67 , m_pHostMenu(pHostMenu)
68 , m_bIsRegHotKey(FALSE)
69 , m_iIndex(-1)
70 , m_cAccessKey(0)
71{
72 m_bDrawFocusRect = FALSE;
73 GetEventSet()->subscribeEvent(EventCmd::EventID, Subscriber(&SMenuBarItem::OnCmd, this));
74}
75
76SMenuBarItem::~SMenuBarItem()
77{
78}
79
80bool SMenuBarItem::IsMenuLoaded() const
81{
82 return true;
83}
84
85UINT SMenuBarItem::PopMenu()
86{
87 if (m_pHostMenu->m_pNowMenu != NULL)
88 return 0;
89 m_pHostMenu->m_bIsShow = TRUE;
90 m_pHostMenu->m_pNowMenu = this;
91 m_pHostMenu->m_iNowMenu = m_iIndex;
92
93 // 把弹出事件发送过去
94 EventPopMenu evt_pop(m_pHostMenu);
95 evt_pop.nMenuIndex = m_iIndex;
96 evt_pop.pMenu = this;
97 FireEvent(evt_pop);
98
99 SetCheck(TRUE);
100
101 CRect rcHost;
102 ::GetWindowRect(m_pHostMenu->m_hWnd, rcHost);
103 CRect rcMenu = GetClientRect();
104
105 if (SMenuBar::m_hMsgHook == NULL)
106 SMenuBar::m_hMsgHook = ::SetWindowsHookEx(WH_MSGFILTER, SMenuBar::MenuSwitch, NULL,
107 GetCurrentThreadId()); // m_bLoop may become TRUE
108
109 int iRet = 0;
110 iRet = TrackPopupMenu(TPM_RETURNCMD, rcHost.left + rcMenu.left, rcHost.top + rcMenu.bottom + 2, m_pHostMenu->m_hWnd);
111
112 SetCheck(FALSE);
113 m_pHostMenu->m_bIsShow = FALSE;
114 if (m_pHostMenu->m_pNowMenu != this || iRet == 0)
115 {
116 m_pHostMenu->m_pNowMenu = NULL;
117 m_pHostMenu->m_iNowMenu = -1;
118 return iRet;
119 }
120 SSLOGI() << "###quit menu and kill timer for " << m_pHostMenu->m_iNowMenu;
121 m_pHostMenu->m_pNowMenu->KillTimer(TIMER_POP);
122 m_pHostMenu->m_iNowMenu = -1;
123 m_pHostMenu->m_pNowMenu = NULL;
124
125 // uninstall hook
126 ::UnhookWindowsHookEx(SMenuBar::m_hMsgHook);
128
129 // 把选择事件发送过去
130 EventSelectMenu evt_sel(m_pHostMenu);
131 evt_sel.nMenuId = iRet;
132 evt_sel.pMenu = this;
133 FireEvent(evt_sel);
134
135 return iRet;
136}
137
138HRESULT SMenuBarItem::OnAttrSrc(const SStringW &strValue, BOOL bLoading)
139{
140 return LoadMenu(S_CW2T(strValue)) ? S_OK : E_INVALIDARG;
141}
142
143void SMenuBarItem::GetDesiredSize(SIZE *psz, int nParentWid, int nParentHei)
144{
145 SWindow::GetDesiredSize(psz, nParentWid, nParentHei);
146 psz->cx += 13;
147 psz->cy += 3;
148}
149
150BOOL SMenuBarItem::OnCmd(IEvtArgs *e)
151{
152 if (!::IsWindow(m_pHostMenu->m_hWnd))
153 m_pHostMenu->m_hWnd = GetContainer()->GetHostHwnd();
154
155 e->SetBubbleUp(false);
156 PopMenu();
157 return TRUE;
158}
159
160void SMenuBarItem::OnTimer(char timerID)
161{
162 if (timerID == TIMER_POP)
163 {
164 if (!m_pHostMenu->m_bIsShow)
165 {
166 KillTimer(timerID);
167 PopMenu();
168 }
169 }
170 else
171 {
172 SetMsgHandled(FALSE);
173 }
174}
175
177 : m_bIsShow(FALSE)
178 , m_hWnd(NULL)
179 , m_pNowMenu(NULL)
180 , m_iNowMenu(-1)
181{
182 m_evtSet.addEvent(EVENTID(EventSelectMenu));
184}
185
187{
189 {
190 ::UnhookWindowsHookEx(SMenuBar::m_hMsgHook);
192 }
193}
194BOOL SMenuBar::Insert(LPCTSTR pszTitle, LPCTSTR pszResName, int iPos)
195{
196 if (!pszResName)
197 return FALSE;
198
199 SMenuBarItem *pNewMenu = new SMenuBarItem(this);
200 SASSERT(pNewMenu);
201 InsertChild(pNewMenu);
202
203 SXmlNode xmlBtnStyle = m_xmlStyle.root().child(XmlBtnStyle);
204 if (xmlBtnStyle)
205 pNewMenu->InitFromXml(&xmlBtnStyle);
206
207 if (pszTitle)
208 pNewMenu->SetWindowText(pszTitle);
209
210 pNewMenu->SetAttribute(L"src", S_CT2W(pszResName));
211 pNewMenu->SetWindowText(pszTitle);
212
213 if (!pNewMenu->IsMenuLoaded())
214 {
215 DestroyChild(pNewMenu);
216 return FALSE;
217 }
218
219 SStringT strText = pszTitle;
220 int nPos = strText.ReverseFind('&');
221 if (nPos > -1)
222 pNewMenu->SetAttribute(L"accel", SStringW().Format(L"alt+%c", strText[nPos + 1]));
223
224 if (iPos < 0)
225 iPos = (int)m_lstMenuItem.GetCount();
226 m_lstMenuItem.InsertAt(iPos, pNewMenu);
227
228 pNewMenu->m_iIndex = iPos;
229 for (size_t i = iPos + 1; i < m_lstMenuItem.GetCount(); i++)
230 {
231 m_lstMenuItem[i]->m_iIndex++;
232 }
233 return TRUE;
234}
235
236BOOL SMenuBar::Insert(IXmlNode *pNode, int iPos)
237{
238 SMenuBarItem *pNewMenu = new SMenuBarItem(this);
239 SASSERT(pNewMenu);
240 InsertChild(pNewMenu);
241
242 SXmlNode xmlBtnStyle = m_xmlStyle.root().child(XmlBtnStyle);
243 if (xmlBtnStyle)
244 pNewMenu->InitFromXml(&xmlBtnStyle);
245
246 pNewMenu->InitFromXml(pNode);
247 if (!pNewMenu->IsMenuLoaded())
248 {
249 DestroyChild(pNewMenu);
250 return FALSE;
251 }
252
253 SXmlNode xmlNode(pNode);
254 SStringW strText = xmlNode.first_child().value();
255 int nPos = strText.ReverseFind(L'&');
256 if (nPos > -1)
257 pNewMenu->SetAttribute(L"accel", SStringW().Format(L"alt+%c", strText[nPos + 1]));
258
259 if (iPos < 0)
260 iPos = (int)m_lstMenuItem.GetCount();
261 m_lstMenuItem.InsertAt(iPos, pNewMenu);
262
263 pNewMenu->m_iIndex = iPos;
264 for (size_t i = iPos + 1; i < m_lstMenuItem.GetCount(); i++)
265 {
266 m_lstMenuItem[i]->m_iIndex++;
267 }
268 return TRUE;
269}
270
272{
273 if (dwPos >= m_lstMenuItem.GetCount())
274 return NULL;
275 return m_lstMenuItem[dwPos];
276}
277int SMenuBar::HitTest(CPoint pt)
278{
279 for (size_t i = 0; i < m_lstMenuItem.GetCount(); i++)
280 {
281 SMenuBarItem *pItem = m_lstMenuItem[i];
282 CRect rcItem = pItem->GetClientRect();
283 if (rcItem.PtInRect(pt))
284 return (int)i;
285 }
286 return -1;
287}
288SMenuBarItem *SMenuBar::GetMenuItem(DWORD dwPos)
289{
290 if (dwPos >= m_lstMenuItem.GetCount())
291 return NULL;
292 return m_lstMenuItem[dwPos];
293}
294
296{
297 SXmlNode xmlBtnStyle = xmlNode.child(XmlBtnStyle);
298 if (xmlBtnStyle)
299 {
300 m_xmlStyle.root().append_copy(xmlBtnStyle);
301 }
302 SXmlNode xmlTMenus = xmlNode.child(XmlMenus);
303 if (xmlTMenus)
304 {
305 for (SXmlNode xmlChild = xmlTMenus.first_child(); xmlChild; xmlChild = xmlChild.next_sibling())
306 {
307 if (_wcsicmp(xmlChild.name(), SMenuBarItem::GetClassName()) != 0)
308 continue;
309 Insert(&xmlChild);
310 }
311 }
312
313 return TRUE;
314}
315
316LRESULT SMenuBar::MenuSwitch(int code, WPARAM wParam, LPARAM lParam)
317{
318 if (code == MSGF_MENU)
319 {
320 MSG msg = *(MSG *)lParam;
321 int nMsg = msg.message;
322 switch (nMsg)
323 {
324 case WM_MOUSEMOVE:
325 {
326 CPoint pt(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
328 {
329 SMenuBar::m_pMenuBar->m_ptMouse = pt;
330 ::MapWindowPoints(msg.hwnd, SMenuBar::m_pMenuBar->m_hWnd, &pt, 1);
331 int nIndex = SMenuBar::m_pMenuBar->HitTest(pt);
332 if (nIndex != -1)
333 {
334 SMenuBarItem *menuItem = SMenuBar::m_pMenuBar->GetMenuItem(nIndex);
335 if (menuItem && SMenuBar::m_pMenuBar->m_iNowMenu != nIndex)
336 {
337 SMenuBar::m_pMenuBar->m_pNowMenu = menuItem;
338 SMenuBar::m_pMenuBar->m_iNowMenu = nIndex;
339 ::PostMessage(msg.hwnd, WM_CANCELMODE, 0, 0); // quit current popup menu.
340 // kill all timer now.
341 for (size_t i = 0; i < SMenuBar::m_pMenuBar->m_lstMenuItem.GetCount(); i++)
342 {
343 SMenuBar::m_pMenuBar->m_lstMenuItem[i]->KillTimer(TIMER_POP);
344 }
345 menuItem->SetTimer(TIMER_POP, 10); // delay popup new menu.
346 SSLOGI() << "###set timer for " << nIndex;
347 return TRUE;
348 }
349 }
350 }
351 break;
352 }
353 case WM_KEYDOWN:
354 {
355 TCHAR vKey = (TCHAR)msg.wParam;
357 break;
358 if (vKey != VK_LEFT && vKey != VK_RIGHT)
359 break;
360 SMenuBarItem *pNowMenu = SMenuBar::m_pMenuBar->m_pNowMenu;
361 SASSERT(pNowMenu);
362 int selItem = -1;
363 HMENU hSubMenu = 0;
364 for (int i = 0; i < GetMenuItemCount(pNowMenu->m_hMenu); i++)
365 {
366 MENUITEMINFO mii = { 0 };
367 mii.cbSize = sizeof(MENUITEMINFO);
368 mii.fMask = MIIM_STATE | MIIM_SUBMENU;
369 GetMenuItemInfo(pNowMenu->m_hMenu, i, TRUE, &mii);
370 if (mii.fState & MF_HILITE)
371 {
372 selItem = i;
373 hSubMenu = mii.hSubMenu;
374 break;
375 }
376 }
377 if (selItem != -1 && hSubMenu)
378 {
379 BOOL isMenuExpended = IsWindowVisible((HWND)hSubMenu);
380 if (isMenuExpended && vKey == VK_LEFT)
381 {
382 return FALSE;
383 }
384 if (!isMenuExpended && vKey == VK_RIGHT)
385 {
386 return FALSE;
387 }
388 }
389 if (vKey == VK_LEFT)
390 {
391 int nRevIndex = SMenuBar::m_pMenuBar->m_iNowMenu - 1;
392 if (nRevIndex < 0)
393 nRevIndex = (int)SMenuBar::m_pMenuBar->m_lstMenuItem.GetCount() - 1;
394 SMenuBarItem *menuItem = SMenuBar::m_pMenuBar->m_lstMenuItem[nRevIndex];
395 if (menuItem)
396 {
397 SMenuBar::m_pMenuBar->m_pNowMenu = menuItem;
398 SMenuBar::m_pMenuBar->m_iNowMenu = nRevIndex;
399 ::PostMessage(SMenuBar::m_pMenuBar->m_hWnd, WM_KEYDOWN, VK_ESCAPE, 0);
400 ::PostMessage(msg.hwnd, WM_CANCELMODE, 0, 0);
401 menuItem->SetTimer(TIMER_POP, 10);
402 return TRUE;
403 }
404 }
405 else if (vKey == VK_RIGHT)
406 {
407 int nNextIndex = SMenuBar::m_pMenuBar->m_iNowMenu + 1;
408 if (nNextIndex >= (int)SMenuBar::m_pMenuBar->m_lstMenuItem.GetCount())
409 nNextIndex = 0;
410 SMenuBarItem *menuItem = SMenuBar::m_pMenuBar->GetMenuItem(nNextIndex);
411 if (menuItem)
412 {
413 SMenuBar::m_pMenuBar->m_pNowMenu = menuItem;
414 SMenuBar::m_pMenuBar->m_iNowMenu = nNextIndex;
415 ::PostMessage(SMenuBar::m_pMenuBar->m_hWnd, WM_KEYDOWN, VK_ESCAPE, 0);
416 ::PostMessage(msg.hwnd, WM_CANCELMODE, 0, 0);
417 menuItem->SetTimer(TIMER_POP, 10);
418 return TRUE;
419 }
420 }
421 }
422 }
423 }
424 return CallNextHookEx(m_hMsgHook, code, wParam, lParam);
425}
426
427SNSEND
通用控件
virtual BOOL WINAPI InitFromXml(IXmlNode *pNode) OVERRIDE
从XML初始化控件
Definition SCmnCtrl.cpp:456
SButton()
构造函数
Definition SCmnCtrl.cpp:377
BOOL subscribeEvent(DWORD dwEventID, const IEvtSlot &subscriber)
订阅事件
Definition SEventSet.h:151
Menu Bar Control.
Definition SMenuBar.h:21
~SMenuBar()
Destructor.
Definition SMenuBar.cpp:186
BOOL m_bIsShow
Definition SMenuBar.h:95
int HitTest(CPoint pt)
Hit test to determine the menu item under the mouse.
Definition SMenuBar.cpp:277
SMenuBar()
Constructor.
Definition SMenuBar.cpp:176
HWND m_hWnd
Definition SMenuBar.h:93
static SMenuBar * m_pMenuBar
Definition SMenuBar.h:101
SMenu * GetMenu(DWORD dwPos)
Get the menu at a specific position.
Definition SMenuBar.cpp:271
static HHOOK m_hMsgHook
Definition SMenuBar.h:100
int m_iNowMenu
Definition SMenuBar.h:97
SMenuBarItem * m_pNowMenu
Definition SMenuBar.h:96
SMenuBarItem * GetMenuItem(DWORD dwPos)
Get the menu bar item at a specific position.
Definition SMenuBar.cpp:288
SXmlDoc m_xmlStyle
Definition SMenuBar.h:94
SArray< SMenuBarItem * > m_lstMenuItem
Definition SMenuBar.h:92
CPoint m_ptMouse
Definition SMenuBar.h:98
virtual BOOL CreateChildren(SXmlNode xmlNode)
Create child items from XML configuration.
Definition SMenuBar.cpp:295
BOOL Insert(LPCTSTR pszTitle, LPCTSTR pszResName, int iPos=-1)
Insert a menu item by title and resource name.
Definition SMenuBar.cpp:194
static LRESULT CALLBACK MenuSwitch(int code, WPARAM wParam, LPARAM lParam)
Callback function for menu switch.
Definition SMenuBar.cpp:316
菜单类
Definition SMenu.h:399
BOOL LoadMenu(LPCTSTR resId) OVERRIDE
加载菜单资源
Definition SMenu.cpp:340
SMenu(const SMenu &src)
复制构造函数
Definition SMenu.cpp:286
UINT TrackPopupMenu(UINT uFlags, int x, int y, HWND hWnd, LPCRECT prcRect=NULL, int nScale=100) OVERRIDE
跟踪弹出菜单
Definition SMenu.cpp:416
static LPCWSTR GetClassName()
Definition Sobject.hpp:41
A class representing an ASCII string.
Definition sstringw.h:96
int ReverseFind(wchar_t ch) SCONST
Finds the last occurrence of a character in the string.
Definition sstringw.cpp:542
BOOL SetTimer(char id, UINT uElapse) OVERRIDE
Sets a timer for the window.
Definition Swnd.cpp:477
BOOL FireEvent(IEvtArgs *evt) OVERRIDE
Fires an event.
Definition Swnd.cpp:1540
BOOL DestroyChild(SWindow *pChild)
Destroys a child window.
Definition Swnd.cpp:519
void GetDesiredSize(SIZE *psz, int nParentWid, int nParentHei) OVERRIDE
Retrieves the desired size of the window.
Definition Swnd.cpp:1839
void GetClientRect(LPRECT prect) SCONST OVERRIDE
Retrieves the client rectangle of the window.
CRect GetWindowRect() const
Retrieves the bounding rectangle of the window.
Definition Swnd.cpp:230
ISwndContainer * GetContainer() OVERRIDE
Retrieves the container associated with this window.
Definition Swnd.cpp:679
BOOL m_bDrawFocusRect
Definition SWnd.h:2610
virtual CRect GetClientRect() const
Retrieves the client rectangle of the window.
Definition Swnd.cpp:243
void InsertChild(SWindow *pNewChild, SWindow *pInsertAfter=NULL)
Inserts a child window into the window tree.
Definition Swnd.cpp:538
void SetCheck(BOOL bCheck) OVERRIDE
Sets the check state of the window.
Definition Swnd.cpp:671
SEventSet * GetEventSet()
Retrieves the event set associated with the window.
Definition SWnd.h:1290
BOOL KillTimer(char id) OVERRIDE
Kills a timer for the window.
Definition Swnd.cpp:483
void SetWindowText(LPCTSTR lpszText) OVERRIDE
Sets the window text.
Definition Swnd.cpp:311
void SetMsgHandled(BOOL bHandled)
Sets the message handled flag.
Definition Swnd.cpp:212
SEventSet m_evtSet
Definition SWnd.h:2581
Class representing an XML node.
Definition SXml.h:352
SXmlNode next_sibling() const
Gets the next sibling node in the children list of the parent node.
Definition SXml.cpp:393
SXmlNode first_child() const
Gets the first child node of the node.
Definition SXml.cpp:383
SXmlNode child(const wchar_t *name, bool bCaseSensitive=false) const
Gets the child node, attribute, or next/previous sibling with the specified name.
Definition SXml.cpp:423
const wchar_t * value() const
Gets the value of the node.
Definition SXml.cpp:368
HWND GetHostHwnd() PURE
Retrieves the handle to the host window.
Interface for XML nodes.
Definition sxml-i.h:128