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