4 * Copyright 2014 David Quintana
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library 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 GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <CommonControls.h>
23 #include <shlwapi_undoc.h>
25 #include "CMenuFocusManager.h"
26 #include "CMenuToolbars.h"
27 #include "CMenuBand.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(CMenuFocus
);
31 DWORD
CMenuFocusManager::TlsIndex
= 0;
33 CMenuFocusManager
* CMenuFocusManager::GetManager()
35 return reinterpret_cast<CMenuFocusManager
*>(TlsGetValue(TlsIndex
));
38 CMenuFocusManager
* CMenuFocusManager::AcquireManager()
40 CMenuFocusManager
* obj
= NULL
;
44 if ((TlsIndex
= TlsAlloc()) == TLS_OUT_OF_INDEXES
)
52 obj
= new CComObject
<CMenuFocusManager
>();
53 TlsSetValue(TlsIndex
, obj
);
61 void CMenuFocusManager::ReleaseManager(CMenuFocusManager
* obj
)
65 TlsSetValue(TlsIndex
, NULL
);
69 LRESULT CALLBACK
CMenuFocusManager::s_MsgFilterHook(INT nCode
, WPARAM wParam
, LPARAM lParam
)
71 return GetManager()->MsgFilterHook(nCode
, wParam
, lParam
);
74 LRESULT CALLBACK
CMenuFocusManager::s_GetMsgHook(INT nCode
, WPARAM wParam
, LPARAM lParam
)
76 return GetManager()->GetMsgHook(nCode
, wParam
, lParam
);
79 HRESULT
CMenuFocusManager::PushToArray(CMenuBand
* item
)
81 if (m_bandCount
>= MAX_RECURSE
)
84 m_bandStack
[m_bandCount
++] = item
;
88 HRESULT
CMenuFocusManager::PopFromArray(CMenuBand
** pItem
)
99 *pItem
= m_bandStack
[m_bandCount
];
101 m_bandStack
[m_bandCount
] = NULL
;
106 HRESULT
CMenuFocusManager::PeekArray(CMenuBand
** pItem
)
113 if (m_bandCount
<= 0)
116 *pItem
= m_bandStack
[m_bandCount
- 1];
121 CMenuFocusManager::CMenuFocusManager() :
123 m_currentFocus(NULL
),
125 m_parentToolbar(NULL
),
126 m_hMsgFilterHook(NULL
),
128 m_mouseTrackDisabled(FALSE
),
133 m_threadId
= GetCurrentThreadId();
136 CMenuFocusManager::~CMenuFocusManager()
140 void CMenuFocusManager::DisableMouseTrack(HWND enableTo
, BOOL disableThis
)
142 BOOL bDisable
= FALSE
;
147 CMenuBand
* band
= m_bandStack
[i
];
150 HRESULT hr
= band
->_GetTopLevelWindow(&hwnd
);
151 if (FAILED_UNEXPECTEDLY(hr
))
154 if (hwnd
== enableTo
)
156 band
->_DisableMouseTrack(disableThis
);
161 band
->_DisableMouseTrack(bDisable
);
165 if (m_mouseTrackDisabled
== bDisable
)
169 SetCapture(m_currentFocus
);
174 m_mouseTrackDisabled
= bDisable
;
178 HRESULT
CMenuFocusManager::IsTrackedWindow(HWND hWnd
)
183 CMenuBand
* band
= m_bandStack
[i
];
186 HRESULT hr
= band
->_GetTopLevelWindow(&hwnd
);
187 if (FAILED_UNEXPECTEDLY(hr
))
197 LRESULT
CMenuFocusManager::MsgFilterHook(INT nCode
, WPARAM wParam
, LPARAM lParam
)
205 return CallNextHookEx(m_hMsgFilterHook
, nCode
, wParam
, lParam
);
207 if (nCode
== MSGF_MENU
)
209 BOOL callNext
= TRUE
;
210 MSG
* msg
= reinterpret_cast<MSG
*>(lParam
);
212 // Do whatever is necessary here
214 switch (msg
->message
)
220 parent
= WindowFromPoint(pt
);
222 ScreenToClient(parent
, &pt
);
224 child
= ChildWindowFromPoint(parent
, pt
);
226 if (child
!= m_parentToolbar
)
229 ScreenToClient(m_parentToolbar
, &msg
->pt
);
231 /* Don't do anything if the mouse has not been moved */
232 if (msg
->pt
.x
== m_ptPrev
.x
&& msg
->pt
.y
== m_ptPrev
.y
)
237 iHitTestResult
= SendMessageW(m_parentToolbar
, TB_HITTEST
, 0, (LPARAM
) &msg
->pt
);
239 /* Make sure that iHitTestResult is one of the menu items and that it is not the current menu item */
240 if (iHitTestResult
>= 0)
242 if (SendMessage(m_parentToolbar
, WM_USER_ISTRACKEDITEM
, iHitTestResult
, 0))
244 SendMessage(m_currentFocus
, WM_CANCELMODE
, 0, 0);
245 PostMessage(m_parentToolbar
, WM_USER_CHANGETRACKEDITEM
, iHitTestResult
, iHitTestResult
);
256 return CallNextHookEx(m_hMsgFilterHook
, nCode
, wParam
, lParam
);
259 LRESULT
CMenuFocusManager::GetMsgHook(INT nCode
, WPARAM wParam
, LPARAM lParam
)
262 return CallNextHookEx(m_hGetMsgHook
, nCode
, wParam
, lParam
);
264 LPARAM pos
= (LPARAM
) GetMessagePos();
266 if (nCode
== HC_ACTION
)
268 BOOL callNext
= TRUE
;
269 MSG
* msg
= reinterpret_cast<MSG
*>(lParam
);
271 // Do whatever is necessary here
273 switch (msg
->message
)
278 case WM_NCLBUTTONDOWN
:
281 POINT pt
= { GET_X_LPARAM(pos
), GET_Y_LPARAM(pos
) };
283 HWND window
= GetAncestor(WindowFromPoint(pt
), GA_ROOT
);
285 if (IsTrackedWindow(window
) != S_OK
)
287 DisableMouseTrack(NULL
, FALSE
);
288 m_currentBand
->_MenuItemHotTrack(MPOS_FULLCANCEL
);
294 if (m_lastMoveFlags
!= wParam
|| m_lastMovePos
!= pos
)
296 m_lastMoveFlags
= wParam
;
299 POINT pt
= { GET_X_LPARAM(pos
), GET_Y_LPARAM(pos
) };
301 HWND window
= WindowFromPoint(pt
);
303 if (IsTrackedWindow(window
) == S_OK
)
305 DisableMouseTrack(window
, FALSE
);
309 DisableMouseTrack(NULL
, FALSE
);
315 //if (!m_currentMenu)
317 DisableMouseTrack(m_currentFocus
, TRUE
);
323 m_currentBand
->_MenuItemHotTrack(MPOS_FULLCANCEL
);
326 m_currentBand
->_MenuItemHotTrack(MPOS_SELECTLEFT
);
329 m_currentBand
->_MenuItemHotTrack(MPOS_SELECTRIGHT
);
332 m_currentBand
->_MenuItemHotTrack(VK_UP
);
335 m_currentBand
->_MenuItemHotTrack(VK_DOWN
);
346 return CallNextHookEx(m_hGetMsgHook
, nCode
, wParam
, lParam
);
349 HRESULT
CMenuFocusManager::PlaceHooks()
351 //SetCapture(window);
354 m_hMsgFilterHook
= SetWindowsHookEx(WH_MSGFILTER
, s_MsgFilterHook
, NULL
, m_threadId
);
358 m_hGetMsgHook
= SetWindowsHookEx(WH_GETMESSAGE
, s_GetMsgHook
, NULL
, m_threadId
);
363 HRESULT
CMenuFocusManager::RemoveHooks()
365 if (m_hMsgFilterHook
)
366 UnhookWindowsHookEx(m_hMsgFilterHook
);
368 UnhookWindowsHookEx(m_hGetMsgHook
);
369 m_hMsgFilterHook
= NULL
;
370 m_hGetMsgHook
= NULL
;
374 HRESULT
CMenuFocusManager::UpdateFocus(CMenuBand
* newBand
, HMENU popupToTrack
)
377 HWND newFocus
= NULL
;
378 HWND oldFocus
= m_currentFocus
;
379 HMENU oldMenu
= m_currentMenu
;
383 hr
= newBand
->_GetTopLevelWindow(&newFocus
);
384 if (FAILED_UNEXPECTEDLY(hr
))
388 m_currentBand
= newBand
;
389 m_currentMenu
= popupToTrack
;
390 m_currentFocus
= newFocus
;
393 m_currentBand
->GetWindow(&m_parentToolbar
);
396 if (oldFocus
&& (!newFocus
|| (oldMenu
!= popupToTrack
)))
398 DisableMouseTrack(NULL
, FALSE
);
401 if (FAILED_UNEXPECTEDLY(hr
))
405 if (newFocus
&& (!oldFocus
|| (oldMenu
!= popupToTrack
)))
408 if (FAILED_UNEXPECTEDLY(hr
))
416 HRESULT
CMenuFocusManager::PushMenu(CMenuBand
* mb
)
420 CMenuBand
* mbParent
= m_currentBand
;
422 hr
= PushToArray(mb
);
423 if (FAILED_UNEXPECTEDLY(hr
))
428 mbParent
->SetClient(static_cast<IMenuPopup
*>(mb
));
431 return UpdateFocus(mb
);
434 HRESULT
CMenuFocusManager::PopMenu(CMenuBand
* mb
)
440 hr
= mb
->_GetTopLevelWindow(&newFocus
);
441 if (FAILED_UNEXPECTEDLY(hr
))
444 DbgPrint("Trying to pop %08p, hwnd=%08x\n", mb
, newFocus
);
447 hr
= PopFromArray(&mbc
);
448 if (FAILED_UNEXPECTEDLY(hr
))
454 while (mbc
&& mb
!= mbc
);
460 if (FAILED_UNEXPECTEDLY(hr
))
463 hr
= UpdateFocus(mb
);
464 if (FAILED_UNEXPECTEDLY(hr
))
475 HRESULT
CMenuFocusManager::PushTrackedPopup(CMenuBand
* mb
, HMENU popup
)
479 hr
= PushToArray(mb
);
480 if (FAILED_UNEXPECTEDLY(hr
))
483 return UpdateFocus(mb
, popup
);
486 HRESULT
CMenuFocusManager::PopTrackedPopup(CMenuBand
* mb
, HMENU popup
)
492 hr
= mb
->_GetTopLevelWindow(&newFocus
);
493 if (FAILED_UNEXPECTEDLY(hr
))
496 DbgPrint("Trying to pop %08p, hwnd=%08x\n", mb
, newFocus
);
499 hr
= PopFromArray(&mbc
);
500 if (FAILED_UNEXPECTEDLY(hr
))
505 } while (mbc
&& mb
!= mbc
);
511 if (FAILED_UNEXPECTEDLY(hr
))
514 hr
= UpdateFocus(mb
);
515 if (FAILED_UNEXPECTEDLY(hr
))