[EXPLORER] -If rshell is present and CBandSiteMenu_CreateInstance or CBandSite_Create...
[reactos.git] / reactos / base / shell / explorer / tbsite.cpp
1 /*
2 * ReactOS Explorer
3 *
4 * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
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 Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "precomp.h"
22
23 #include <shdeprecated.h>
24
25 /*****************************************************************************
26 ** ITrayBandSite ************************************************************
27 *****************************************************************************/
28
29 // WARNING: Can't use ATL for this class due to our ATL not fully supporting the AGGREGATION functions needed for this class to be an "outer" class
30 // it works just fine this way.
31 class CTrayBandSite :
32 public ITrayBandSite,
33 public IBandSite,
34 public IBandSiteStreamCallback
35 /* TODO: IWinEventHandler */
36 {
37 volatile LONG m_RefCount;
38
39 CComPtr<ITrayWindow> m_Tray;
40
41 CComPtr<IUnknown> m_Inner;
42 CComPtr<IBandSite> m_BandSite;
43 CComPtr<IDeskBand> m_TaskBand;
44 CComPtr<IWinEventHandler> m_WindowEventHandler;
45 CComPtr<IContextMenu> m_ContextMenu;
46
47 HWND m_Rebar;
48
49 union
50 {
51 DWORD dwFlags;
52 struct
53 {
54 DWORD Locked : 1;
55 };
56 };
57
58 public:
59
60 virtual ULONG STDMETHODCALLTYPE AddRef()
61 {
62 return InterlockedIncrement(&m_RefCount);
63 }
64
65 virtual ULONG STDMETHODCALLTYPE Release()
66 {
67 ULONG Ret = InterlockedDecrement(&m_RefCount);
68
69 if (Ret == 0)
70 delete this;
71
72 return Ret;
73 }
74
75 virtual HRESULT STDMETHODCALLTYPE QueryInterface(IN REFIID riid, OUT LPVOID *ppvObj)
76 {
77 if (ppvObj == NULL)
78 return E_POINTER;
79
80 if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IBandSiteHelper))
81 {
82 // return IBandSiteStreamCallback's IUnknown
83 *ppvObj = static_cast<IBandSiteStreamCallback*>(this);
84 }
85 else if (IsEqualIID(riid, IID_IBandSite))
86 {
87 *ppvObj = static_cast<IBandSite*>(this);
88 }
89 else if (IsEqualIID(riid, IID_IWinEventHandler))
90 {
91 TRACE("ITaskBandSite: IWinEventHandler queried!\n");
92 *ppvObj = NULL;
93 return E_NOINTERFACE;
94 }
95 else if (m_Inner != NULL)
96 {
97 return m_Inner->QueryInterface(riid, ppvObj);
98 }
99 else
100 {
101 *ppvObj = NULL;
102 return E_NOINTERFACE;
103 }
104
105 AddRef();
106 return S_OK;
107 }
108
109 public:
110 CTrayBandSite() :
111 m_RefCount(0),
112 m_Rebar(NULL)
113 {
114 }
115
116 virtual ~CTrayBandSite() { }
117
118 virtual HRESULT STDMETHODCALLTYPE OnLoad(
119 IN OUT IStream *pStm,
120 IN REFIID riid,
121 OUT PVOID *pvObj)
122 {
123 LARGE_INTEGER liPosZero;
124 ULARGE_INTEGER liCurrent;
125 CLSID clsid;
126 ULONG ulRead;
127 HRESULT hRet;
128
129 /* NOTE: Callback routine called by the shell while loading the task band
130 stream. We use it to intercept the default behavior when the task
131 band is loaded from the stream.
132
133 NOTE: riid always points to IID_IUnknown! This is because the shell hasn't
134 read anything from the stream and therefore doesn't know what CLSID
135 it's dealing with. We'll have to find it out ourselves by reading
136 the GUID from the stream. */
137
138 /* Read the current position of the stream, we'll have to reset it everytime
139 we read a CLSID that's not the task band... */
140 ZeroMemory(&liPosZero, sizeof(liPosZero));
141 hRet = pStm->Seek(liPosZero, STREAM_SEEK_CUR, &liCurrent);
142
143 if (SUCCEEDED(hRet))
144 {
145 /* Now let's read the CLSID from the stream and see if it's our task band */
146 hRet = pStm->Read(&clsid, (ULONG)sizeof(clsid), &ulRead);
147
148 if (SUCCEEDED(hRet) && ulRead == sizeof(clsid))
149 {
150 if (IsEqualGUID(clsid, CLSID_ITaskBand))
151 {
152 ASSERT(m_TaskBand != NULL);
153 /* We're trying to load the task band! Let's create it... */
154
155 hRet = m_TaskBand->QueryInterface(
156 riid,
157 pvObj);
158 if (SUCCEEDED(hRet))
159 {
160 /* Load the stream */
161 TRACE("IBandSiteStreamCallback::OnLoad intercepted the task band CLSID!\n");
162 }
163
164 return hRet;
165 }
166 }
167 }
168
169 /* Reset the position and let the shell do all the work for us */
170 hRet = pStm->Seek(
171 *(LARGE_INTEGER*) &liCurrent,
172 STREAM_SEEK_SET,
173 NULL);
174 if (SUCCEEDED(hRet))
175 {
176 /* Let the shell handle everything else for us :) */
177 hRet = OleLoadFromStream(pStm,
178 riid,
179 pvObj);
180 }
181
182 if (!SUCCEEDED(hRet))
183 {
184 TRACE("IBandSiteStreamCallback::OnLoad(0x%p, 0x%p, 0x%p) returns 0x%x\n", pStm, riid, pvObj, hRet);
185 }
186
187 return hRet;
188 }
189
190 virtual HRESULT STDMETHODCALLTYPE OnSave(
191 IN OUT IUnknown *pUnk,
192 IN OUT IStream *pStm)
193 {
194 /* NOTE: Callback routine called by the shell while saving the task band
195 stream. We use it to intercept the default behavior when the task
196 band is saved to the stream */
197 /* FIXME: Implement */
198 TRACE("IBandSiteStreamCallback::OnSave(0x%p, 0x%p) returns E_NOTIMPL\n", pUnk, pStm);
199 return E_NOTIMPL;
200 }
201
202 virtual HRESULT STDMETHODCALLTYPE IsTaskBand(IN IUnknown *punk)
203 {
204 return IsSameObject(m_BandSite, punk);
205 }
206
207 virtual HRESULT STDMETHODCALLTYPE ProcessMessage(
208 IN HWND hWnd,
209 IN UINT uMsg,
210 IN WPARAM wParam,
211 IN LPARAM lParam,
212 OUT LRESULT *plResult)
213 {
214 HRESULT hRet;
215
216 ASSERT(m_Rebar != NULL);
217
218 /* Custom task band behavior */
219 switch (uMsg)
220 {
221 case WM_NOTIFY:
222 {
223 const NMHDR *nmh = (const NMHDR *) lParam;
224
225 if (nmh->hwndFrom == m_Rebar)
226 {
227 switch (nmh->code)
228 {
229 case NM_NCHITTEST:
230 {
231 LPNMMOUSE nmm = (LPNMMOUSE) lParam;
232
233 if (nmm->dwHitInfo == RBHT_CLIENT || nmm->dwHitInfo == RBHT_NOWHERE ||
234 nmm->dwItemSpec == (DWORD_PTR) -1)
235 {
236 /* Make the rebar control appear transparent so the user
237 can drag the tray window */
238 *plResult = HTTRANSPARENT;
239 }
240 return S_OK;
241 }
242
243 case RBN_MINMAX:
244 /* Deny if an Administrator disabled this "feature" */
245 *plResult = (SHRestricted(REST_NOMOVINGBAND) != 0);
246 return S_OK;
247 }
248 }
249
250 //TRACE("ITrayBandSite::ProcessMessage: WM_NOTIFY for 0x%p, From: 0x%p, Code: NM_FIRST-%u...\n", hWnd, nmh->hwndFrom, NM_FIRST - nmh->code);
251 break;
252 }
253 }
254
255 /* Forward to the shell's IWinEventHandler interface to get the default shell behavior! */
256 if (!m_WindowEventHandler)
257 return E_FAIL;
258
259 /*TRACE("Calling IWinEventHandler::ProcessMessage(0x%p, 0x%x, 0x%p, 0x%p, 0x%p) hWndRebar=0x%p\n", hWnd, uMsg, wParam, lParam, plResult, hWndRebar);*/
260 hRet = m_WindowEventHandler->OnWinEvent(hWnd, uMsg, wParam, lParam, plResult);
261
262 #if 0
263 if (FAILED(hRet))
264 {
265 if (uMsg == WM_NOTIFY)
266 {
267 const NMHDR *nmh = (const NMHDR *) lParam;
268 ERR("ITrayBandSite->IWinEventHandler::ProcessMessage: WM_NOTIFY for 0x%p, From: 0x%p, Code: NM_FIRST-%u returned 0x%x\n", hWnd, nmh->hwndFrom, NM_FIRST - nmh->code, hRet);
269 }
270 else
271 {
272 ERR("ITrayBandSite->IWinEventHandler::ProcessMessage(0x%p,0x%x,0x%p,0x%p,0x%p->0x%p) returned: 0x%x\n", hWnd, uMsg, wParam, lParam, plResult, *plResult, hRet);
273 }
274 }
275 #endif
276
277 return hRet;
278 }
279
280 virtual HRESULT STDMETHODCALLTYPE AddContextMenus(
281 IN HMENU hmenu,
282 IN UINT indexMenu,
283 IN UINT idCmdFirst,
284 IN UINT idCmdLast,
285 IN UINT uFlags,
286 OUT IContextMenu **ppcm)
287 {
288 HRESULT hRet;
289
290 if (m_ContextMenu == NULL)
291 {
292 /* Cache the context menu so we don't need to CoCreateInstance all the time... */
293 hRet = _CBandSiteMenu_CreateInstance(IID_PPV_ARG(IContextMenu, &m_ContextMenu));
294 if (FAILED_UNEXPECTEDLY(hRet))
295 return hRet;
296
297 hRet = IUnknown_SetOwner(m_ContextMenu, (IBandSite*)this);
298 if (FAILED_UNEXPECTEDLY(hRet))
299 return hRet;
300 }
301
302 if (ppcm != NULL)
303 {
304 m_ContextMenu->AddRef();
305 *ppcm = m_ContextMenu;
306 }
307
308 /* Add the menu items */
309 hRet = m_ContextMenu->QueryContextMenu(hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
310 if (FAILED_UNEXPECTEDLY(hRet))
311 return hRet;
312
313 return S_OK;
314 }
315
316 virtual HRESULT STDMETHODCALLTYPE Lock(IN BOOL bLock)
317 {
318 BOOL bPrevLocked = Locked;
319 BANDSITEINFO bsi;
320 HRESULT hRet;
321
322 ASSERT(m_BandSite != NULL);
323
324 if (bPrevLocked != bLock)
325 {
326 Locked = bLock;
327
328 bsi.dwMask = BSIM_STYLE;
329 bsi.dwStyle = (Locked ? BSIS_LOCKED | BSIS_NOGRIPPER : BSIS_AUTOGRIPPER);
330
331 hRet = m_BandSite->SetBandSiteInfo(&bsi);
332
333 /* HACK for CORE-9809 ! */
334 if (hRet == E_NOTIMPL)
335 hRet = S_OK;
336 else
337 ERR("HACK for CORE-9809 no longer needed!\n");
338
339 if (SUCCEEDED(hRet))
340 {
341 hRet = Update();
342 }
343
344 return hRet;
345 }
346
347 return S_FALSE;
348 }
349
350 /*******************************************************************/
351
352 virtual HRESULT STDMETHODCALLTYPE AddBand(IN IUnknown *punk)
353 {
354 /* Send the DBID_DELAYINIT command to initialize the band to be added */
355 /* FIXME: Should be delayed */
356 IUnknown_Exec(punk, IID_IDeskBand, DBID_DELAYINIT, 0, NULL, NULL);
357
358 return m_BandSite->AddBand(punk);
359 }
360
361 virtual HRESULT STDMETHODCALLTYPE EnumBands(
362 IN UINT uBand,
363 OUT DWORD *pdwBandID)
364 {
365 return m_BandSite->EnumBands(uBand, pdwBandID);
366 }
367
368 virtual HRESULT STDMETHODCALLTYPE QueryBand(
369 IN DWORD dwBandID,
370 OUT IDeskBand **ppstb,
371 OUT DWORD *pdwState,
372 OUT LPWSTR pszName,
373 IN int cchName)
374 {
375 HRESULT hRet;
376 IDeskBand *pstb = NULL;
377
378 hRet = m_BandSite->QueryBand(
379 dwBandID,
380 &pstb,
381 pdwState,
382 pszName,
383 cchName);
384
385 if (SUCCEEDED(hRet))
386 {
387 hRet = IsSameObject(pstb, m_TaskBand);
388 if (hRet == S_OK)
389 {
390 /* Add the BSSF_UNDELETEABLE flag to pdwState because the task bar band shouldn't be deletable */
391 if (pdwState != NULL)
392 *pdwState |= BSSF_UNDELETEABLE;
393 }
394 else if (!SUCCEEDED(hRet))
395 {
396 pstb->Release();
397 pstb = NULL;
398 }
399
400 if (ppstb != NULL)
401 *ppstb = pstb;
402 }
403 else if (ppstb != NULL)
404 *ppstb = NULL;
405
406 return hRet;
407 }
408
409 virtual HRESULT STDMETHODCALLTYPE SetBandState(
410 IN DWORD dwBandID,
411 IN DWORD dwMask,
412 IN DWORD dwState)
413 {
414 return m_BandSite->SetBandState(dwBandID, dwMask, dwState);
415 }
416
417 virtual HRESULT STDMETHODCALLTYPE RemoveBand(
418 IN DWORD dwBandID)
419 {
420 return m_BandSite->RemoveBand(dwBandID);
421 }
422
423 virtual HRESULT STDMETHODCALLTYPE GetBandObject(
424 IN DWORD dwBandID,
425 IN REFIID riid,
426 OUT VOID **ppv)
427 {
428 return m_BandSite->GetBandObject(dwBandID, riid, ppv);
429 }
430
431 virtual HRESULT STDMETHODCALLTYPE SetBandSiteInfo(
432 IN const BANDSITEINFO *pbsinfo)
433 {
434 return m_BandSite->SetBandSiteInfo(pbsinfo);
435 }
436
437 virtual HRESULT STDMETHODCALLTYPE GetBandSiteInfo(
438 IN OUT BANDSITEINFO *pbsinfo)
439 {
440 return m_BandSite->GetBandSiteInfo(pbsinfo);
441 }
442
443 virtual BOOL HasTaskBand()
444 {
445 CComPtr<IPersist> pBand;
446 CLSID BandCLSID;
447 DWORD dwBandID;
448 UINT uBand = 0;
449
450 /* Enumerate all bands */
451 while (SUCCEEDED(m_BandSite->EnumBands(uBand, &dwBandID)))
452 {
453 if (SUCCEEDED(m_BandSite->GetBandObject(dwBandID, IID_PPV_ARG(IPersist, &pBand))))
454 {
455 if (SUCCEEDED(pBand->GetClassID(&BandCLSID)))
456 {
457 if (IsEqualGUID(BandCLSID, CLSID_ITaskBand))
458 {
459 return TRUE;
460 }
461 }
462 }
463 uBand++;
464 }
465
466 return FALSE;
467 }
468
469 virtual HRESULT Update()
470 {
471 return IUnknown_Exec(m_Inner,
472 IID_IDeskBand,
473 DBID_BANDINFOCHANGED,
474 0,
475 NULL,
476 NULL);
477 }
478
479 virtual VOID BroadcastOleCommandExec(REFGUID pguidCmdGroup,
480 DWORD nCmdID,
481 DWORD nCmdExecOpt,
482 VARIANTARG *pvaIn,
483 VARIANTARG *pvaOut)
484 {
485 IOleCommandTarget *pOct;
486 DWORD dwBandID;
487 UINT uBand = 0;
488
489 /* Enumerate all bands */
490 while (SUCCEEDED(m_BandSite->EnumBands(uBand, &dwBandID)))
491 {
492 if (SUCCEEDED(m_BandSite->GetBandObject(dwBandID, IID_PPV_ARG(IOleCommandTarget, &pOct))))
493 {
494 /* Execute the command */
495 pOct->Exec(
496 &pguidCmdGroup,
497 nCmdID,
498 nCmdExecOpt,
499 pvaIn,
500 pvaOut);
501
502 pOct->Release();
503 }
504
505 uBand++;
506 }
507 }
508
509 virtual HRESULT FinishInit()
510 {
511 /* Broadcast the DBID_FINISHINIT command */
512 BroadcastOleCommandExec(IID_IDeskBand, DBID_FINISHINIT, 0, NULL, NULL);
513
514 return S_OK;
515 }
516
517 virtual HRESULT Show(IN BOOL bShow)
518 {
519 CComPtr<IDeskBarClient> pDbc;
520 HRESULT hRet;
521
522 hRet = m_BandSite->QueryInterface(IID_PPV_ARG(IDeskBarClient, &pDbc));
523 if (SUCCEEDED(hRet))
524 {
525 hRet = pDbc->UIActivateDBC(bShow ? DBC_SHOW : DBC_HIDE);
526 }
527
528 return hRet;
529 }
530
531 virtual HRESULT LoadFromStream(IN OUT IStream *pStm)
532 {
533 CComPtr<IPersistStream> pPStm;
534 HRESULT hRet;
535
536 ASSERT(m_BandSite != NULL);
537
538 /* We implement the undocumented COM interface IBandSiteStreamCallback
539 that the shell will query so that we can intercept and custom-load
540 the task band when it finds the task band's CLSID (which is internal).
541 This way we can prevent the shell from attempting to CoCreateInstance
542 the (internal) task band, resulting in a failure... */
543 hRet = m_BandSite->QueryInterface(IID_PPV_ARG(IPersistStream, &pPStm));
544 if (SUCCEEDED(hRet))
545 {
546 hRet = pPStm->Load(pStm);
547 TRACE("->Load() returned 0x%x\n", hRet);
548 }
549
550 return hRet;
551 }
552
553 virtual IStream * GetUserBandsStream(IN DWORD grfMode)
554 {
555 HKEY hkStreams;
556 IStream *Stream = NULL;
557
558 if (RegCreateKeyW(hkExplorer,
559 L"Streams",
560 &hkStreams) == ERROR_SUCCESS)
561 {
562 Stream = SHOpenRegStreamW(hkStreams,
563 L"Desktop",
564 L"TaskbarWinXP",
565 grfMode);
566
567 RegCloseKey(hkStreams);
568 }
569
570 return Stream;
571 }
572
573 virtual IStream * GetDefaultBandsStream(IN DWORD grfMode)
574 {
575 HKEY hkStreams;
576 IStream *Stream = NULL;
577
578 if (RegCreateKeyW(HKEY_LOCAL_MACHINE,
579 L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Streams",
580 &hkStreams) == ERROR_SUCCESS)
581 {
582 Stream = SHOpenRegStreamW(hkStreams,
583 L"Desktop",
584 L"Default Taskbar",
585 grfMode);
586
587 RegCloseKey(hkStreams);
588 }
589
590 return Stream;
591 }
592
593 virtual HRESULT Load()
594 {
595 IStream *pStm;
596 HRESULT hRet;
597
598 /* Try to load the user's settings */
599 pStm = GetUserBandsStream(STGM_READ);
600 if (pStm != NULL)
601 {
602 hRet = LoadFromStream(pStm);
603
604 TRACE("Loaded user bands settings: 0x%x\n", hRet);
605 pStm->Release();
606 }
607 else
608 hRet = E_FAIL;
609
610 /* If the user's settings couldn't be loaded, try with
611 default settings (ie. when the user logs in for the
612 first time! */
613 if (!SUCCEEDED(hRet))
614 {
615 pStm = GetDefaultBandsStream(STGM_READ);
616 if (pStm != NULL)
617 {
618 hRet = LoadFromStream(pStm);
619
620 TRACE("Loaded default user bands settings: 0x%x\n", hRet);
621 pStm->Release();
622 }
623 else
624 hRet = E_FAIL;
625 }
626
627 return hRet;
628 }
629
630 HRESULT _Init(IN ITrayWindow *tray, IN IDeskBand* pTaskBand)
631 {
632 CComPtr<IDeskBarClient> pDbc;
633 CComPtr<IDeskBand> pDb;
634 CComPtr<IOleWindow> pOw;
635 HRESULT hRet;
636
637 m_Tray = tray;
638 m_TaskBand = pTaskBand;
639
640 /* Create the RebarBandSite */
641 hRet = _CBandSite_CreateInstance(static_cast<IBandSite*>(this), IID_PPV_ARG(IUnknown, &m_Inner));
642 if (FAILED_UNEXPECTEDLY(hRet))
643 return hRet;
644
645 hRet = m_Inner->QueryInterface(IID_PPV_ARG(IBandSite, &m_BandSite));
646 if (FAILED_UNEXPECTEDLY(hRet))
647 return hRet;
648
649 hRet = m_Inner->QueryInterface(IID_PPV_ARG(IWinEventHandler, &m_WindowEventHandler));
650 if (FAILED_UNEXPECTEDLY(hRet))
651 return hRet;
652
653 hRet = m_Inner->QueryInterface(IID_PPV_ARG(IDeskBarClient, &pDbc));
654 if (FAILED_UNEXPECTEDLY(hRet))
655 return hRet;
656
657
658
659
660 /* Crete the rebar in the tray */
661 hRet = pDbc->SetDeskBarSite(tray);
662 if (FAILED_UNEXPECTEDLY(hRet))
663 return hRet;
664
665 hRet = pDbc->GetWindow(&m_Rebar);
666 if (FAILED_UNEXPECTEDLY(hRet))
667 return hRet;
668
669 SetWindowStyle(m_Rebar, RBS_BANDBORDERS, 0);
670
671 /* Set the Desk Bar mode to the current one */
672 DWORD dwMode = 0;
673 /* FIXME: We need to set the mode (and update) whenever the user docks
674 the tray window to another monitor edge! */
675 if (!m_Tray->IsHorizontal())
676 dwMode = DBIF_VIEWMODE_VERTICAL;
677
678 hRet = pDbc->SetModeDBC(dwMode);
679
680 /* Load the saved state of the task band site */
681 /* FIXME: We should delay loading shell extensions, also see DBID_DELAYINIT */
682 Load();
683
684 /* Add the task bar band if it hasn't been added while loading */
685 if (!HasTaskBand())
686 {
687 hRet = m_BandSite->AddBand(m_TaskBand);
688 if (FAILED_UNEXPECTEDLY(hRet))
689 return hRet;
690 }
691
692 /* Should we send this after showing it? */
693 Update();
694
695 /* FIXME: When should we send this? Does anyone care anyway? */
696 FinishInit();
697
698 /* Activate the band site */
699 Show(TRUE);
700
701 return S_OK;
702 }
703 };
704 /*******************************************************************/
705
706 HRESULT CTrayBandSite_CreateInstance(IN ITrayWindow *tray, IN IDeskBand* pTaskBand, OUT ITrayBandSite** pBandSite)
707 {
708 HRESULT hr;
709
710 CTrayBandSite * tb = new CTrayBandSite();
711 if (!tb)
712 return E_FAIL;
713
714 tb->AddRef();
715
716 hr = tb->_Init(tray, pTaskBand);
717 if (FAILED_UNEXPECTEDLY(hr))
718 {
719 tb->Release();
720 return hr;
721 }
722
723 *pBandSite = tb;
724
725 return S_OK;
726 }