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>
27 /*****************************************************************************
28 ** ITrayBandSite ************************************************************
29 *****************************************************************************/
31 // 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
32 // it works just fine this way.
36 public IBandSiteStreamCallback
37 /* TODO: IWinEventHandler */
39 volatile LONG m_RefCount
;
41 CComPtr
<ITrayWindow
> Tray
;
43 CComPtr
<IUnknown
> punkInner
;
44 CComPtr
<IBandSite
> BandSite
;
45 CComPtr
<ITaskBand
> TaskBand
;
46 CComPtr
<IWinEventHandler
> WindowEventHandler
;
47 CComPtr
<IContextMenu
> ContextMenu
;
62 virtual ULONG STDMETHODCALLTYPE
AddRef()
64 return InterlockedIncrement(&m_RefCount
);
67 virtual ULONG STDMETHODCALLTYPE
Release()
69 ULONG Ret
= InterlockedDecrement(&m_RefCount
);
77 virtual HRESULT STDMETHODCALLTYPE
QueryInterface(IN REFIID riid
, OUT LPVOID
*ppvObj
)
82 if (IsEqualIID(riid
, IID_IUnknown
) || IsEqualIID(riid
, IID_IBandSiteStreamCallback
))
84 // return IBandSiteStreamCallback's IUnknown
85 *ppvObj
= static_cast<IBandSiteStreamCallback
*>(this);
87 else if (IsEqualIID(riid
, IID_IBandSite
))
89 *ppvObj
= static_cast<IBandSite
*>(this);
91 else if (IsEqualIID(riid
, IID_IWinEventHandler
))
93 TRACE("ITaskBandSite: IWinEventHandler queried!\n");
97 else if (punkInner
!= NULL
)
99 return punkInner
->QueryInterface(riid
, ppvObj
);
104 return E_NOINTERFACE
;
118 virtual ~CTrayBandSite() { }
120 virtual HRESULT STDMETHODCALLTYPE
OnLoad(
121 IN OUT IStream
*pStm
,
125 LARGE_INTEGER liPosZero
;
126 ULARGE_INTEGER liCurrent
;
131 /* NOTE: Callback routine called by the shell while loading the task band
132 stream. We use it to intercept the default behavior when the task
133 band is loaded from the stream.
135 NOTE: riid always points to IID_IUnknown! This is because the shell hasn't
136 read anything from the stream and therefore doesn't know what CLSID
137 it's dealing with. We'll have to find it out ourselves by reading
138 the GUID from the stream. */
140 /* Read the current position of the stream, we'll have to reset it everytime
141 we read a CLSID that's not the task band... */
142 ZeroMemory(&liPosZero
,
144 hRet
= pStm
->Seek(liPosZero
, STREAM_SEEK_CUR
, &liCurrent
);
148 /* Now let's read the CLSID from the stream and see if it's our task band */
149 hRet
= pStm
->Read(&clsid
, (ULONG
)sizeof(clsid
), &ulRead
);
151 if (SUCCEEDED(hRet
) && ulRead
== sizeof(clsid
))
153 if (IsEqualGUID(clsid
, CLSID_ITaskBand
))
155 ASSERT(TaskBand
!= NULL
);
156 /* We're trying to load the task band! Let's create it... */
158 hRet
= TaskBand
->QueryInterface(
163 /* Load the stream */
164 TRACE("IBandSiteStreamCallback::OnLoad intercepted the task band CLSID!\n");
172 /* Reset the position and let the shell do all the work for us */
174 *(LARGE_INTEGER
*) &liCurrent
,
179 /* Let the shell handle everything else for us :) */
180 hRet
= OleLoadFromStream(pStm
,
185 if (!SUCCEEDED(hRet
))
187 TRACE("IBandSiteStreamCallback::OnLoad(0x%p, 0x%p, 0x%p) returns 0x%x\n", pStm
, riid
, pvObj
, hRet
);
193 virtual HRESULT STDMETHODCALLTYPE
OnSave(
194 IN OUT IUnknown
*pUnk
,
195 IN OUT IStream
*pStm
)
197 /* NOTE: Callback routine called by the shell while saving the task band
198 stream. We use it to intercept the default behavior when the task
199 band is saved to the stream */
200 /* FIXME: Implement */
201 TRACE("IBandSiteStreamCallback::OnSave(0x%p, 0x%p) returns E_NOTIMPL\n", pUnk
, pStm
);
205 virtual HRESULT STDMETHODCALLTYPE
IsTaskBand(
208 return IsSameObject((IUnknown
*) BandSite
,
212 virtual HRESULT STDMETHODCALLTYPE
ProcessMessage(
217 OUT LRESULT
*plResult
)
221 ASSERT(hWndRebar
!= NULL
);
223 /* Custom task band behavior */
228 const NMHDR
*nmh
= (const NMHDR
*) lParam
;
230 if (nmh
->hwndFrom
== hWndRebar
)
236 LPNMMOUSE nmm
= (LPNMMOUSE
) lParam
;
238 if (nmm
->dwHitInfo
== RBHT_CLIENT
|| nmm
->dwHitInfo
== RBHT_NOWHERE
||
239 nmm
->dwItemSpec
== (DWORD_PTR
) -1)
241 /* Make the rebar control appear transparent so the user
242 can drag the tray window */
243 *plResult
= HTTRANSPARENT
;
249 /* Deny if an Administrator disabled this "feature" */
250 *plResult
= (SHRestricted(REST_NOMOVINGBAND
) != 0);
255 //TRACE("ITrayBandSite::ProcessMessage: WM_NOTIFY for 0x%p, From: 0x%p, Code: NM_FIRST-%u...\n", hWnd, nmh->hwndFrom, NM_FIRST - nmh->code);
260 /* Forward to the shell's IWinEventHandler interface to get the default shell behavior! */
261 if (!WindowEventHandler
)
264 /*TRACE("Calling IWinEventHandler::ProcessMessage(0x%p, 0x%x, 0x%p, 0x%p, 0x%p) hWndRebar=0x%p\n", hWnd, uMsg, wParam, lParam, plResult, hWndRebar);*/
265 hRet
= WindowEventHandler
->OnWinEvent(hWnd
, uMsg
, wParam
, lParam
, plResult
);
270 if (uMsg
== WM_NOTIFY
)
272 const NMHDR
*nmh
= (const NMHDR
*) lParam
;
273 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
);
277 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
);
285 virtual HRESULT STDMETHODCALLTYPE
AddContextMenus(
291 OUT IContextMenu
**ppcm
)
296 if (ContextMenu
== NULL
)
298 /* Cache the context menu so we don't need to CoCreateInstance all the time... */
299 hRet
= CoCreateInstance(CLSID_IShellBandSiteMenu
, NULL
, CLSCTX_INPROC_SERVER
, IID_PPV_ARG(IShellService
, &pSs
));
300 TRACE("CoCreateInstance(CLSID_IShellBandSiteMenu) for IShellService returned: 0x%x\n", hRet
);
301 if (!SUCCEEDED(hRet
))
304 hRet
= pSs
->SetOwner((IBandSite
*)this);
305 if (!SUCCEEDED(hRet
))
311 hRet
= pSs
->QueryInterface(IID_PPV_ARG(IContextMenu
, &ContextMenu
));
315 if (!SUCCEEDED(hRet
))
321 ContextMenu
->AddRef();
325 /* Add the menu items */
326 return ContextMenu
->QueryContextMenu(
334 virtual HRESULT STDMETHODCALLTYPE
Lock(
337 BOOL bPrevLocked
= Locked
;
341 ASSERT(BandSite
!= NULL
);
343 if (bPrevLocked
!= bLock
)
347 bsi
.dwMask
= BSIM_STYLE
;
348 bsi
.dwStyle
= (Locked
? BSIS_LOCKED
| BSIS_NOGRIPPER
: BSIS_AUTOGRIPPER
);
350 hRet
= BandSite
->SetBandSiteInfo(&bsi
);
362 /*******************************************************************/
364 virtual HRESULT STDMETHODCALLTYPE
AddBand(
367 IOleCommandTarget
*pOct
;
370 hRet
= punk
->QueryInterface(IID_PPV_ARG(IOleCommandTarget
, &pOct
));
373 /* Send the DBID_DELAYINIT command to initialize the band to be added */
374 /* FIXME: Should be delayed */
385 return BandSite
->AddBand(
389 virtual HRESULT STDMETHODCALLTYPE
EnumBands(
391 OUT DWORD
*pdwBandID
)
393 return BandSite
->EnumBands(
398 virtual HRESULT STDMETHODCALLTYPE
QueryBand(
400 OUT IDeskBand
**ppstb
,
406 IDeskBand
*pstb
= NULL
;
408 hRet
= BandSite
->QueryBand(
417 hRet
= IsSameObject(pstb
, TaskBand
);
420 /* Add the BSSF_UNDELETEABLE flag to pdwState because the task bar band shouldn't be deletable */
421 if (pdwState
!= NULL
)
422 *pdwState
|= BSSF_UNDELETEABLE
;
424 else if (!SUCCEEDED(hRet
))
433 else if (ppstb
!= NULL
)
439 virtual HRESULT STDMETHODCALLTYPE
SetBandState(
444 return BandSite
->SetBandState(dwBandID
, dwMask
, dwState
);
447 virtual HRESULT STDMETHODCALLTYPE
RemoveBand(
450 return BandSite
->RemoveBand(dwBandID
);
453 virtual HRESULT STDMETHODCALLTYPE
GetBandObject(
458 return BandSite
->GetBandObject(dwBandID
, riid
, ppv
);
461 virtual HRESULT STDMETHODCALLTYPE
SetBandSiteInfo(
462 IN
const BANDSITEINFO
*pbsinfo
)
464 return BandSite
->SetBandSiteInfo(pbsinfo
);
467 virtual HRESULT STDMETHODCALLTYPE
GetBandSiteInfo(
468 IN OUT BANDSITEINFO
*pbsinfo
)
470 return BandSite
->GetBandSiteInfo(pbsinfo
);
473 virtual BOOL
HasTaskBand()
475 ASSERT(TaskBand
!= NULL
);
477 return SUCCEEDED(TaskBand
->GetRebarBandID(
481 virtual HRESULT
AddTaskBand()
484 /* FIXME: This is the code for the simple taskbar */
485 IObjectWithSite
*pOws
;
488 hRet
= TaskBand
->QueryInterface(
489 &IID_IObjectWithSite
,
493 hRet
= pOws
->SetSite(
494 (IUnknown
*)TaskBand
);
503 return BandSite
->AddBand(TaskBand
);
510 virtual HRESULT
Update()
512 IOleCommandTarget
*pOct
;
515 hRet
= punkInner
->QueryInterface(IID_PPV_ARG(IOleCommandTarget
, &pOct
));
518 /* Send the DBID_BANDINFOCHANGED command to update the band site */
521 DBID_BANDINFOCHANGED
,
532 virtual VOID
BroadcastOleCommandExec(const GUID
*pguidCmdGroup
,
538 IOleCommandTarget
*pOct
;
542 /* Enumerate all bands */
543 while (SUCCEEDED(BandSite
->EnumBands(uBand
, &dwBandID
)))
545 if (SUCCEEDED(BandSite
->GetBandObject(dwBandID
, IID_PPV_ARG(IOleCommandTarget
, &pOct
))))
547 /* Execute the command */
562 virtual HRESULT
FinishInit()
564 /* Broadcast the DBID_FINISHINIT command */
565 BroadcastOleCommandExec(&IID_IDeskBand
, DBID_FINISHINIT
, 0, NULL
, NULL
);
570 virtual HRESULT
Show(
573 IDeskBarClient
*pDbc
;
576 hRet
= BandSite
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &pDbc
));
579 hRet
= pDbc
->UIActivateDBC(
580 bShow
? DBC_SHOW
: DBC_HIDE
);
587 virtual HRESULT
LoadFromStream(IN OUT IStream
*pStm
)
589 IPersistStream
*pPStm
;
592 ASSERT(BandSite
!= NULL
);
594 /* We implement the undocumented COM interface IBandSiteStreamCallback
595 that the shell will query so that we can intercept and custom-load
596 the task band when it finds the task band's CLSID (which is internal).
597 This way we can prevent the shell from attempting to CoCreateInstance
598 the (internal) task band, resulting in a failure... */
599 hRet
= BandSite
->QueryInterface(IID_PPV_ARG(IPersistStream
, &pPStm
));
604 TRACE("->Load() returned 0x%x\n", hRet
);
612 GetUserBandsStream(IN DWORD grfMode
)
615 IStream
*Stream
= NULL
;
617 if (RegCreateKey(hkExplorer
,
619 &hkStreams
) == ERROR_SUCCESS
)
621 Stream
= SHOpenRegStream(hkStreams
,
623 TEXT("TaskbarWinXP"),
626 RegCloseKey(hkStreams
);
633 GetDefaultBandsStream(IN DWORD grfMode
)
636 IStream
*Stream
= NULL
;
638 if (RegCreateKey(HKEY_LOCAL_MACHINE
,
639 TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Streams"),
640 &hkStreams
) == ERROR_SUCCESS
)
642 Stream
= SHOpenRegStream(hkStreams
,
644 TEXT("Default Taskbar"),
647 RegCloseKey(hkStreams
);
653 virtual HRESULT
Load()
658 /* Try to load the user's settings */
659 pStm
= GetUserBandsStream(STGM_READ
);
662 hRet
= LoadFromStream(pStm
);
664 TRACE("Loaded user bands settings: 0x%x\n", hRet
);
670 /* If the user's settings couldn't be loaded, try with
671 default settings (ie. when the user logs in for the
673 if (!SUCCEEDED(hRet
))
675 pStm
= GetDefaultBandsStream(STGM_READ
);
678 hRet
= LoadFromStream(pStm
);
680 TRACE("Loaded default user bands settings: 0x%x\n", hRet
);
690 HRESULT
_Init(IN OUT ITrayWindow
*tray
, OUT HWND
*phWndRebar
, OUT HWND
*phwndTaskSwitch
)
692 IDeskBarClient
*pDbc
;
698 *phwndTaskSwitch
= NULL
;
702 /* Create a RebarBandSite provided by the shell */
703 hRet
= CoCreateInstance(CLSID_RebarBandSite
,
704 static_cast<IBandSite
*>(this),
705 CLSCTX_INPROC_SERVER
,
706 IID_PPV_ARG(IUnknown
, &punkInner
));
707 if (!SUCCEEDED(hRet
))
712 hRet
= punkInner
->QueryInterface(IID_PPV_ARG(IBandSite
, &BandSite
));
713 if (!SUCCEEDED(hRet
))
718 hRet
= punkInner
->QueryInterface(IID_PPV_ARG(IWinEventHandler
, &WindowEventHandler
));
719 if (!SUCCEEDED(hRet
))
724 TaskBand
= CreateTaskBand(Tray
);
725 if (TaskBand
!= NULL
)
727 /* Add the task band to the site */
728 hRet
= BandSite
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &pDbc
));
731 hRet
= TaskBand
->QueryInterface(IID_PPV_ARG(IOleWindow
, &pOw
));
734 /* We cause IDeskBarClient to create the rebar control by passing the new
735 task band to it. The band reports the tray window handle as window handle
736 so that IDeskBarClient knows the parent window of the Rebar control that
737 it wants to create. */
738 hRet
= pDbc
->SetDeskBarSite(pOw
);
742 /* The Rebar control is now created, we can query the window handle */
743 hRet
= pDbc
->GetWindow(&hWndRebar
);
747 /* We need to manually remove the RBS_BANDBORDERS style! */
748 SetWindowStyle(hWndRebar
, RBS_BANDBORDERS
, 0);
759 /* Set the Desk Bar mode to the current one */
761 /* FIXME: We need to set the mode (and update) whenever the user docks
762 the tray window to another monitor edge! */
764 if (!Tray
->IsHorizontal())
765 dwMode
= DBIF_VIEWMODE_VERTICAL
;
767 hRet
= pDbc
->SetModeDBC(dwMode
);
773 /* Load the saved state of the task band site */
774 /* FIXME: We should delay loading shell extensions, also see DBID_DELAYINIT */
777 /* Add the task bar band if it hasn't been added already */
778 hRet
= AddTaskBand();
781 hRet
= TaskBand
->QueryInterface(IID_PPV_ARG(IDeskBand
, &pDb
));
784 hRet
= pDb
->GetWindow(phwndTaskSwitch
);
785 if (!SUCCEEDED(hRet
))
786 *phwndTaskSwitch
= NULL
;
792 /* Should we send this after showing it? */
795 /* FIXME: When should we send this? Does anyone care anyway? */
798 /* Activate the band site */
803 *phWndRebar
= hWndRebar
;
808 /*******************************************************************/
811 CreateTrayBandSite(IN OUT ITrayWindow
*Tray
,
812 OUT HWND
*phWndRebar
,
813 OUT HWND
*phWndTaskSwitch
)
817 CTrayBandSite
* tb
= new CTrayBandSite();
824 hr
= tb
->_Init(Tray
, phWndRebar
, phWndTaskSwitch
);
825 if (FAILED_UNEXPECTEDLY(hr
))