[RSHELL]
[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 (IsEqualGUID(guidService, SID_SMenuPopup) ||
232 IsEqualGUID(guidService, SID_SMenuBandParent) ||
233 IsEqualGUID(guidService, SID_STopLevelBrowser))
234 {
235 return this->QueryInterface(riid, ppvObject);
236 }
237
238 if (m_Site == NULL)
239 return E_NOINTERFACE;
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 if (m_Client)
337 {
338 RECT rc;
339
340 GetClientRect(&rc);
341
342 if (m_Banner != NULL)
343 {
344 BITMAP bm;
345 ::GetObject(m_Banner, sizeof(bm), &bm);
346 rc.left += bm.bmWidth;
347 }
348
349 ::SetWindowPos(m_ClientWindow, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, 0);
350 }
351
352 return 0;
353 }
354
355 LRESULT CMenuDeskBar::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
356 {
357 CComPtr<IWinEventHandler> winEventHandler;
358 LRESULT result;
359 HRESULT hr;
360
361 result = 0;
362 if (m_Client.p != NULL)
363 {
364 hr = m_Client->QueryInterface(IID_PPV_ARG(IWinEventHandler, &winEventHandler));
365 if (SUCCEEDED(hr) && winEventHandler.p != NULL)
366 hr = winEventHandler->OnWinEvent(NULL, uMsg, wParam, lParam, &result);
367 }
368 return result;
369 }
370
371 LRESULT CMenuDeskBar::OnWindowPosChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
372 {
373 return 0;
374 }
375
376 LRESULT CMenuDeskBar::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
377 {
378 bHandled = FALSE;
379
380 if (m_Banner && !m_IconSize)
381 {
382 BITMAP bm;
383 PAINTSTRUCT ps;
384 HDC hdc = BeginPaint(&ps);
385
386 HDC hdcMem = ::CreateCompatibleDC(hdc);
387 HGDIOBJ hbmOld = ::SelectObject(hdcMem, m_Banner);
388
389 ::GetObject(m_Banner, sizeof(bm), &bm);
390
391 RECT rc;
392 if (!GetClientRect(&rc))
393 WARN("GetClientRect failed\n");
394
395 const int bx = bm.bmWidth;
396 const int by = bm.bmHeight;
397 const int cy = rc.bottom;
398
399 TRACE("Painting banner: %d by %d\n", bm.bmWidth, bm.bmHeight);
400
401 if (!::StretchBlt(hdc, 0, 0, bx, cy - by, hdcMem, 0, 0, bx, 1, SRCCOPY))
402 WARN("StretchBlt failed\n");
403
404 if (!::BitBlt(hdc, 0, cy - by, bx, by, hdcMem, 0, 0, SRCCOPY))
405 WARN("BitBlt failed\n");
406
407 ::SelectObject(hdcMem, hbmOld);
408 ::DeleteDC(hdcMem);
409
410 EndPaint(&ps);
411 }
412
413 return TRUE;
414 }
415
416 HRESULT STDMETHODCALLTYPE CMenuDeskBar::Popup(POINTL *ppt, RECTL *prcExclude, MP_POPUPFLAGS dwFlags)
417 {
418 HRESULT hr;
419 CComPtr<IOleCommandTarget> oct;
420 CComPtr<IInputObject> io;
421 CComPtr<IDeskBand> band;
422 CComPtr<IDeskBarClient> dbc;
423
424 if (m_hWnd == NULL)
425 return E_FAIL;
426
427 hr = IUnknown_QueryService(m_Client, SID_SMenuBandChild, IID_PPV_ARG(IOleCommandTarget, &oct));
428 if (FAILED(hr))
429 return hr;
430
431 hr = m_Client->QueryInterface(IID_PPV_ARG(IDeskBarClient, &dbc));
432 if (FAILED(hr))
433 return hr;
434
435 hr = dbc->SetModeDBC(1);
436 // Allow it to fail with E_NOTIMPL.
437
438 // No clue about the arg, using anything != 0
439 hr = dbc->UIActivateDBC(TRUE);
440 if (FAILED(hr))
441 return hr;
442
443 RECT rc = { 0 };
444 hr = dbc->GetSize(0, &rc);
445 if (FAILED(hr))
446 return hr;
447
448 // Unknown meaning
449 const int CMD = 19;
450 const int CMD_EXEC_OPT = 0;
451
452 hr = IUnknown_QueryServiceExec(m_Client, SID_SMenuBandChild, &CLSID_MenuBand, CMD, CMD_EXEC_OPT, NULL, NULL);
453 if (FAILED(hr))
454 return hr;
455
456 ::AdjustWindowRect(&rc, ::GetWindowLong(m_hWnd, GWL_STYLE), FALSE);
457 rc.right -= rc.left;
458 rc.bottom -= rc.top;
459
460 if (m_Banner != NULL)
461 {
462 BITMAP bm;
463 ::GetObject(m_Banner, sizeof(bm), &bm);
464 rc.right += bm.bmWidth;
465 }
466
467 int x = ppt->x;
468 int y = ppt->y - rc.bottom;
469 int cx = rc.right;
470 int cy = rc.bottom;
471
472 if (y < 0)
473 {
474 y = 0;
475 }
476
477 // if (y+cy > work area height) cy = work area height - y
478
479 this->SetWindowPos(HWND_TOPMOST, x, y, cx, cy, SWP_SHOWWINDOW);
480
481 // HACK: The bar needs to be notified of the size AFTER it is shown.
482 // Quick & dirty way of getting it done.
483 BOOL bHandled;
484 OnSize(WM_SIZE, 0, 0, bHandled);
485
486 UIActivateIO(TRUE, NULL);
487
488 return S_OK;
489 }
490
491 HRESULT STDMETHODCALLTYPE CMenuDeskBar::SetIconSize(THIS_ DWORD iIcon)
492 {
493 HRESULT hr;
494 m_IconSize = iIcon;
495
496 // Unknown meaning (set flags? set icon size?)
497 const int CMD = 16;
498 const int CMD_EXEC_OPT = iIcon ? 0 : 2; // seems to work
499
500 hr = IUnknown_QueryServiceExec(m_Client, SID_SMenuBandChild, &CLSID_MenuBand, CMD, CMD_EXEC_OPT, NULL, NULL);
501 if (FAILED(hr))
502 return hr;
503
504 BOOL bHandled;
505 OnSize(WM_SIZE, 0, 0, bHandled);
506
507 return hr;
508 }
509
510 HRESULT STDMETHODCALLTYPE CMenuDeskBar::GetIconSize(THIS_ DWORD* piIcon)
511 {
512 if (piIcon)
513 *piIcon = m_IconSize;
514 return S_OK;
515 }
516
517 HRESULT STDMETHODCALLTYPE CMenuDeskBar::SetBitmap(THIS_ HBITMAP hBitmap)
518 {
519 m_Banner = hBitmap;
520
521 BOOL bHandled;
522 OnSize(WM_SIZE, 0, 0, bHandled);
523
524 return S_OK;
525 }
526
527 HRESULT STDMETHODCALLTYPE CMenuDeskBar::GetBitmap(THIS_ HBITMAP* phBitmap)
528 {
529 if (phBitmap)
530 *phBitmap = m_Banner;
531 return S_OK;
532 }
533
534 HRESULT STDMETHODCALLTYPE CMenuDeskBar::OnSelect(
535 DWORD dwSelectType)
536 {
537 CComPtr<IDeskBarClient> dbc;
538 HRESULT hr;
539
540 switch (dwSelectType)
541 {
542 case MPOS_EXECUTE:
543 case MPOS_FULLCANCEL:
544 case MPOS_CANCELLEVEL:
545
546 hr = m_Client->QueryInterface(IID_PPV_ARG(IDeskBarClient, &dbc));
547 if (FAILED(hr))
548 return hr;
549
550 hr = dbc->UIActivateDBC(FALSE);
551 if (FAILED(hr))
552 return hr;
553
554 SetWindowPos(m_hWnd, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
555
556 UIActivateIO(FALSE, NULL);
557
558 if (dwSelectType == MPOS_CANCELLEVEL)
559 break;
560
561 dwSelectType = MPOS_CANCELLEVEL;
562 case MPOS_SELECTLEFT:
563 case MPOS_SELECTRIGHT:
564 /*CComPtr<IMenuPopup> pmp;
565 hr = IUnknown_QueryService(m_Client, SID_SMenuBandChild, IID_PPV_ARG(IMenuPopup, &pmp));
566 if (FAILED(hr))
567 return hr;*/
568
569 hr = m_SubMenuParent->OnSelect(dwSelectType);
570 if (FAILED(hr))
571 return hr;
572 case MPOS_CHILDTRACKING:
573 break;
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 }