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
,
142 hRet
= pStm
->Seek(liPosZero
, STREAM_SEEK_CUR
, &liCurrent
);
146 /* Now let's read the CLSID from the stream and see if it's our task band */
147 hRet
= pStm
->Read(&clsid
, (ULONG
)sizeof(clsid
), &ulRead
);
149 if (SUCCEEDED(hRet
) && ulRead
== sizeof(clsid
))
151 if (IsEqualGUID(clsid
, CLSID_ITaskBand
))
153 ASSERT(m_TaskBand
!= NULL
);
154 /* We're trying to load the task band! Let's create it... */
156 hRet
= m_TaskBand
->QueryInterface(
161 /* Load the stream */
162 TRACE("IBandSiteStreamCallback::OnLoad intercepted the task band CLSID!\n");
170 /* Reset the position and let the shell do all the work for us */
172 *(LARGE_INTEGER
*) &liCurrent
,
177 /* Let the shell handle everything else for us :) */
178 hRet
= OleLoadFromStream(pStm
,
183 if (!SUCCEEDED(hRet
))
185 TRACE("IBandSiteStreamCallback::OnLoad(0x%p, 0x%p, 0x%p) returns 0x%x\n", pStm
, riid
, pvObj
, hRet
);
191 virtual HRESULT STDMETHODCALLTYPE
OnSave(
192 IN OUT IUnknown
*pUnk
,
193 IN OUT IStream
*pStm
)
195 /* NOTE: Callback routine called by the shell while saving the task band
196 stream. We use it to intercept the default behavior when the task
197 band is saved to the stream */
198 /* FIXME: Implement */
199 TRACE("IBandSiteStreamCallback::OnSave(0x%p, 0x%p) returns E_NOTIMPL\n", pUnk
, pStm
);
203 virtual HRESULT STDMETHODCALLTYPE
IsTaskBand(IN IUnknown
*punk
)
205 return IsSameObject(m_BandSite
, punk
);
208 virtual HRESULT STDMETHODCALLTYPE
ProcessMessage(
213 OUT LRESULT
*plResult
)
217 ASSERT(m_Rebar
!= NULL
);
219 /* Custom task band behavior */
224 const NMHDR
*nmh
= (const NMHDR
*) lParam
;
226 if (nmh
->hwndFrom
== m_Rebar
)
232 LPNMMOUSE nmm
= (LPNMMOUSE
) lParam
;
234 if (nmm
->dwHitInfo
== RBHT_CLIENT
|| nmm
->dwHitInfo
== RBHT_NOWHERE
||
235 nmm
->dwItemSpec
== (DWORD_PTR
) -1)
237 /* Make the rebar control appear transparent so the user
238 can drag the tray window */
239 *plResult
= HTTRANSPARENT
;
245 /* Deny if an Administrator disabled this "feature" */
246 *plResult
= (SHRestricted(REST_NOMOVINGBAND
) != 0);
251 //TRACE("ITrayBandSite::ProcessMessage: WM_NOTIFY for 0x%p, From: 0x%p, Code: NM_FIRST-%u...\n", hWnd, nmh->hwndFrom, NM_FIRST - nmh->code);
256 /* Forward to the shell's IWinEventHandler interface to get the default shell behavior! */
257 if (!m_WindowEventHandler
)
260 /*TRACE("Calling IWinEventHandler::ProcessMessage(0x%p, 0x%x, 0x%p, 0x%p, 0x%p) hWndRebar=0x%p\n", hWnd, uMsg, wParam, lParam, plResult, hWndRebar);*/
261 hRet
= m_WindowEventHandler
->OnWinEvent(hWnd
, uMsg
, wParam
, lParam
, plResult
);
266 if (uMsg
== WM_NOTIFY
)
268 const NMHDR
*nmh
= (const NMHDR
*) lParam
;
269 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
);
273 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
);
281 virtual HRESULT STDMETHODCALLTYPE
AddContextMenus(
287 OUT IContextMenu
**ppcm
)
289 if (m_ContextMenu
== NULL
)
292 CComPtr
<IShellService
> pSs
;
294 /* Cache the context menu so we don't need to CoCreateInstance all the time... */
295 hRet
= CoCreateInstance(CLSID_BandSiteMenu
, NULL
, CLSCTX_INPROC_SERVER
, IID_PPV_ARG(IShellService
, &pSs
));
296 TRACE("CoCreateInstance(CLSID_BandSiteMenu) for IShellService returned: 0x%x\n", hRet
);
297 if (!SUCCEEDED(hRet
))
300 hRet
= pSs
->SetOwner((IBandSite
*)this);
301 if (!SUCCEEDED(hRet
))
306 hRet
= pSs
->QueryInterface(IID_PPV_ARG(IContextMenu
, &m_ContextMenu
));
308 if (!SUCCEEDED(hRet
))
314 m_ContextMenu
->AddRef();
315 *ppcm
= m_ContextMenu
;
318 /* Add the menu items */
319 return m_ContextMenu
->QueryContextMenu(hmenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
322 virtual HRESULT STDMETHODCALLTYPE
Lock(IN BOOL bLock
)
324 BOOL bPrevLocked
= Locked
;
328 ASSERT(m_BandSite
!= NULL
);
330 if (bPrevLocked
!= bLock
)
334 bsi
.dwMask
= BSIM_STYLE
;
335 bsi
.dwStyle
= (Locked
? BSIS_LOCKED
| BSIS_NOGRIPPER
: BSIS_AUTOGRIPPER
);
337 hRet
= m_BandSite
->SetBandSiteInfo(&bsi
);
349 /*******************************************************************/
351 virtual HRESULT STDMETHODCALLTYPE
AddBand(IN IUnknown
*punk
)
353 /* Send the DBID_DELAYINIT command to initialize the band to be added */
354 /* FIXME: Should be delayed */
355 IUnknown_Exec(punk
, IID_IDeskBand
, DBID_DELAYINIT
, 0, NULL
, NULL
);
357 return m_BandSite
->AddBand(punk
);
360 virtual HRESULT STDMETHODCALLTYPE
EnumBands(
362 OUT DWORD
*pdwBandID
)
364 return m_BandSite
->EnumBands(uBand
, pdwBandID
);
367 virtual HRESULT STDMETHODCALLTYPE
QueryBand(
369 OUT IDeskBand
**ppstb
,
375 IDeskBand
*pstb
= NULL
;
377 hRet
= m_BandSite
->QueryBand(
386 hRet
= IsSameObject(pstb
, m_TaskBand
);
389 /* Add the BSSF_UNDELETEABLE flag to pdwState because the task bar band shouldn't be deletable */
390 if (pdwState
!= NULL
)
391 *pdwState
|= BSSF_UNDELETEABLE
;
393 else if (!SUCCEEDED(hRet
))
402 else if (ppstb
!= NULL
)
408 virtual HRESULT STDMETHODCALLTYPE
SetBandState(
413 return m_BandSite
->SetBandState(dwBandID
, dwMask
, dwState
);
416 virtual HRESULT STDMETHODCALLTYPE
RemoveBand(
419 return m_BandSite
->RemoveBand(dwBandID
);
422 virtual HRESULT STDMETHODCALLTYPE
GetBandObject(
427 return m_BandSite
->GetBandObject(dwBandID
, riid
, ppv
);
430 virtual HRESULT STDMETHODCALLTYPE
SetBandSiteInfo(
431 IN
const BANDSITEINFO
*pbsinfo
)
433 return m_BandSite
->SetBandSiteInfo(pbsinfo
);
436 virtual HRESULT STDMETHODCALLTYPE
GetBandSiteInfo(
437 IN OUT BANDSITEINFO
*pbsinfo
)
439 return m_BandSite
->GetBandSiteInfo(pbsinfo
);
442 virtual BOOL
HasTaskBand()
444 ASSERT(m_TaskBand
!= NULL
);
446 return SUCCEEDED(m_TaskBand
->GetRebarBandID(NULL
));
449 virtual HRESULT
AddTaskBand()
452 /* FIXME: This is the code for the simple taskbar */
453 IObjectWithSite
*pOws
;
456 hRet
= TaskBand
->QueryInterface(
457 &IID_IObjectWithSite
,
461 hRet
= pOws
->SetSite(
462 (IUnknown
*)TaskBand
);
471 return m_BandSite
->AddBand(m_TaskBand
);
478 virtual HRESULT
Update()
480 return IUnknown_Exec(m_Inner
,
482 DBID_BANDINFOCHANGED
,
488 virtual VOID
BroadcastOleCommandExec(REFGUID pguidCmdGroup
,
494 IOleCommandTarget
*pOct
;
498 /* Enumerate all bands */
499 while (SUCCEEDED(m_BandSite
->EnumBands(uBand
, &dwBandID
)))
501 if (SUCCEEDED(m_BandSite
->GetBandObject(dwBandID
, IID_PPV_ARG(IOleCommandTarget
, &pOct
))))
503 /* Execute the command */
518 virtual HRESULT
FinishInit()
520 /* Broadcast the DBID_FINISHINIT command */
521 BroadcastOleCommandExec(IID_IDeskBand
, DBID_FINISHINIT
, 0, NULL
, NULL
);
526 virtual HRESULT
Show(IN BOOL bShow
)
528 CComPtr
<IDeskBarClient
> pDbc
;
531 hRet
= m_BandSite
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &pDbc
));
534 hRet
= pDbc
->UIActivateDBC(bShow
? DBC_SHOW
: DBC_HIDE
);
540 virtual HRESULT
LoadFromStream(IN OUT IStream
*pStm
)
542 CComPtr
<IPersistStream
> pPStm
;
545 ASSERT(m_BandSite
!= NULL
);
547 /* We implement the undocumented COM interface IBandSiteStreamCallback
548 that the shell will query so that we can intercept and custom-load
549 the task band when it finds the task band's CLSID (which is internal).
550 This way we can prevent the shell from attempting to CoCreateInstance
551 the (internal) task band, resulting in a failure... */
552 hRet
= m_BandSite
->QueryInterface(IID_PPV_ARG(IPersistStream
, &pPStm
));
555 hRet
= pPStm
->Load(pStm
);
556 TRACE("->Load() returned 0x%x\n", hRet
);
562 virtual IStream
* GetUserBandsStream(IN DWORD grfMode
)
565 IStream
*Stream
= NULL
;
567 if (RegCreateKey(hkExplorer
,
569 &hkStreams
) == ERROR_SUCCESS
)
571 Stream
= SHOpenRegStream(hkStreams
,
573 TEXT("TaskbarWinXP"),
576 RegCloseKey(hkStreams
);
582 virtual IStream
* GetDefaultBandsStream(IN DWORD grfMode
)
585 IStream
*Stream
= NULL
;
587 if (RegCreateKey(HKEY_LOCAL_MACHINE
,
588 TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Streams"),
589 &hkStreams
) == ERROR_SUCCESS
)
591 Stream
= SHOpenRegStream(hkStreams
,
593 TEXT("Default Taskbar"),
596 RegCloseKey(hkStreams
);
602 virtual HRESULT
Load()
607 /* Try to load the user's settings */
608 pStm
= GetUserBandsStream(STGM_READ
);
611 hRet
= LoadFromStream(pStm
);
613 TRACE("Loaded user bands settings: 0x%x\n", hRet
);
619 /* If the user's settings couldn't be loaded, try with
620 default settings (ie. when the user logs in for the
622 if (!SUCCEEDED(hRet
))
624 pStm
= GetDefaultBandsStream(STGM_READ
);
627 hRet
= LoadFromStream(pStm
);
629 TRACE("Loaded default user bands settings: 0x%x\n", hRet
);
639 HRESULT
_Init(IN OUT ITrayWindow
*tray
, OUT HWND
*phWndRebar
, OUT HWND
*phwndTaskSwitch
)
641 CComPtr
<IDeskBarClient
> pDbc
;
642 CComPtr
<IDeskBand
> pDb
;
643 CComPtr
<IOleWindow
> pOw
;
647 *phwndTaskSwitch
= NULL
;
651 /* Create a RebarBandSite provided by the shell */
652 hRet
= CoCreateInstance(CLSID_RebarBandSite
,
653 static_cast<IBandSite
*>(this),
654 CLSCTX_INPROC_SERVER
,
655 IID_PPV_ARG(IUnknown
, &m_Inner
));
656 if (!SUCCEEDED(hRet
))
661 hRet
= m_Inner
->QueryInterface(IID_PPV_ARG(IBandSite
, &m_BandSite
));
662 if (!SUCCEEDED(hRet
))
667 hRet
= m_Inner
->QueryInterface(IID_PPV_ARG(IWinEventHandler
, &m_WindowEventHandler
));
668 if (!SUCCEEDED(hRet
))
673 m_TaskBand
= CreateTaskBand(m_Tray
);
674 if (m_TaskBand
!= NULL
)
676 /* Add the task band to the site */
677 hRet
= m_BandSite
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &pDbc
));
680 hRet
= m_TaskBand
->QueryInterface(IID_PPV_ARG(IOleWindow
, &pOw
));
683 /* We cause IDeskBarClient to create the rebar control by passing the new
684 task band to it. The band reports the tray window handle as window handle
685 so that IDeskBarClient knows the parent window of the Rebar control that
686 it wants to create. */
687 hRet
= pDbc
->SetDeskBarSite(pOw
);
691 /* The Rebar control is now created, we can query the window handle */
692 hRet
= pDbc
->GetWindow(&m_Rebar
);
696 /* We need to manually remove the RBS_BANDBORDERS style! */
697 SetWindowStyle(m_Rebar
, RBS_BANDBORDERS
, 0);
706 /* Set the Desk Bar mode to the current one */
708 /* FIXME: We need to set the mode (and update) whenever the user docks
709 the tray window to another monitor edge! */
711 if (!m_Tray
->IsHorizontal())
712 dwMode
= DBIF_VIEWMODE_VERTICAL
;
714 hRet
= pDbc
->SetModeDBC(dwMode
);
720 /* Load the saved state of the task band site */
721 /* FIXME: We should delay loading shell extensions, also see DBID_DELAYINIT */
724 /* Add the task bar band if it hasn't been added already */
725 hRet
= AddTaskBand();
728 hRet
= m_TaskBand
->QueryInterface(IID_PPV_ARG(IDeskBand
, &pDb
));
731 hRet
= pDb
->GetWindow(phwndTaskSwitch
);
732 if (!SUCCEEDED(hRet
))
733 *phwndTaskSwitch
= NULL
;
737 /* Should we send this after showing it? */
740 /* FIXME: When should we send this? Does anyone care anyway? */
743 /* Activate the band site */
748 *phWndRebar
= m_Rebar
;
753 /*******************************************************************/
756 CreateTrayBandSite(IN OUT ITrayWindow
*Tray
,
757 OUT HWND
*phWndRebar
,
758 OUT HWND
*phWndTaskSwitch
)
762 CTrayBandSite
* tb
= new CTrayBandSite();
769 hr
= tb
->_Init(Tray
, phWndRebar
, phWndTaskSwitch
);
770 if (FAILED_UNEXPECTEDLY(hr
))