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