4 * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
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.
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.
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
23 #include <shdeprecated.h>
25 /*****************************************************************************
26 ** ITrayBandSite ************************************************************
27 *****************************************************************************/
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.
34 public IBandSiteStreamCallback
35 /* TODO: IWinEventHandler */
37 volatile LONG m_RefCount
;
39 CComPtr
<ITrayWindow
> m_Tray
;
41 CComPtr
<IUnknown
> m_Inner
;
42 CComPtr
<IBandSite
> m_BandSite
;
43 CComPtr
<ITaskBand
> m_TaskBand
;
44 CComPtr
<IWinEventHandler
> m_WindowEventHandler
;
45 CComPtr
<IContextMenu
> m_ContextMenu
;
60 virtual ULONG STDMETHODCALLTYPE
AddRef()
62 return InterlockedIncrement(&m_RefCount
);
65 virtual ULONG STDMETHODCALLTYPE
Release()
67 ULONG Ret
= InterlockedDecrement(&m_RefCount
);
75 virtual HRESULT STDMETHODCALLTYPE
QueryInterface(IN REFIID riid
, OUT LPVOID
*ppvObj
)
80 if (IsEqualIID(riid
, IID_IUnknown
) || IsEqualIID(riid
, IID_IBandSiteHelper
))
82 // return IBandSiteStreamCallback's IUnknown
83 *ppvObj
= static_cast<IBandSiteStreamCallback
*>(this);
85 else if (IsEqualIID(riid
, IID_IBandSite
))
87 *ppvObj
= static_cast<IBandSite
*>(this);
89 else if (IsEqualIID(riid
, IID_IWinEventHandler
))
91 TRACE("ITaskBandSite: IWinEventHandler queried!\n");
95 else if (m_Inner
!= NULL
)
97 return m_Inner
->QueryInterface(riid
, ppvObj
);
102 return E_NOINTERFACE
;
116 virtual ~CTrayBandSite() { }
118 virtual HRESULT STDMETHODCALLTYPE
OnLoad(
119 IN OUT IStream
*pStm
,
123 LARGE_INTEGER liPosZero
;
124 ULARGE_INTEGER liCurrent
;
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.
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. */
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
);
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
);
148 if (SUCCEEDED(hRet
) && ulRead
== sizeof(clsid
))
150 if (IsEqualGUID(clsid
, CLSID_ITaskBand
))
152 ASSERT(m_TaskBand
!= NULL
);
153 /* We're trying to load the task band! Let's create it... */
155 hRet
= m_TaskBand
->QueryInterface(
160 /* Load the stream */
161 TRACE("IBandSiteStreamCallback::OnLoad intercepted the task band CLSID!\n");
169 /* Reset the position and let the shell do all the work for us */
171 *(LARGE_INTEGER
*) &liCurrent
,
176 /* Let the shell handle everything else for us :) */
177 hRet
= OleLoadFromStream(pStm
,
182 if (!SUCCEEDED(hRet
))
184 TRACE("IBandSiteStreamCallback::OnLoad(0x%p, 0x%p, 0x%p) returns 0x%x\n", pStm
, riid
, pvObj
, hRet
);
190 virtual HRESULT STDMETHODCALLTYPE
OnSave(
191 IN OUT IUnknown
*pUnk
,
192 IN OUT IStream
*pStm
)
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
);
202 virtual HRESULT STDMETHODCALLTYPE
IsTaskBand(IN IUnknown
*punk
)
204 return IsSameObject(m_BandSite
, punk
);
207 virtual HRESULT STDMETHODCALLTYPE
ProcessMessage(
212 OUT LRESULT
*plResult
)
216 ASSERT(m_Rebar
!= NULL
);
218 /* Custom task band behavior */
223 const NMHDR
*nmh
= (const NMHDR
*) lParam
;
225 if (nmh
->hwndFrom
== m_Rebar
)
231 LPNMMOUSE nmm
= (LPNMMOUSE
) lParam
;
233 if (nmm
->dwHitInfo
== RBHT_CLIENT
|| nmm
->dwHitInfo
== RBHT_NOWHERE
||
234 nmm
->dwItemSpec
== (DWORD_PTR
) -1)
236 /* Make the rebar control appear transparent so the user
237 can drag the tray window */
238 *plResult
= HTTRANSPARENT
;
244 /* Deny if an Administrator disabled this "feature" */
245 *plResult
= (SHRestricted(REST_NOMOVINGBAND
) != 0);
250 //TRACE("ITrayBandSite::ProcessMessage: WM_NOTIFY for 0x%p, From: 0x%p, Code: NM_FIRST-%u...\n", hWnd, nmh->hwndFrom, NM_FIRST - nmh->code);
255 /* Forward to the shell's IWinEventHandler interface to get the default shell behavior! */
256 if (!m_WindowEventHandler
)
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
);
265 if (uMsg
== WM_NOTIFY
)
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
);
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
);
280 virtual HRESULT STDMETHODCALLTYPE
AddContextMenus(
286 OUT IContextMenu
**ppcm
)
288 if (m_ContextMenu
== NULL
)
291 CComPtr
<IShellService
> pSs
;
293 /* Cache the context menu so we don't need to CoCreateInstance all the time... */
294 hRet
= CoCreateInstance(CLSID_BandSiteMenu
, NULL
, CLSCTX_INPROC_SERVER
, IID_PPV_ARG(IShellService
, &pSs
));
295 TRACE("CoCreateInstance(CLSID_BandSiteMenu) for IShellService returned: 0x%x\n", hRet
);
296 if (!SUCCEEDED(hRet
))
299 hRet
= pSs
->SetOwner((IBandSite
*)this);
300 if (!SUCCEEDED(hRet
))
305 hRet
= pSs
->QueryInterface(IID_PPV_ARG(IContextMenu
, &m_ContextMenu
));
307 if (!SUCCEEDED(hRet
))
313 m_ContextMenu
->AddRef();
314 *ppcm
= m_ContextMenu
;
317 /* Add the menu items */
318 return m_ContextMenu
->QueryContextMenu(hmenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
321 virtual HRESULT STDMETHODCALLTYPE
Lock(IN BOOL bLock
)
323 BOOL bPrevLocked
= Locked
;
327 ASSERT(m_BandSite
!= NULL
);
329 if (bPrevLocked
!= bLock
)
333 bsi
.dwMask
= BSIM_STYLE
;
334 bsi
.dwStyle
= (Locked
? BSIS_LOCKED
| BSIS_NOGRIPPER
: BSIS_AUTOGRIPPER
);
336 hRet
= m_BandSite
->SetBandSiteInfo(&bsi
);
338 /* HACK for CORE-9809 ! */
339 if (hRet
== E_NOTIMPL
)
342 ERR("HACK for CORE-9809 no longer needed!\n");
355 /*******************************************************************/
357 virtual HRESULT STDMETHODCALLTYPE
AddBand(IN IUnknown
*punk
)
359 /* Send the DBID_DELAYINIT command to initialize the band to be added */
360 /* FIXME: Should be delayed */
361 IUnknown_Exec(punk
, IID_IDeskBand
, DBID_DELAYINIT
, 0, NULL
, NULL
);
363 return m_BandSite
->AddBand(punk
);
366 virtual HRESULT STDMETHODCALLTYPE
EnumBands(
368 OUT DWORD
*pdwBandID
)
370 return m_BandSite
->EnumBands(uBand
, pdwBandID
);
373 virtual HRESULT STDMETHODCALLTYPE
QueryBand(
375 OUT IDeskBand
**ppstb
,
381 IDeskBand
*pstb
= NULL
;
383 hRet
= m_BandSite
->QueryBand(
392 hRet
= IsSameObject(pstb
, m_TaskBand
);
395 /* Add the BSSF_UNDELETEABLE flag to pdwState because the task bar band shouldn't be deletable */
396 if (pdwState
!= NULL
)
397 *pdwState
|= BSSF_UNDELETEABLE
;
399 else if (!SUCCEEDED(hRet
))
408 else if (ppstb
!= NULL
)
414 virtual HRESULT STDMETHODCALLTYPE
SetBandState(
419 return m_BandSite
->SetBandState(dwBandID
, dwMask
, dwState
);
422 virtual HRESULT STDMETHODCALLTYPE
RemoveBand(
425 return m_BandSite
->RemoveBand(dwBandID
);
428 virtual HRESULT STDMETHODCALLTYPE
GetBandObject(
433 return m_BandSite
->GetBandObject(dwBandID
, riid
, ppv
);
436 virtual HRESULT STDMETHODCALLTYPE
SetBandSiteInfo(
437 IN
const BANDSITEINFO
*pbsinfo
)
439 return m_BandSite
->SetBandSiteInfo(pbsinfo
);
442 virtual HRESULT STDMETHODCALLTYPE
GetBandSiteInfo(
443 IN OUT BANDSITEINFO
*pbsinfo
)
445 return m_BandSite
->GetBandSiteInfo(pbsinfo
);
448 virtual BOOL
HasTaskBand()
450 ASSERT(m_TaskBand
!= NULL
);
452 return SUCCEEDED(m_TaskBand
->GetRebarBandID(NULL
));
455 virtual HRESULT
AddTaskBand()
458 /* FIXME: This is the code for the simple taskbar */
459 IObjectWithSite
*pOws
;
462 hRet
= TaskBand
->QueryInterface(
463 &IID_IObjectWithSite
,
467 hRet
= pOws
->SetSite(
468 (IUnknown
*)TaskBand
);
477 return m_BandSite
->AddBand(m_TaskBand
);
484 virtual HRESULT
Update()
486 return IUnknown_Exec(m_Inner
,
488 DBID_BANDINFOCHANGED
,
494 virtual VOID
BroadcastOleCommandExec(REFGUID pguidCmdGroup
,
500 IOleCommandTarget
*pOct
;
504 /* Enumerate all bands */
505 while (SUCCEEDED(m_BandSite
->EnumBands(uBand
, &dwBandID
)))
507 if (SUCCEEDED(m_BandSite
->GetBandObject(dwBandID
, IID_PPV_ARG(IOleCommandTarget
, &pOct
))))
509 /* Execute the command */
524 virtual HRESULT
FinishInit()
526 /* Broadcast the DBID_FINISHINIT command */
527 BroadcastOleCommandExec(IID_IDeskBand
, DBID_FINISHINIT
, 0, NULL
, NULL
);
532 virtual HRESULT
Show(IN BOOL bShow
)
534 CComPtr
<IDeskBarClient
> pDbc
;
537 hRet
= m_BandSite
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &pDbc
));
540 hRet
= pDbc
->UIActivateDBC(bShow
? DBC_SHOW
: DBC_HIDE
);
546 virtual HRESULT
LoadFromStream(IN OUT IStream
*pStm
)
548 CComPtr
<IPersistStream
> pPStm
;
551 ASSERT(m_BandSite
!= NULL
);
553 /* We implement the undocumented COM interface IBandSiteStreamCallback
554 that the shell will query so that we can intercept and custom-load
555 the task band when it finds the task band's CLSID (which is internal).
556 This way we can prevent the shell from attempting to CoCreateInstance
557 the (internal) task band, resulting in a failure... */
558 hRet
= m_BandSite
->QueryInterface(IID_PPV_ARG(IPersistStream
, &pPStm
));
561 hRet
= pPStm
->Load(pStm
);
562 TRACE("->Load() returned 0x%x\n", hRet
);
568 virtual IStream
* GetUserBandsStream(IN DWORD grfMode
)
571 IStream
*Stream
= NULL
;
573 if (RegCreateKeyW(hkExplorer
,
575 &hkStreams
) == ERROR_SUCCESS
)
577 Stream
= SHOpenRegStreamW(hkStreams
,
582 RegCloseKey(hkStreams
);
588 virtual IStream
* GetDefaultBandsStream(IN DWORD grfMode
)
591 IStream
*Stream
= NULL
;
593 if (RegCreateKeyW(HKEY_LOCAL_MACHINE
,
594 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Streams",
595 &hkStreams
) == ERROR_SUCCESS
)
597 Stream
= SHOpenRegStreamW(hkStreams
,
602 RegCloseKey(hkStreams
);
608 virtual HRESULT
Load()
613 /* Try to load the user's settings */
614 pStm
= GetUserBandsStream(STGM_READ
);
617 hRet
= LoadFromStream(pStm
);
619 TRACE("Loaded user bands settings: 0x%x\n", hRet
);
625 /* If the user's settings couldn't be loaded, try with
626 default settings (ie. when the user logs in for the
628 if (!SUCCEEDED(hRet
))
630 pStm
= GetDefaultBandsStream(STGM_READ
);
633 hRet
= LoadFromStream(pStm
);
635 TRACE("Loaded default user bands settings: 0x%x\n", hRet
);
645 HRESULT
_Init(IN OUT ITrayWindow
*tray
, OUT HWND
*phWndRebar
, OUT HWND
*phwndTaskSwitch
)
647 CComPtr
<IDeskBarClient
> pDbc
;
648 CComPtr
<IDeskBand
> pDb
;
649 CComPtr
<IOleWindow
> pOw
;
653 *phwndTaskSwitch
= NULL
;
657 /* Create a RebarBandSite provided by the shell */
658 hRet
= CoCreateInstance(CLSID_RebarBandSite
,
659 static_cast<IBandSite
*>(this),
660 CLSCTX_INPROC_SERVER
,
661 IID_PPV_ARG(IUnknown
, &m_Inner
));
662 if (!SUCCEEDED(hRet
))
667 hRet
= m_Inner
->QueryInterface(IID_PPV_ARG(IBandSite
, &m_BandSite
));
668 if (!SUCCEEDED(hRet
))
673 hRet
= m_Inner
->QueryInterface(IID_PPV_ARG(IWinEventHandler
, &m_WindowEventHandler
));
674 if (!SUCCEEDED(hRet
))
679 m_TaskBand
= CreateTaskBand(m_Tray
);
680 if (m_TaskBand
!= NULL
)
682 /* Add the task band to the site */
683 hRet
= m_BandSite
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &pDbc
));
686 hRet
= m_TaskBand
->QueryInterface(IID_PPV_ARG(IOleWindow
, &pOw
));
689 /* We cause IDeskBarClient to create the rebar control by passing the new
690 task band to it. The band reports the tray window handle as window handle
691 so that IDeskBarClient knows the parent window of the Rebar control that
692 it wants to create. */
693 hRet
= pDbc
->SetDeskBarSite(pOw
);
697 /* The Rebar control is now created, we can query the window handle */
698 hRet
= pDbc
->GetWindow(&m_Rebar
);
702 /* We need to manually remove the RBS_BANDBORDERS style! */
703 SetWindowStyle(m_Rebar
, RBS_BANDBORDERS
, 0);
712 /* Set the Desk Bar mode to the current one */
714 /* FIXME: We need to set the mode (and update) whenever the user docks
715 the tray window to another monitor edge! */
717 if (!m_Tray
->IsHorizontal())
718 dwMode
= DBIF_VIEWMODE_VERTICAL
;
720 hRet
= pDbc
->SetModeDBC(dwMode
);
726 /* Load the saved state of the task band site */
727 /* FIXME: We should delay loading shell extensions, also see DBID_DELAYINIT */
730 /* Add the task bar band if it hasn't been added already */
731 hRet
= AddTaskBand();
734 hRet
= m_TaskBand
->QueryInterface(IID_PPV_ARG(IDeskBand
, &pDb
));
737 hRet
= pDb
->GetWindow(phwndTaskSwitch
);
738 if (!SUCCEEDED(hRet
))
739 *phwndTaskSwitch
= NULL
;
743 /* Should we send this after showing it? */
746 /* FIXME: When should we send this? Does anyone care anyway? */
749 /* Activate the band site */
754 *phWndRebar
= m_Rebar
;
759 /*******************************************************************/
762 CreateTrayBandSite(IN OUT ITrayWindow
*Tray
,
763 OUT HWND
*phWndRebar
,
764 OUT HWND
*phWndTaskSwitch
)
768 CTrayBandSite
* tb
= new CTrayBandSite();
775 hr
= tb
->_Init(Tray
, phWndRebar
, phWndTaskSwitch
);
776 if (FAILED_UNEXPECTEDLY(hr
))