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
> Tray
;
41 CComPtr
<IUnknown
> punkInner
;
42 CComPtr
<IBandSite
> BandSite
;
43 CComPtr
<ITaskBand
> TaskBand
;
44 CComPtr
<IWinEventHandler
> WindowEventHandler
;
45 CComPtr
<IContextMenu
> 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 (punkInner
!= NULL
)
97 return punkInner
->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(TaskBand
!= NULL
);
154 /* We're trying to load the task band! Let's create it... */
156 hRet
= 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(
206 return IsSameObject((IUnknown
*) BandSite
,
210 virtual HRESULT STDMETHODCALLTYPE
ProcessMessage(
215 OUT LRESULT
*plResult
)
219 ASSERT(hWndRebar
!= NULL
);
221 /* Custom task band behavior */
226 const NMHDR
*nmh
= (const NMHDR
*) lParam
;
228 if (nmh
->hwndFrom
== hWndRebar
)
234 LPNMMOUSE nmm
= (LPNMMOUSE
) lParam
;
236 if (nmm
->dwHitInfo
== RBHT_CLIENT
|| nmm
->dwHitInfo
== RBHT_NOWHERE
||
237 nmm
->dwItemSpec
== (DWORD_PTR
) -1)
239 /* Make the rebar control appear transparent so the user
240 can drag the tray window */
241 *plResult
= HTTRANSPARENT
;
247 /* Deny if an Administrator disabled this "feature" */
248 *plResult
= (SHRestricted(REST_NOMOVINGBAND
) != 0);
253 //TRACE("ITrayBandSite::ProcessMessage: WM_NOTIFY for 0x%p, From: 0x%p, Code: NM_FIRST-%u...\n", hWnd, nmh->hwndFrom, NM_FIRST - nmh->code);
258 /* Forward to the shell's IWinEventHandler interface to get the default shell behavior! */
259 if (!WindowEventHandler
)
262 /*TRACE("Calling IWinEventHandler::ProcessMessage(0x%p, 0x%x, 0x%p, 0x%p, 0x%p) hWndRebar=0x%p\n", hWnd, uMsg, wParam, lParam, plResult, hWndRebar);*/
263 hRet
= WindowEventHandler
->OnWinEvent(hWnd
, uMsg
, wParam
, lParam
, plResult
);
268 if (uMsg
== WM_NOTIFY
)
270 const NMHDR
*nmh
= (const NMHDR
*) lParam
;
271 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
);
275 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
);
283 virtual HRESULT STDMETHODCALLTYPE
AddContextMenus(
289 OUT IContextMenu
**ppcm
)
291 if (ContextMenu
== NULL
)
294 CComPtr
<IShellService
> pSs
;
296 /* Cache the context menu so we don't need to CoCreateInstance all the time... */
297 hRet
= CoCreateInstance(CLSID_BandSiteMenu
, NULL
, CLSCTX_INPROC_SERVER
, IID_PPV_ARG(IShellService
, &pSs
));
298 TRACE("CoCreateInstance(CLSID_BandSiteMenu) for IShellService returned: 0x%x\n", hRet
);
299 if (!SUCCEEDED(hRet
))
302 hRet
= pSs
->SetOwner((IBandSite
*)this);
303 if (!SUCCEEDED(hRet
))
308 hRet
= pSs
->QueryInterface(IID_PPV_ARG(IContextMenu
, &ContextMenu
));
310 if (!SUCCEEDED(hRet
))
316 ContextMenu
->AddRef();
320 /* Add the menu items */
321 return ContextMenu
->QueryContextMenu(hmenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
324 virtual HRESULT STDMETHODCALLTYPE
Lock(
327 BOOL bPrevLocked
= Locked
;
331 ASSERT(BandSite
!= NULL
);
333 if (bPrevLocked
!= bLock
)
337 bsi
.dwMask
= BSIM_STYLE
;
338 bsi
.dwStyle
= (Locked
? BSIS_LOCKED
| BSIS_NOGRIPPER
: BSIS_AUTOGRIPPER
);
340 hRet
= BandSite
->SetBandSiteInfo(&bsi
);
352 /*******************************************************************/
354 virtual HRESULT STDMETHODCALLTYPE
AddBand(
357 IOleCommandTarget
*pOct
;
360 hRet
= punk
->QueryInterface(IID_PPV_ARG(IOleCommandTarget
, &pOct
));
363 /* Send the DBID_DELAYINIT command to initialize the band to be added */
364 /* FIXME: Should be delayed */
375 return BandSite
->AddBand(
379 virtual HRESULT STDMETHODCALLTYPE
EnumBands(
381 OUT DWORD
*pdwBandID
)
383 return BandSite
->EnumBands(
388 virtual HRESULT STDMETHODCALLTYPE
QueryBand(
390 OUT IDeskBand
**ppstb
,
396 IDeskBand
*pstb
= NULL
;
398 hRet
= BandSite
->QueryBand(
407 hRet
= IsSameObject(pstb
, TaskBand
);
410 /* Add the BSSF_UNDELETEABLE flag to pdwState because the task bar band shouldn't be deletable */
411 if (pdwState
!= NULL
)
412 *pdwState
|= BSSF_UNDELETEABLE
;
414 else if (!SUCCEEDED(hRet
))
423 else if (ppstb
!= NULL
)
429 virtual HRESULT STDMETHODCALLTYPE
SetBandState(
434 return BandSite
->SetBandState(dwBandID
, dwMask
, dwState
);
437 virtual HRESULT STDMETHODCALLTYPE
RemoveBand(
440 return BandSite
->RemoveBand(dwBandID
);
443 virtual HRESULT STDMETHODCALLTYPE
GetBandObject(
448 return BandSite
->GetBandObject(dwBandID
, riid
, ppv
);
451 virtual HRESULT STDMETHODCALLTYPE
SetBandSiteInfo(
452 IN
const BANDSITEINFO
*pbsinfo
)
454 return BandSite
->SetBandSiteInfo(pbsinfo
);
457 virtual HRESULT STDMETHODCALLTYPE
GetBandSiteInfo(
458 IN OUT BANDSITEINFO
*pbsinfo
)
460 return BandSite
->GetBandSiteInfo(pbsinfo
);
463 virtual BOOL
HasTaskBand()
465 ASSERT(TaskBand
!= NULL
);
467 return SUCCEEDED(TaskBand
->GetRebarBandID(
471 virtual HRESULT
AddTaskBand()
474 /* FIXME: This is the code for the simple taskbar */
475 IObjectWithSite
*pOws
;
478 hRet
= TaskBand
->QueryInterface(
479 &IID_IObjectWithSite
,
483 hRet
= pOws
->SetSite(
484 (IUnknown
*)TaskBand
);
493 return BandSite
->AddBand(TaskBand
);
500 virtual HRESULT
Update()
502 IOleCommandTarget
*pOct
;
505 hRet
= punkInner
->QueryInterface(IID_PPV_ARG(IOleCommandTarget
, &pOct
));
508 /* Send the DBID_BANDINFOCHANGED command to update the band site */
511 DBID_BANDINFOCHANGED
,
522 virtual VOID
BroadcastOleCommandExec(const GUID
*pguidCmdGroup
,
528 IOleCommandTarget
*pOct
;
532 /* Enumerate all bands */
533 while (SUCCEEDED(BandSite
->EnumBands(uBand
, &dwBandID
)))
535 if (SUCCEEDED(BandSite
->GetBandObject(dwBandID
, IID_PPV_ARG(IOleCommandTarget
, &pOct
))))
537 /* Execute the command */
552 virtual HRESULT
FinishInit()
554 /* Broadcast the DBID_FINISHINIT command */
555 BroadcastOleCommandExec(&IID_IDeskBand
, DBID_FINISHINIT
, 0, NULL
, NULL
);
560 virtual HRESULT
Show(
563 IDeskBarClient
*pDbc
;
566 hRet
= BandSite
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &pDbc
));
569 hRet
= pDbc
->UIActivateDBC(
570 bShow
? DBC_SHOW
: DBC_HIDE
);
577 virtual HRESULT
LoadFromStream(IN OUT IStream
*pStm
)
579 IPersistStream
*pPStm
;
582 ASSERT(BandSite
!= NULL
);
584 /* We implement the undocumented COM interface IBandSiteStreamCallback
585 that the shell will query so that we can intercept and custom-load
586 the task band when it finds the task band's CLSID (which is internal).
587 This way we can prevent the shell from attempting to CoCreateInstance
588 the (internal) task band, resulting in a failure... */
589 hRet
= BandSite
->QueryInterface(IID_PPV_ARG(IPersistStream
, &pPStm
));
594 TRACE("->Load() returned 0x%x\n", hRet
);
602 GetUserBandsStream(IN DWORD grfMode
)
605 IStream
*Stream
= NULL
;
607 if (RegCreateKey(hkExplorer
,
609 &hkStreams
) == ERROR_SUCCESS
)
611 Stream
= SHOpenRegStream(hkStreams
,
613 TEXT("TaskbarWinXP"),
616 RegCloseKey(hkStreams
);
623 GetDefaultBandsStream(IN DWORD grfMode
)
626 IStream
*Stream
= NULL
;
628 if (RegCreateKey(HKEY_LOCAL_MACHINE
,
629 TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Streams"),
630 &hkStreams
) == ERROR_SUCCESS
)
632 Stream
= SHOpenRegStream(hkStreams
,
634 TEXT("Default Taskbar"),
637 RegCloseKey(hkStreams
);
643 virtual HRESULT
Load()
648 /* Try to load the user's settings */
649 pStm
= GetUserBandsStream(STGM_READ
);
652 hRet
= LoadFromStream(pStm
);
654 TRACE("Loaded user bands settings: 0x%x\n", hRet
);
660 /* If the user's settings couldn't be loaded, try with
661 default settings (ie. when the user logs in for the
663 if (!SUCCEEDED(hRet
))
665 pStm
= GetDefaultBandsStream(STGM_READ
);
668 hRet
= LoadFromStream(pStm
);
670 TRACE("Loaded default user bands settings: 0x%x\n", hRet
);
680 HRESULT
_Init(IN OUT ITrayWindow
*tray
, OUT HWND
*phWndRebar
, OUT HWND
*phwndTaskSwitch
)
682 IDeskBarClient
*pDbc
;
688 *phwndTaskSwitch
= NULL
;
692 /* Create a RebarBandSite provided by the shell */
693 hRet
= CoCreateInstance(CLSID_RebarBandSite
,
694 static_cast<IBandSite
*>(this),
695 CLSCTX_INPROC_SERVER
,
696 IID_PPV_ARG(IUnknown
, &punkInner
));
697 if (!SUCCEEDED(hRet
))
702 hRet
= punkInner
->QueryInterface(IID_PPV_ARG(IBandSite
, &BandSite
));
703 if (!SUCCEEDED(hRet
))
708 hRet
= punkInner
->QueryInterface(IID_PPV_ARG(IWinEventHandler
, &WindowEventHandler
));
709 if (!SUCCEEDED(hRet
))
714 TaskBand
= CreateTaskBand(Tray
);
715 if (TaskBand
!= NULL
)
717 /* Add the task band to the site */
718 hRet
= BandSite
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &pDbc
));
721 hRet
= TaskBand
->QueryInterface(IID_PPV_ARG(IOleWindow
, &pOw
));
724 /* We cause IDeskBarClient to create the rebar control by passing the new
725 task band to it. The band reports the tray window handle as window handle
726 so that IDeskBarClient knows the parent window of the Rebar control that
727 it wants to create. */
728 hRet
= pDbc
->SetDeskBarSite(pOw
);
732 /* The Rebar control is now created, we can query the window handle */
733 hRet
= pDbc
->GetWindow(&hWndRebar
);
737 /* We need to manually remove the RBS_BANDBORDERS style! */
738 SetWindowStyle(hWndRebar
, RBS_BANDBORDERS
, 0);
749 /* Set the Desk Bar mode to the current one */
751 /* FIXME: We need to set the mode (and update) whenever the user docks
752 the tray window to another monitor edge! */
754 if (!Tray
->IsHorizontal())
755 dwMode
= DBIF_VIEWMODE_VERTICAL
;
757 hRet
= pDbc
->SetModeDBC(dwMode
);
763 /* Load the saved state of the task band site */
764 /* FIXME: We should delay loading shell extensions, also see DBID_DELAYINIT */
767 /* Add the task bar band if it hasn't been added already */
768 hRet
= AddTaskBand();
771 hRet
= TaskBand
->QueryInterface(IID_PPV_ARG(IDeskBand
, &pDb
));
774 hRet
= pDb
->GetWindow(phwndTaskSwitch
);
775 if (!SUCCEEDED(hRet
))
776 *phwndTaskSwitch
= NULL
;
782 /* Should we send this after showing it? */
785 /* FIXME: When should we send this? Does anyone care anyway? */
788 /* Activate the band site */
793 *phWndRebar
= hWndRebar
;
798 /*******************************************************************/
801 CreateTrayBandSite(IN OUT ITrayWindow
*Tray
,
802 OUT HWND
*phWndRebar
,
803 OUT HWND
*phWndTaskSwitch
)
807 CTrayBandSite
* tb
= new CTrayBandSite();
814 hr
= tb
->_Init(Tray
, phWndRebar
, phWndTaskSwitch
);
815 if (FAILED_UNEXPECTEDLY(hr
))