d0be84ed1785dc2849e7bd8cdc650d4522521745
[reactos.git] / base / shell / rshell / CMenuDeskBar.cpp
1 /*
2 * Shell Menu Desk Bar
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 <atlwin.h>
22 #include <shlwapi_undoc.h>
23
24 WINE_DEFAULT_DEBUG_CHANNEL(CMenuDeskBar);
25
26 typedef CWinTraits<
27 WS_POPUP | WS_DLGFRAME | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
28 WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | WS_EX_PALETTEWINDOW
29 > CMenuWinTraits;
30
31 class CMenuDeskBar :
32 public CWindowImpl<CMenuDeskBar, CWindow, CMenuWinTraits>,
33 public CComCoClass<CMenuDeskBar>,
34 public CComObjectRootEx<CComMultiThreadModelNoCS>,
35 public IOleCommandTarget,
36 public IServiceProvider,
37 public IInputObjectSite,
38 public IInputObject,
39 public IMenuPopup,
40 public IObjectWithSite,
41 public IBanneredBar,
42 public IInitializeObject
43 {
44 public:
45 CMenuDeskBar();
46 ~CMenuDeskBar();
47
48 private:
49 CComPtr<IUnknown> m_Site;
50 CComPtr<IUnknown> m_Client;
51 CComPtr<IMenuPopup> m_SubMenuParent;
52
53 DWORD m_IconSize;
54 HBITMAP m_Banner;
55
56 public:
57 // *** IMenuPopup methods ***
58 virtual HRESULT STDMETHODCALLTYPE Popup(POINTL *ppt, RECTL *prcExclude, MP_POPUPFLAGS dwFlags);
59 virtual HRESULT STDMETHODCALLTYPE OnSelect(DWORD dwSelectType);
60 virtual HRESULT STDMETHODCALLTYPE SetSubMenu(IMenuPopup *pmp, BOOL fSet);
61
62 // *** IOleWindow methods ***
63 virtual HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd);
64 virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode);
65
66 // *** IObjectWithSite methods ***
67 virtual HRESULT STDMETHODCALLTYPE SetSite(IUnknown *pUnkSite);
68 virtual HRESULT STDMETHODCALLTYPE GetSite(REFIID riid, PVOID *ppvSite);
69
70 // *** IBanneredBar methods ***
71 virtual HRESULT STDMETHODCALLTYPE SetIconSize(DWORD iIcon);
72 virtual HRESULT STDMETHODCALLTYPE GetIconSize(DWORD* piIcon);
73 virtual HRESULT STDMETHODCALLTYPE SetBitmap(HBITMAP hBitmap);
74 virtual HRESULT STDMETHODCALLTYPE GetBitmap(HBITMAP* phBitmap);
75
76 // *** IInitializeObject methods ***
77 virtual HRESULT STDMETHODCALLTYPE Initialize(THIS);
78
79 // *** IOleCommandTarget methods ***
80 virtual HRESULT STDMETHODCALLTYPE QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText);
81 virtual HRESULT STDMETHODCALLTYPE Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut);
82
83 // *** IServiceProvider methods ***
84 virtual HRESULT STDMETHODCALLTYPE QueryService(REFGUID guidService, REFIID riid, void **ppvObject);
85
86 // *** IInputObjectSite methods ***
87 virtual HRESULT STDMETHODCALLTYPE OnFocusChangeIS(LPUNKNOWN lpUnknown, BOOL bFocus);
88
89 // *** IInputObject methods ***
90 virtual HRESULT STDMETHODCALLTYPE UIActivateIO(BOOL bActivating, LPMSG lpMsg);
91 virtual HRESULT STDMETHODCALLTYPE HasFocusIO(THIS);
92 virtual HRESULT STDMETHODCALLTYPE TranslateAcceleratorIO(LPMSG lpMsg);
93
94 // *** IDeskBar methods ***
95 virtual HRESULT STDMETHODCALLTYPE SetClient(IUnknown *punkClient);
96 virtual HRESULT STDMETHODCALLTYPE GetClient(IUnknown **ppunkClient);
97 virtual HRESULT STDMETHODCALLTYPE OnPosRectChangeDB(LPRECT prc);
98
99 DECLARE_NOT_AGGREGATABLE(CMenuDeskBar)
100 DECLARE_PROTECT_FINAL_CONSTRUCT()
101
102 DECLARE_WND_CLASS_EX(_T("BaseBar"), CS_SAVEBITS | CS_DROPSHADOW, COLOR_3DFACE)
103
104 BEGIN_MSG_MAP(CMenuDeskBar)
105 MESSAGE_HANDLER(WM_SIZE, OnSize)
106 MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
107 MESSAGE_HANDLER(WM_WINDOWPOSCHANGED, OnWindowPosChanged)
108 MESSAGE_HANDLER(WM_PAINT, OnPaint)
109 END_MSG_MAP()
110
111 BEGIN_COM_MAP(CMenuDeskBar)
112 COM_INTERFACE_ENTRY_IID(IID_IMenuPopup, IMenuPopup)
113 COM_INTERFACE_ENTRY_IID(IID_IOleCommandTarget, IOleCommandTarget)
114 COM_INTERFACE_ENTRY_IID(IID_IServiceProvider, IServiceProvider)
115 COM_INTERFACE_ENTRY_IID(IID_IInputObjectSite, IInputObjectSite)
116 COM_INTERFACE_ENTRY_IID(IID_IInputObject, IInputObject)
117 COM_INTERFACE_ENTRY_IID(IID_IDeskBar, IMenuPopup)
118 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IMenuPopup)
119 COM_INTERFACE_ENTRY_IID(IID_IObjectWithSite, IObjectWithSite)
120 COM_INTERFACE_ENTRY_IID(IID_IBanneredBar, IBanneredBar)
121 COM_INTERFACE_ENTRY_IID(IID_IInitializeObject, IInitializeObject)
122 END_COM_MAP()
123
124 private:
125
126 // message handlers
127 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
128 LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
129 LRESULT OnWindowPosChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
130 LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
131 };
132
133 extern "C"
134 HRESULT CMenuDeskBar_Constructor(REFIID riid, LPVOID *ppv)
135 {
136 *ppv = NULL;
137
138 CMenuDeskBar * deskbar = new CComObject<CMenuDeskBar>();
139
140 if (!deskbar)
141 return E_OUTOFMEMORY;
142
143 HRESULT hr = deskbar->QueryInterface(riid, ppv);
144
145 if (FAILED(hr))
146 deskbar->Release();
147
148 return hr;
149 }
150
151 CMenuDeskBar::CMenuDeskBar() :
152 m_Client(NULL),
153 m_Banner(NULL)
154 {
155 }
156
157 CMenuDeskBar::~CMenuDeskBar()
158 {
159 }
160
161 HRESULT STDMETHODCALLTYPE CMenuDeskBar::GetWindow(HWND *lphwnd)
162 {
163 if (lphwnd == NULL)
164 return E_POINTER;
165 *lphwnd = m_hWnd;
166 return S_OK;
167 }
168
169 HRESULT STDMETHODCALLTYPE CMenuDeskBar::ContextSensitiveHelp(BOOL fEnterMode)
170 {
171 return E_NOTIMPL;
172 }
173
174 HRESULT STDMETHODCALLTYPE CMenuDeskBar::OnFocusChangeIS(IUnknown *punkObj, BOOL fSetFocus)
175 {
176 CComPtr<IInputObjectSite> ios;
177
178 HRESULT hr = m_Client->QueryInterface(IID_PPV_ARG(IInputObjectSite, &ios));
179 if (FAILED(hr))
180 return hr;
181
182 return ios->OnFocusChangeIS(punkObj, fSetFocus);
183 }
184
185 HRESULT STDMETHODCALLTYPE CMenuDeskBar::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds,
186 OLECMD prgCmds [], OLECMDTEXT *pCmdText)
187 {
188 return E_NOTIMPL;
189 }
190
191 HRESULT STDMETHODCALLTYPE CMenuDeskBar::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
192 DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
193 {
194 if (IsEqualIID(*pguidCmdGroup, CGID_Explorer))
195 {
196 }
197 else if (IsEqualIID(*pguidCmdGroup, IID_IDeskBarClient))
198 {
199 switch (nCmdID)
200 {
201 case 0:
202 // hide current band
203 break;
204 case 2:
205 break;
206 case 3:
207 break;
208 }
209 }
210 return E_NOTIMPL;
211 }
212
213 HRESULT STDMETHODCALLTYPE CMenuDeskBar::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
214 {
215 if (m_Site == NULL)
216 return E_FAIL;
217
218 if (IsEqualGUID(guidService, SID_SMenuPopup) ||
219 IsEqualGUID(guidService, SID_SMenuBandParent) ||
220 IsEqualGUID(guidService, SID_STopLevelBrowser))
221 {
222 return this->QueryInterface(riid, ppvObject);
223 }
224
225 return IUnknown_QueryService(m_Site, guidService, riid, ppvObject);
226 }
227
228 HRESULT STDMETHODCALLTYPE CMenuDeskBar::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
229 {
230 return IUnknown_UIActivateIO(m_Client, fActivate, lpMsg);
231 }
232
233 HRESULT STDMETHODCALLTYPE CMenuDeskBar::HasFocusIO()
234 {
235 CComPtr<IInputObject> io;
236
237 HRESULT hr = m_Client->QueryInterface(IID_PPV_ARG(IInputObject, &io));
238 if (FAILED(hr))
239 return hr;
240
241 return io->HasFocusIO();
242 }
243
244 HRESULT STDMETHODCALLTYPE CMenuDeskBar::TranslateAcceleratorIO(LPMSG lpMsg)
245 {
246 CComPtr<IInputObject> io;
247
248 HRESULT hr = m_Client->QueryInterface(IID_PPV_ARG(IInputObject, &io));
249 if (FAILED(hr))
250 return hr;
251
252 return io->TranslateAcceleratorIO(lpMsg);
253 }
254
255 HRESULT STDMETHODCALLTYPE CMenuDeskBar::SetClient(IUnknown *punkClient)
256 {
257 CComPtr<IDeskBarClient> pDeskBandClient;
258 HRESULT hResult;
259
260 m_Client.Release();
261
262 if (punkClient == NULL)
263 return S_OK;
264
265 if (m_hWnd == NULL)
266 {
267 Create(NULL);
268 }
269
270 hResult = punkClient->QueryInterface(IID_IUnknown, reinterpret_cast<void **>(&m_Client));
271 if (FAILED(hResult))
272 return hResult;
273
274 hResult = m_Client->QueryInterface(IID_IDeskBarClient, (VOID**) &pDeskBandClient);
275 if (FAILED(hResult))
276 return hResult;
277
278 return pDeskBandClient->SetDeskBarSite(static_cast<IDeskBar*>(this));
279 }
280
281 HRESULT STDMETHODCALLTYPE CMenuDeskBar::GetClient(IUnknown **ppunkClient)
282 {
283 if (ppunkClient == NULL)
284 return E_POINTER;
285
286 if (!m_Client)
287 return E_FAIL;
288
289 return m_Client->QueryInterface(IID_PPV_ARG(IUnknown, ppunkClient));
290 }
291
292 HRESULT STDMETHODCALLTYPE CMenuDeskBar::OnPosRectChangeDB(LPRECT prc)
293 {
294 if (prc == NULL)
295 return E_POINTER;
296 return S_OK;
297 }
298
299 HRESULT STDMETHODCALLTYPE CMenuDeskBar::SetSite(IUnknown *pUnkSite)
300 {
301 m_Site = pUnkSite;
302
303 return S_OK;
304 }
305
306 HRESULT STDMETHODCALLTYPE CMenuDeskBar::GetSite(REFIID riid, void **ppvSite)
307 {
308 if (m_Site == NULL)
309 return E_FAIL;
310
311 return m_Site->QueryInterface(riid, ppvSite);
312 }
313
314 LRESULT CMenuDeskBar::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
315 {
316 HRESULT hr;
317
318 if (m_Client)
319 {
320 CComPtr<IOleWindow> pOw;
321 hr = m_Client->QueryInterface(IID_PPV_ARG(IOleWindow, &pOw));
322 if (FAILED(hr))
323 {
324 ERR("IUnknown_QueryInterface pBs failed: %x\n", hr);
325 return 0;
326 }
327
328 HWND clientWnd;
329 pOw->GetWindow(&clientWnd);
330
331 RECT rc;
332
333 GetClientRect(&rc);
334
335 if (m_Banner != NULL)
336 {
337 BITMAP bm;
338 ::GetObject(m_Banner, sizeof(bm), &bm);
339 rc.left += bm.bmWidth;
340 }
341
342 ::SetWindowPos(clientWnd, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, 0);
343 }
344
345 return 0;
346 }
347
348 LRESULT CMenuDeskBar::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
349 {
350 CComPtr<IWinEventHandler> winEventHandler;
351 LRESULT result;
352 HRESULT hResult;
353
354 result = 0;
355 if (m_Client.p != NULL)
356 {
357 hResult = m_Client->QueryInterface(IID_IWinEventHandler, reinterpret_cast<void **>(&winEventHandler));
358 if (SUCCEEDED(hResult) && winEventHandler.p != NULL)
359 hResult = winEventHandler->OnWinEvent(NULL, uMsg, wParam, lParam, &result);
360 }
361 return result;
362 }
363
364 LRESULT CMenuDeskBar::OnWindowPosChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
365 {
366 return 0;
367 }
368
369 LRESULT CMenuDeskBar::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
370 {
371 bHandled = FALSE;
372
373 if (m_Banner && !m_IconSize)
374 {
375 BITMAP bm;
376 PAINTSTRUCT ps;
377 HDC hdc = BeginPaint(&ps);
378
379 HDC hdcMem = ::CreateCompatibleDC(hdc);
380 HGDIOBJ hbmOld = ::SelectObject(hdcMem, m_Banner);
381
382 ::GetObject(m_Banner, sizeof(bm), &bm);
383
384 RECT rc;
385 if (!GetClientRect(&rc))
386 WARN("GetClientRect failed\n");
387
388 const int bx = bm.bmWidth;
389 const int by = bm.bmHeight;
390 const int cy = rc.bottom;
391
392 TRACE("Painting banner: %d by %d\n", bm.bmWidth, bm.bmHeight);
393
394 if (!::StretchBlt(hdc, 0, 0, bx, cy - by, hdcMem, 0, 0, bx, 1, SRCCOPY))
395 WARN("StretchBlt failed\n");
396
397 if (!::BitBlt(hdc, 0, cy - by, bx, by, hdcMem, 0, 0, SRCCOPY))
398 WARN("BitBlt failed\n");
399
400 ::SelectObject(hdcMem, hbmOld);
401 ::DeleteDC(hdcMem);
402
403 EndPaint(&ps);
404 }
405
406 return TRUE;
407 }
408
409 HRESULT STDMETHODCALLTYPE CMenuDeskBar::Popup(POINTL *ppt, RECTL *prcExclude, MP_POPUPFLAGS dwFlags)
410 {
411 HRESULT hr;
412 CComPtr<IOleCommandTarget> oct;
413 CComPtr<IInputObject> io;
414 CComPtr<IDeskBand> band;
415 CComPtr<IDeskBarClient> dbc;
416
417 if (m_hWnd == NULL)
418 return E_FAIL;
419
420 hr = IUnknown_QueryService(m_Client, SID_SMenuBandChild, IID_PPV_ARG(IOleCommandTarget, &oct));
421 if (FAILED(hr))
422 return hr;
423
424 hr = m_Client->QueryInterface(IID_PPV_ARG(IDeskBarClient, &dbc));
425 if (FAILED(hr))
426 return hr;
427
428 // No clue about the arg, using anything != 0
429 hr = dbc->UIActivateDBC(TRUE);
430 if (FAILED(hr))
431 return hr;
432
433 RECT rc = { 0 };
434 hr = dbc->GetSize(0, &rc);
435 if (FAILED(hr))
436 return hr;
437
438 // Unknown meaning
439 const int CMD = 19;
440 const int CMD_EXEC_OPT = 0;
441
442 hr = IUnknown_QueryServiceExec(m_Client, SID_SMenuBandChild, &CLSID_MenuBand, CMD, CMD_EXEC_OPT, NULL, NULL);
443 if (FAILED(hr))
444 return hr;
445
446 ::AdjustWindowRect(&rc, ::GetWindowLong(m_hWnd, GWL_STYLE), FALSE);
447 rc.right -= rc.left;
448 rc.bottom -= rc.top;
449
450 if (m_Banner != NULL)
451 {
452 BITMAP bm;
453 ::GetObject(m_Banner, sizeof(bm), &bm);
454 rc.right += bm.bmWidth;
455 }
456
457 int x = ppt->x;
458 int y = ppt->y - rc.bottom;
459 int cx = rc.right;
460 int cy = rc.bottom;
461
462 if (y < 0)
463 {
464 y = 0;
465 }
466
467 // if (y+cy > work area height) cy = work area height - y
468
469 this->SetWindowPos(HWND_TOPMOST, x, y, cx, cy, SWP_SHOWWINDOW);
470
471 // HACK: The bar needs to be notified of the size AFTER it is shown.
472 // Quick & dirty way of getting it done.
473 BOOL bHandled;
474 OnSize(WM_SIZE, 0, 0, bHandled);
475
476 UIActivateIO(TRUE, NULL);
477
478 return S_OK;
479 }
480
481 HRESULT STDMETHODCALLTYPE CMenuDeskBar::SetIconSize(THIS_ DWORD iIcon)
482 {
483 HRESULT hr;
484 m_IconSize = iIcon;
485
486 // Unknown meaning (set flags? set icon size?)
487 const int CMD = 16;
488 const int CMD_EXEC_OPT = iIcon ? 0 : 2; // seems to work
489
490 hr = IUnknown_QueryServiceExec(m_Client, SID_SMenuBandChild, &CLSID_MenuBand, CMD, CMD_EXEC_OPT, NULL, NULL);
491 if (FAILED(hr))
492 return hr;
493
494 BOOL bHandled;
495 OnSize(WM_SIZE, 0, 0, bHandled);
496
497 return hr;
498 }
499
500 HRESULT STDMETHODCALLTYPE CMenuDeskBar::GetIconSize(THIS_ DWORD* piIcon)
501 {
502 if (piIcon)
503 *piIcon = m_IconSize;
504 return S_OK;
505 }
506
507 HRESULT STDMETHODCALLTYPE CMenuDeskBar::SetBitmap(THIS_ HBITMAP hBitmap)
508 {
509 m_Banner = hBitmap;
510
511 BOOL bHandled;
512 OnSize(WM_SIZE, 0, 0, bHandled);
513
514 return S_OK;
515 }
516
517 HRESULT STDMETHODCALLTYPE CMenuDeskBar::GetBitmap(THIS_ HBITMAP* phBitmap)
518 {
519 if (phBitmap)
520 *phBitmap = m_Banner;
521 return S_OK;
522 }
523
524 HRESULT STDMETHODCALLTYPE CMenuDeskBar::OnSelect(
525 DWORD dwSelectType)
526 {
527 CComPtr<IDeskBarClient> dbc;
528 HRESULT hr;
529
530 bool bubbleUp = false;
531 bool cancel = false;
532
533 switch (dwSelectType)
534 {
535 case MPOS_FULLCANCEL:
536 case MPOS_EXECUTE:
537 bubbleUp = true;
538 cancel = true;
539 break;
540 case MPOS_CANCELLEVEL:
541 cancel = true;
542 break;
543 case MPOS_SELECTLEFT:
544 case MPOS_SELECTRIGHT:
545 // if unhandled, spread upwards?
546 bubbleUp = true;
547 break;
548 case MPOS_CHILDTRACKING:
549 break;
550 }
551
552 if (cancel)
553 {
554 hr = m_Client->QueryInterface(IID_PPV_ARG(IDeskBarClient, &dbc));
555 if (FAILED(hr))
556 return hr;
557
558 hr = dbc->UIActivateDBC(FALSE);
559 if (FAILED(hr))
560 return hr;
561
562 SetWindowPos(m_hWnd, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
563
564 UIActivateIO(FALSE, NULL);
565 }
566
567 //if (bubbleUp && m_Site)
568 //{
569 // CComPtr<IMenuPopup> pmp;
570 // HRESULT hr = IUnknown_QueryService(m_Site, SID_SMenuPopup, IID_PPV_ARG(IMenuPopup, &pmp));
571 // if (FAILED(hr))
572 // return hr;
573 // pmp->OnSelect(dwSelectType);
574 //}
575
576 return S_OK;
577 }
578
579 HRESULT STDMETHODCALLTYPE CMenuDeskBar::SetSubMenu(
580 IMenuPopup *pmp,
581 BOOL fSet)
582 {
583 if (fSet)
584 {
585 m_SubMenuParent = pmp;
586 }
587 else
588 {
589 if (m_SubMenuParent)
590 {
591 if (SHIsSameObject(pmp, m_SubMenuParent))
592 {
593 m_SubMenuParent = NULL;
594 }
595 }
596 }
597 return S_OK;
598 }
599
600
601 HRESULT STDMETHODCALLTYPE CMenuDeskBar::Initialize(THIS)
602 {
603 return S_OK;
604 }