3ab28908b51a041f8b6594d3e3999ca9174172ad
[reactos.git] / base / shell / rshell / CMenuFocusManager.cpp
1 /*
2 * Shell Menu Band
3 *
4 * Copyright 2014 David Quintana
5 *
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.
10 *
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.
15 *
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
19 */
20 #include "precomp.h"
21 #include <windowsx.h>
22 #include <CommonControls.h>
23 #include <shlwapi_undoc.h>
24
25 #include "CMenuFocusManager.h"
26 #include "CMenuBand.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(CMenuFocus);
29
30 DWORD CMenuFocusManager::TlsIndex = 0;
31
32 CMenuFocusManager * CMenuFocusManager::GetManager()
33 {
34 return reinterpret_cast<CMenuFocusManager *>(TlsGetValue(TlsIndex));
35 }
36
37 CMenuFocusManager * CMenuFocusManager::AcquireManager()
38 {
39 CMenuFocusManager * obj = NULL;
40
41 if (!TlsIndex)
42 {
43 if ((TlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)
44 return NULL;
45 }
46
47 obj = GetManager();
48
49 if (!obj)
50 {
51 obj = new CComObject<CMenuFocusManager>();
52 TlsSetValue(TlsIndex, obj);
53 }
54
55 obj->AddRef();
56
57 return obj;
58 }
59
60 void CMenuFocusManager::ReleaseManager(CMenuFocusManager * obj)
61 {
62 if (!obj->Release())
63 {
64 TlsSetValue(TlsIndex, NULL);
65 }
66 }
67
68 LRESULT CALLBACK CMenuFocusManager::s_GetMsgHook(INT nCode, WPARAM wParam, LPARAM lParam)
69 {
70 return GetManager()->GetMsgHook(nCode, wParam, lParam);
71 }
72
73 HRESULT CMenuFocusManager::PushToArray(CMenuBand * item)
74 {
75 if (m_bandCount >= MAX_RECURSE)
76 return E_OUTOFMEMORY;
77
78 m_bandStack[m_bandCount++] = item;
79 return S_OK;
80 }
81
82 HRESULT CMenuFocusManager::PopFromArray(CMenuBand ** pItem)
83 {
84 if (pItem)
85 *pItem = NULL;
86
87 if (m_bandCount <= 0)
88 return E_FAIL;
89
90 m_bandCount--;
91
92 if (pItem)
93 *pItem = m_bandStack[m_bandCount];
94
95 m_bandStack[m_bandCount] = NULL;
96
97 return S_OK;
98 }
99
100 HRESULT CMenuFocusManager::PeekArray(CMenuBand ** pItem)
101 {
102 if (!pItem)
103 return E_FAIL;
104
105 *pItem = NULL;
106
107 if (m_bandCount <= 0)
108 return S_FALSE;
109
110 *pItem = m_bandStack[m_bandCount - 1];
111
112 return S_OK;
113 }
114
115 CMenuFocusManager::CMenuFocusManager() :
116 m_currentBand(NULL),
117 m_currentFocus(NULL),
118 m_mouseTrackDisabled(FALSE),
119 m_lastMoveFlags(0),
120 m_lastMovePos(0),
121 m_bandCount(0)
122 {
123 m_threadId = GetCurrentThreadId();
124 }
125
126 CMenuFocusManager::~CMenuFocusManager()
127 {
128 }
129
130 void CMenuFocusManager::DisableMouseTrack(HWND enableTo, BOOL disableThis)
131 {
132 BOOL bDisable = FALSE;
133
134 int i = m_bandCount;
135 while (--i >= 0)
136 {
137 CMenuBand * band = m_bandStack[i];
138
139 HWND hwnd;
140 HRESULT hr = band->_GetTopLevelWindow(&hwnd);
141 if (FAILED_UNEXPECTEDLY(hr))
142 break;
143
144 if (hwnd == enableTo)
145 {
146 band->_DisableMouseTrack(disableThis);
147 bDisable = TRUE;
148 }
149 else
150 {
151 band->_DisableMouseTrack(bDisable);
152 }
153 }
154
155 if (m_mouseTrackDisabled == bDisable)
156 {
157 if (bDisable)
158 {
159 SetCapture(m_currentFocus);
160 }
161 else
162 ReleaseCapture();
163
164 m_mouseTrackDisabled = bDisable;
165 }
166 }
167
168 HRESULT CMenuFocusManager::IsTrackedWindow(HWND hWnd)
169 {
170 if (hWnd == m_currentFocus)
171 return S_OK;
172
173 int i = m_bandCount - 1;
174 while (--i >= 0)
175 {
176 CMenuBand * band = m_bandStack[i];
177
178 HWND hwnd;
179 HRESULT hr = band->_GetTopLevelWindow(&hwnd);
180 if (FAILED_UNEXPECTEDLY(hr))
181 return hr;
182
183 if (hwnd == hWnd)
184 return S_OK;
185 }
186
187 return S_FALSE;
188 }
189
190 LRESULT CMenuFocusManager::GetMsgHook(INT nCode, WPARAM wParam, LPARAM lParam)
191 {
192 if (nCode < 0)
193 return CallNextHookEx(m_hHook, nCode, wParam, lParam);
194
195 LPARAM pos = (LPARAM) GetMessagePos();
196
197 if (nCode == HC_ACTION)
198 {
199 BOOL callNext = TRUE;
200 MSG* msg = reinterpret_cast<MSG*>(lParam);
201
202 // Do whatever is necessary here
203
204 switch (msg->message)
205 {
206 case WM_ACTIVATE: // does not trigger
207 ActivationChange(msg->hwnd);
208 case WM_CLOSE:
209 break;
210 case WM_LBUTTONDOWN:
211 {
212 POINT pt = { GET_X_LPARAM(pos), GET_Y_LPARAM(pos) };
213
214 HWND window = WindowFromPoint(pt);
215
216 if (IsTrackedWindow(window) != S_OK)
217 {
218 DisableMouseTrack(NULL, FALSE);
219 m_currentBand->_MenuItemHotTrack(MPOS_FULLCANCEL);
220 }
221
222 break;
223 }
224 case WM_MOUSEMOVE:
225 if (m_lastMoveFlags != wParam || m_lastMovePos != pos)
226 {
227 m_lastMoveFlags = wParam;
228 m_lastMovePos = pos;
229
230 POINT pt = { GET_X_LPARAM(pos), GET_Y_LPARAM(pos) };
231
232 HWND window = WindowFromPoint(pt);
233
234 if (IsTrackedWindow(window) == S_OK)
235 {
236 DisableMouseTrack(window, FALSE);
237 }
238 else
239 {
240 DisableMouseTrack(NULL, FALSE);
241 }
242 }
243 break;
244 case WM_SYSKEYDOWN:
245 case WM_KEYDOWN:
246 //if (!m_currentMenu)
247 {
248 DisableMouseTrack(m_currentFocus, TRUE);
249 switch (msg->wParam)
250 {
251 case VK_MENU:
252 case VK_LMENU:
253 case VK_RMENU:
254 m_currentBand->_MenuItemHotTrack(MPOS_FULLCANCEL);
255 break;
256 case VK_LEFT:
257 m_currentBand->_MenuItemHotTrack(MPOS_SELECTLEFT);
258 break;
259 case VK_RIGHT:
260 m_currentBand->_MenuItemHotTrack(MPOS_SELECTRIGHT);
261 break;
262 case VK_UP:
263 m_currentBand->_MenuItemHotTrack(VK_UP);
264 break;
265 case VK_DOWN:
266 m_currentBand->_MenuItemHotTrack(VK_DOWN);
267 break;
268 }
269 }
270 break;
271 }
272
273 if (!callNext)
274 return 0;
275 }
276
277 return CallNextHookEx(m_hHook, nCode, wParam, lParam);
278 }
279
280 HRESULT CMenuFocusManager::PlaceHooks(HWND window)
281 {
282 //SetCapture(window);
283 m_hHook = SetWindowsHookEx(WH_GETMESSAGE, s_GetMsgHook, NULL, m_threadId);
284 return S_OK;
285 }
286
287 HRESULT CMenuFocusManager::RemoveHooks(HWND window)
288 {
289 UnhookWindowsHookEx(m_hHook);
290 //ReleaseCapture();
291 return S_OK;
292 }
293
294 HRESULT CMenuFocusManager::ActivationChange(HWND newHwnd)
295 {
296 HRESULT hr;
297 CMenuBand * newBand = NULL;
298
299 CMenuBand * band;
300 PeekArray(&band);
301
302 while (m_bandCount >= 0)
303 {
304 HWND hwnd;
305 hr = band->_GetTopLevelWindow(&hwnd);
306 if (FAILED_UNEXPECTEDLY(hr))
307 return hr;
308
309 if (hwnd == newHwnd)
310 {
311 newBand = band;
312 break;
313 }
314 else
315 {
316 PopFromArray(NULL);
317 PeekArray(&band);
318 }
319 }
320
321 return UpdateFocus(newBand);
322 }
323
324 HRESULT CMenuFocusManager::UpdateFocus(CMenuBand * newBand, HMENU popupToTrack)
325 {
326 HRESULT hr;
327 HWND newFocus;
328
329 if (newBand == NULL)
330 {
331 DisableMouseTrack(NULL, FALSE);
332
333 hr = RemoveHooks(m_currentFocus);
334 m_currentFocus = NULL;
335 m_currentBand = NULL;
336 m_currentMenu = NULL;
337 return S_OK;
338 }
339
340 hr = newBand->_GetTopLevelWindow(&newFocus);
341 if (FAILED_UNEXPECTEDLY(hr))
342 return hr;
343
344 if (!m_currentBand)
345 {
346 hr = PlaceHooks(newFocus);
347 if (FAILED_UNEXPECTEDLY(hr))
348 return hr;
349 }
350
351 CHAR title[1024];
352 GetWindowTextA(newFocus, title, 1024);
353
354 DbgPrint("Focus is now at %08p, hwnd=%08x, title='%s'. m_bandCount=%d\n", newBand, newFocus, title, m_bandCount);
355
356 m_currentFocus = newFocus;
357 m_currentBand = newBand;
358 m_currentMenu = popupToTrack;
359
360 return S_OK;
361 }
362
363 HRESULT CMenuFocusManager::PushMenu(CMenuBand * mb)
364 {
365 HRESULT hr;
366
367 hr = PushToArray(mb);
368 if (FAILED_UNEXPECTEDLY(hr))
369 return hr;
370
371 return UpdateFocus(mb);
372 }
373
374 HRESULT CMenuFocusManager::PopMenu(CMenuBand * mb)
375 {
376 CMenuBand * mbc;
377 HRESULT hr;
378
379 HWND newFocus;
380 hr = mb->_GetTopLevelWindow(&newFocus);
381 if (FAILED_UNEXPECTEDLY(hr))
382 return hr;
383
384 DbgPrint("Trying to pop %08p, hwnd=%08x\n", mb, newFocus);
385
386 do {
387 hr = PopFromArray(&mbc);
388 if (FAILED_UNEXPECTEDLY(hr))
389 {
390 mbc = NULL;
391 return hr;
392 }
393 }
394 while (mbc && mb != mbc);
395
396 hr = PeekArray(&mb);
397 if (FAILED_UNEXPECTEDLY(hr))
398 return hr;
399
400 hr = UpdateFocus(mb);
401 if (FAILED_UNEXPECTEDLY(hr))
402 return hr;
403
404 if (!mbc)
405 return E_FAIL;
406
407 return S_OK;
408 }
409
410 HRESULT CMenuFocusManager::PushTrackedPopup(CMenuBand * mb, HMENU popup)
411 {
412 HRESULT hr;
413
414 hr = PushToArray(mb);
415 if (FAILED_UNEXPECTEDLY(hr))
416 return hr;
417
418 return UpdateFocus(mb, popup);
419 }
420
421 HRESULT CMenuFocusManager::PopTrackedPopup(CMenuBand * mb, HMENU popup)
422 {
423 return PopMenu(mb);
424 }