Sync with trunk r64222.
[reactos.git] / base / shell / explorer-new / tbsite.c
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 #include "undoc.h"
26
27 /*****************************************************************************
28 ** ITrayBandSite ************************************************************
29 *****************************************************************************/
30
31 static const ITrayBandSiteVtbl ITrayBandSiteImpl_Vtbl;
32 static const IBandSiteVtbl IBandSiteImpl_Vtbl;
33
34 typedef struct
35 {
36 const ITrayBandSiteVtbl *lpVtbl;
37 const IBandSiteVtbl *lpBandSiteVtbl;
38 LONG Ref;
39
40 ITrayWindow *Tray;
41
42 IUnknown *punkInner;
43 IBandSite *BandSite;
44 ITaskBand *TaskBand;
45 IWinEventHandler *WindowEventHandler;
46 IContextMenu *ContextMenu;
47
48 HWND hWndRebar;
49
50 union
51 {
52 DWORD dwFlags;
53 struct
54 {
55 DWORD Locked : 1;
56 };
57 };
58 } ITrayBandSiteImpl;
59
60 static HRESULT
61 ITrayBandSiteImpl_Update(IN OUT ITrayBandSiteImpl *This);
62
63 static IUnknown *
64 IUnknown_from_ITrayBandSiteImpl(ITrayBandSiteImpl *This)
65 {
66 return (IUnknown *)&This->lpVtbl;
67 }
68
69 IMPL_CASTS(ITrayBandSite, ITrayBandSite, lpVtbl)
70 IMPL_CASTS(IBandSite, ITrayBandSite, lpBandSiteVtbl)
71
72 static ULONG STDMETHODCALLTYPE
73 ITrayBandSiteImpl_AddRef(IN OUT ITrayBandSite *iface)
74 {
75 ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_ITrayBandSite(iface);
76
77 return InterlockedIncrement(&This->Ref);
78 }
79
80 static VOID
81 ITrayBandSiteImpl_Free(IN OUT ITrayBandSiteImpl *This)
82 {
83 if (This->BandSite != NULL)
84 {
85 IBandSite_Release(This->BandSite);
86 This->BandSite = NULL;
87 }
88
89 if (This->WindowEventHandler != NULL)
90 {
91 IWinEventHandler_Release(This->WindowEventHandler);
92 This->WindowEventHandler = NULL;
93 }
94
95 if (This->ContextMenu != NULL)
96 {
97 IContextMenu_Release(This->ContextMenu);
98 This->ContextMenu = NULL;
99 }
100
101 if (This->punkInner != NULL)
102 {
103 IUnknown_Release(This->punkInner);
104 This->punkInner = NULL;
105 }
106
107 HeapFree(hProcessHeap,
108 0,
109 This);
110 }
111
112 static ULONG STDMETHODCALLTYPE
113 ITrayBandSiteImpl_Release(IN OUT ITrayBandSite *iface)
114 {
115 ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_ITrayBandSite(iface);
116 ULONG Ret;
117
118 Ret = InterlockedDecrement(&This->Ref);
119
120 if (Ret == 0)
121 ITrayBandSiteImpl_Free(This);
122
123 return Ret;
124 }
125
126 static HRESULT STDMETHODCALLTYPE
127 ITrayBandSiteImpl_QueryInterface(IN OUT ITrayBandSite *iface,
128 IN REFIID riid,
129 OUT LPVOID *ppvObj)
130 {
131 ITrayBandSiteImpl *This;
132
133 if (ppvObj == NULL)
134 return E_POINTER;
135
136 This = ITrayBandSiteImpl_from_ITrayBandSite(iface);
137
138 if (IsEqualIID(riid,
139 &IID_IUnknown) ||
140 IsEqualIID(riid,
141 &IID_IBandSiteStreamCallback))
142 {
143 /* NOTE: IID_IBandSiteStreamCallback is queried by the shell, we
144 implement this interface directly */
145 *ppvObj = IUnknown_from_ITrayBandSiteImpl(This);
146 }
147 else if (IsEqualIID(riid,
148 &IID_IBandSite))
149 {
150 *ppvObj = IBandSite_from_ITrayBandSiteImpl(This);
151 }
152 else if (IsEqualIID(riid,
153 &IID_IWinEventHandler))
154 {
155 TRACE("ITaskBandSite: IWinEventHandler queried!\n");
156 *ppvObj = NULL;
157 return E_NOINTERFACE;
158 }
159 else if (This->punkInner != NULL)
160 {
161 return IUnknown_QueryInterface(This->punkInner,
162 riid,
163 ppvObj);
164 }
165 else
166 {
167 *ppvObj = NULL;
168 return E_NOINTERFACE;
169 }
170
171 ITrayBandSiteImpl_AddRef(iface);
172 return S_OK;
173 }
174
175 static HRESULT STDMETHODCALLTYPE
176 ITrayBandSiteImpl_OnLoad(IN OUT ITrayBandSite *iface,
177 IN OUT IStream *pStm,
178 IN REFIID riid,
179 OUT PVOID *pvObj)
180 {
181 ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_ITrayBandSite(iface);
182 LARGE_INTEGER liPosZero;
183 ULARGE_INTEGER liCurrent;
184 CLSID clsid;
185 ULONG ulRead;
186 HRESULT hRet;
187
188 /* NOTE: Callback routine called by the shell while loading the task band
189 stream. We use it to intercept the default behavior when the task
190 band is loaded from the stream.
191
192 NOTE: riid always points to IID_IUnknown! This is because the shell hasn't
193 read anything from the stream and therefore doesn't know what CLSID
194 it's dealing with. We'll have to find it out ourselves by reading
195 the GUID from the stream. */
196
197 /* Read the current position of the stream, we'll have to reset it everytime
198 we read a CLSID that's not the task band... */
199 ZeroMemory(&liPosZero,
200 sizeof(liPosZero));
201 hRet = IStream_Seek(pStm,
202 liPosZero,
203 STREAM_SEEK_CUR,
204 &liCurrent);
205
206 if (SUCCEEDED(hRet))
207 {
208 /* Now let's read the CLSID from the stream and see if it's our task band */
209 #if defined(IStream_Read)
210 hRet = IStream_Read(pStm,
211 &clsid,
212 sizeof(clsid),
213 &ulRead);
214 #else
215 ulRead = sizeof(clsid);
216 hRet = IStream_Read(pStm,
217 &clsid,
218 sizeof(clsid));
219 #endif
220 if (SUCCEEDED(hRet) && ulRead == sizeof(clsid))
221 {
222 if (IsEqualGUID(&clsid,
223 &CLSID_ITaskBand))
224 {
225 ASSERT(This->TaskBand != NULL);
226 /* We're trying to load the task band! Let's create it... */
227
228 hRet = ITaskBand_QueryInterface(This->TaskBand,
229 riid,
230 pvObj);
231 if (SUCCEEDED(hRet))
232 {
233 /* Load the stream */
234 TRACE("IBandSiteStreamCallback::OnLoad intercepted the task band CLSID!\n");
235 }
236
237 return hRet;
238 }
239 }
240 }
241
242 /* Reset the position and let the shell do all the work for us */
243 hRet = IStream_Seek(pStm,
244 *(LARGE_INTEGER*)&liCurrent,
245 STREAM_SEEK_SET,
246 NULL);
247 if (SUCCEEDED(hRet))
248 {
249 /* Let the shell handle everything else for us :) */
250 hRet = OleLoadFromStream(pStm,
251 riid,
252 pvObj);
253 }
254
255 if (!SUCCEEDED(hRet))
256 {
257 TRACE("IBandSiteStreamCallback::OnLoad(0x%p, 0x%p, 0x%p) returns 0x%x\n", pStm, riid, pvObj, hRet);
258 }
259
260 return hRet;
261 }
262
263 static HRESULT STDMETHODCALLTYPE
264 ITrayBandSiteImpl_OnSave(IN OUT ITrayBandSite *iface,
265 IN OUT IUnknown *pUnk,
266 IN OUT IStream *pStm)
267 {
268 /* NOTE: Callback routine called by the shell while saving the task band
269 stream. We use it to intercept the default behavior when the task
270 band is saved to the stream */
271 /* FIXME: Implement */
272 TRACE("IBandSiteStreamCallback::OnSave(0x%p, 0x%p) returns E_NOTIMPL\n", pUnk, pStm);
273 return E_NOTIMPL;
274 }
275
276 static HRESULT
277 IsSameObject(IN IUnknown *punk1,
278 IN IUnknown *punk2)
279 {
280 HRESULT hRet;
281
282 hRet = IUnknown_QueryInterface(punk1,
283 &IID_IUnknown,
284 (PVOID*)&punk1);
285 if (!SUCCEEDED(hRet))
286 return hRet;
287
288 hRet = IUnknown_QueryInterface(punk2,
289 &IID_IUnknown,
290 (PVOID*)&punk2);
291 IUnknown_Release(punk1);
292
293 if (!SUCCEEDED(hRet))
294 return hRet;
295
296 IUnknown_Release(punk2);
297
298 /* We're dealing with the same object if the IUnknown pointers are equal */
299 return (punk1 == punk2) ? S_OK : S_FALSE;
300 }
301
302 static HRESULT STDMETHODCALLTYPE
303 ITrayBandSiteImpl_IsTaskBand(IN OUT ITrayBandSite *iface,
304 IN IUnknown *punk)
305 {
306 ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_ITrayBandSite(iface);
307 return IsSameObject((IUnknown *)This->BandSite,
308 punk);
309 }
310
311 static HRESULT STDMETHODCALLTYPE
312 ITrayBandSiteImpl_ProcessMessage(IN OUT ITrayBandSite *iface,
313 IN HWND hWnd,
314 IN UINT uMsg,
315 IN WPARAM wParam,
316 IN LPARAM lParam,
317 OUT LRESULT *plResult)
318 {
319 ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_ITrayBandSite(iface);
320 HRESULT hRet;
321
322 ASSERT(This->hWndRebar != NULL);
323
324 /* Custom task band behavior */
325 switch (uMsg)
326 {
327 case WM_NOTIFY:
328 {
329 const NMHDR *nmh = (const NMHDR *)lParam;
330
331 if (nmh->hwndFrom == This->hWndRebar)
332 {
333 switch (nmh->code)
334 {
335 case NM_NCHITTEST:
336 {
337 LPNMMOUSE nmm = (LPNMMOUSE)lParam;
338
339 if (nmm->dwHitInfo == RBHT_CLIENT || nmm->dwHitInfo == RBHT_NOWHERE ||
340 nmm->dwItemSpec == (DWORD_PTR)-1)
341 {
342 /* Make the rebar control appear transparent so the user
343 can drag the tray window */
344 *plResult = HTTRANSPARENT;
345 }
346 return S_OK;
347 }
348
349 case RBN_MINMAX:
350 /* Deny if an Administrator disabled this "feature" */
351 *plResult = (SHRestricted(REST_NOMOVINGBAND) != 0);
352 return S_OK;
353 }
354 }
355
356 //TRACE("ITrayBandSite::ProcessMessage: WM_NOTIFY for 0x%p, From: 0x%p, Code: NM_FIRST-%u...\n", hWnd, nmh->hwndFrom, NM_FIRST - nmh->code);
357 break;
358 }
359 };
360
361 /* Forward to the shell's IWinEventHandler interface to get the default
362 shell behavior! */
363 if (This->WindowEventHandler != NULL)
364 {
365 /*TRACE("Calling IWinEventHandler::ProcessMessage(0x%p, 0x%x, 0x%p, 0x%p, 0x%p) This->hWndRebar=0x%p\n", hWnd, uMsg, wParam, lParam, plResult, This->hWndRebar);*/
366 hRet = IWinEventHandler_OnWinEvent(This->WindowEventHandler,
367 hWnd,
368 uMsg,
369 wParam,
370 lParam,
371 plResult);
372 if (!SUCCEEDED(hRet))
373 {
374 if (uMsg == WM_NOTIFY)
375 {
376 const NMHDR *nmh = (const NMHDR *)lParam;
377 TRACE("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);
378 }
379 else
380 {
381 TRACE("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);
382 }
383 }
384 }
385 else
386 hRet = E_FAIL;
387
388 return hRet;
389 }
390
391 static HRESULT STDMETHODCALLTYPE
392 ITrayBandSiteImpl_AddContextMenus(IN OUT ITrayBandSite *iface,
393 IN HMENU hmenu,
394 IN UINT indexMenu,
395 IN UINT idCmdFirst,
396 IN UINT idCmdLast,
397 IN UINT uFlags,
398 OUT IContextMenu **ppcm)
399 {
400 ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_ITrayBandSite(iface);
401 IShellService *pSs;
402 HRESULT hRet;
403
404 if (This->ContextMenu == NULL)
405 {
406 /* Cache the context menu so we don't need to CoCreateInstance all the time... */
407 hRet = CoCreateInstance(&CLSID_IShellBandSiteMenu,
408 NULL,
409 CLSCTX_INPROC_SERVER,
410 &IID_IShellService,
411 (PVOID*)&pSs);
412 TRACE("CoCreateInstance(CLSID_IShellBandSiteMenu) for IShellService returned: 0x%x\n", hRet);
413 if (!SUCCEEDED(hRet))
414 return hRet;
415
416 hRet = IShellService_SetOwner(pSs,
417 IUnknown_from_ITrayBandSiteImpl(This));
418 if (!SUCCEEDED(hRet))
419 {
420 IShellService_Release(pSs);
421 return hRet;
422 }
423
424 hRet = IShellService_QueryInterface(pSs,
425 &IID_IContextMenu,
426 (PVOID*)&This->ContextMenu);
427
428 IShellService_Release(pSs);
429
430 if (!SUCCEEDED(hRet))
431 return hRet;
432 }
433
434 if (ppcm != NULL)
435 {
436 IContextMenu_AddRef(This->ContextMenu);
437 *ppcm = This->ContextMenu;
438 }
439
440 /* Add the menu items */
441 return IContextMenu_QueryContextMenu(This->ContextMenu,
442 hmenu,
443 indexMenu,
444 idCmdFirst,
445 idCmdLast,
446 uFlags);
447 }
448
449 static HRESULT STDMETHODCALLTYPE
450 ITrayBandSiteImpl_Lock(IN OUT ITrayBandSite *iface,
451 IN BOOL bLock)
452 {
453 ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_ITrayBandSite(iface);
454 BOOL bPrevLocked = This->Locked;
455 BANDSITEINFO bsi;
456 HRESULT hRet;
457
458 ASSERT(This->BandSite != NULL);
459
460 if (bPrevLocked != bLock)
461 {
462 This->Locked = bLock;
463
464 bsi.dwMask = BSIM_STYLE;
465 bsi.dwStyle = (This->Locked ? BSIS_LOCKED | BSIS_NOGRIPPER : BSIS_AUTOGRIPPER);
466
467 hRet = IBandSite_SetBandSiteInfo(This->BandSite,
468 &bsi);
469 if (SUCCEEDED(hRet))
470 {
471 hRet = ITrayBandSiteImpl_Update(This);
472 }
473
474 return hRet;
475 }
476
477 return S_FALSE;
478 }
479
480 static const ITrayBandSiteVtbl ITrayBandSiteImpl_Vtbl =
481 {
482 /*** IUnknown methods ***/
483 ITrayBandSiteImpl_QueryInterface,
484 ITrayBandSiteImpl_AddRef,
485 ITrayBandSiteImpl_Release,
486 /*** IBandSiteStreamCallback methods ***/
487 ITrayBandSiteImpl_OnLoad,
488 ITrayBandSiteImpl_OnSave,
489 /*** ITrayBandSite methods ***/
490 ITrayBandSiteImpl_IsTaskBand,
491 ITrayBandSiteImpl_ProcessMessage,
492 ITrayBandSiteImpl_AddContextMenus,
493 ITrayBandSiteImpl_Lock
494 };
495
496 /*******************************************************************/
497
498 METHOD_IUNKNOWN_INHERITED_ADDREF(IBandSite, ITrayBandSite)
499 METHOD_IUNKNOWN_INHERITED_RELEASE(IBandSite, ITrayBandSite)
500 METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE(IBandSite, ITrayBandSite)
501
502 static HRESULT STDMETHODCALLTYPE
503 ITrayBandSiteImpl_AddBand(IN OUT IBandSite *iface,
504 IN IUnknown *punk)
505 {
506 ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_IBandSite(iface);
507 IOleCommandTarget *pOct;
508 HRESULT hRet;
509
510 hRet = IUnknown_QueryInterface(punk,
511 &IID_IOleCommandTarget,
512 (PVOID*)&pOct);
513 if (SUCCEEDED(hRet))
514 {
515 /* Send the DBID_DELAYINIT command to initialize the band to be added */
516 /* FIXME: Should be delayed */
517 IOleCommandTarget_Exec(pOct,
518 &IID_IDeskBand,
519 DBID_DELAYINIT,
520 0,
521 NULL,
522 NULL);
523
524 IOleCommandTarget_Release(pOct);
525 }
526
527 return IBandSite_AddBand(This->BandSite,
528 punk);
529 }
530
531 static HRESULT STDMETHODCALLTYPE
532 ITrayBandSiteImpl_EnumBands(IN OUT IBandSite *iface,
533 IN UINT uBand,
534 OUT DWORD *pdwBandID)
535 {
536 ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_IBandSite(iface);
537 return IBandSite_EnumBands(This->BandSite,
538 uBand,
539 pdwBandID);
540 }
541
542 static HRESULT STDMETHODCALLTYPE
543 ITrayBandSiteImpl_QueryBand(IN OUT IBandSite *iface,
544 IN DWORD dwBandID,
545 OUT IDeskBand **ppstb,
546 OUT DWORD *pdwState,
547 OUT LPWSTR pszName,
548 IN int cchName)
549 {
550 HRESULT hRet;
551 IDeskBand *pstb = NULL;
552 ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_IBandSite(iface);
553
554 hRet = IBandSite_QueryBand(This->BandSite,
555 dwBandID,
556 &pstb,
557 pdwState,
558 pszName,
559 cchName);
560
561 if (SUCCEEDED(hRet))
562 {
563 hRet = IsSameObject((IUnknown *)pstb,
564 (IUnknown *)This->TaskBand);
565 if (hRet == S_OK)
566 {
567 /* Add the BSSF_UNDELETEABLE flag to pdwState because the task bar band shouldn't be deletable */
568 if (pdwState != NULL)
569 *pdwState |= BSSF_UNDELETEABLE;
570 }
571 else if (!SUCCEEDED(hRet))
572 {
573 IDeskBand_Release(pstb);
574 pstb = NULL;
575 }
576
577 if (ppstb != NULL)
578 *ppstb = pstb;
579 }
580 else if (ppstb != NULL)
581 *ppstb = NULL;
582
583 return hRet;
584 }
585
586 static HRESULT STDMETHODCALLTYPE
587 ITrayBandSiteImpl_SetBandState(IN OUT IBandSite *iface,
588 IN DWORD dwBandID,
589 IN DWORD dwMask,
590 IN DWORD dwState)
591 {
592 ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_IBandSite(iface);
593 return IBandSite_SetBandState(This->BandSite,
594 dwBandID,
595 dwMask,
596 dwState);
597 }
598
599 static HRESULT STDMETHODCALLTYPE
600 ITrayBandSiteImpl_RemoveBand(IN OUT IBandSite *iface,
601 IN DWORD dwBandID)
602 {
603 ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_IBandSite(iface);
604 return IBandSite_RemoveBand(This->BandSite,
605 dwBandID);
606 }
607
608 static HRESULT STDMETHODCALLTYPE
609 ITrayBandSiteImpl_GetBandObject(IN OUT IBandSite *iface,
610 IN DWORD dwBandID,
611 IN REFIID riid,
612 OUT VOID **ppv)
613 {
614 ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_IBandSite(iface);
615 return IBandSite_GetBandObject(This->BandSite,
616 dwBandID,
617 riid,
618 ppv);
619 }
620
621 static HRESULT STDMETHODCALLTYPE
622 ITrayBandSiteImpl_SetBandSiteInfo(IN OUT IBandSite *iface,
623 IN const BANDSITEINFO *pbsinfo)
624 {
625 ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_IBandSite(iface);
626 return IBandSite_SetBandSiteInfo(This->BandSite,
627 pbsinfo);
628 }
629
630 static HRESULT STDMETHODCALLTYPE
631 ITrayBandSiteImpl_GetBandSiteInfo(IN OUT IBandSite *iface,
632 IN OUT BANDSITEINFO *pbsinfo)
633 {
634 ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_IBandSite(iface);
635 return IBandSite_GetBandSiteInfo(This->BandSite,
636 pbsinfo);
637 }
638
639 static const IBandSiteVtbl IBandSiteImpl_Vtbl =
640 {
641 /*** IUnknown methods ***/
642 METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE_NAME(IBandSite, ITrayBandSite),
643 METHOD_IUNKNOWN_INHERITED_ADDREF_NAME(IBandSite, ITrayBandSite),
644 METHOD_IUNKNOWN_INHERITED_RELEASE_NAME(IBandSite, ITrayBandSite),
645 /*** IBandSite methods ***/
646 ITrayBandSiteImpl_AddBand,
647 ITrayBandSiteImpl_EnumBands,
648 ITrayBandSiteImpl_QueryBand,
649 ITrayBandSiteImpl_SetBandState,
650 ITrayBandSiteImpl_RemoveBand,
651 ITrayBandSiteImpl_GetBandObject,
652 ITrayBandSiteImpl_SetBandSiteInfo,
653 ITrayBandSiteImpl_GetBandSiteInfo,
654 };
655
656 static BOOL
657 ITrayBandSiteImpl_HasTaskBand(IN OUT ITrayBandSiteImpl *This)
658 {
659 ASSERT(This->TaskBand != NULL);
660
661 return SUCCEEDED(ITaskBand_GetRebarBandID(This->TaskBand,
662 NULL));
663 }
664
665 static HRESULT
666 ITrayBandSiteImpl_AddTaskBand(IN OUT ITrayBandSiteImpl *This)
667 {
668 #if 0
669 /* FIXME: This is the code for the simple taskbar */
670 IObjectWithSite *pOws;
671 HRESULT hRet;
672
673 hRet = ITaskBand_QueryInterface(This->TaskBand,
674 &IID_IObjectWithSite,
675 (PVOID*)&pOws);
676 if (SUCCEEDED(hRet))
677 {
678 hRet = IObjectWithSite_SetSite(pOws,
679 (IUnknown *)This->TaskBand);
680
681 IObjectWithSite_Release(pOws);
682 }
683
684 return hRet;
685 #else
686 if (!ITrayBandSiteImpl_HasTaskBand(This))
687 {
688 return IBandSite_AddBand(This->BandSite,
689 (IUnknown *)This->TaskBand);
690 }
691
692 return S_OK;
693 #endif
694 }
695
696 static HRESULT
697 ITrayBandSiteImpl_Update(IN OUT ITrayBandSiteImpl *This)
698 {
699 IOleCommandTarget *pOct;
700 HRESULT hRet;
701
702 hRet = IUnknown_QueryInterface(This->punkInner,
703 &IID_IOleCommandTarget,
704 (PVOID*)&pOct);
705 if (SUCCEEDED(hRet))
706 {
707 /* Send the DBID_BANDINFOCHANGED command to update the band site */
708 hRet = IOleCommandTarget_Exec(pOct,
709 &IID_IDeskBand,
710 DBID_BANDINFOCHANGED,
711 0,
712 NULL,
713 NULL);
714
715 IOleCommandTarget_Release(pOct);
716 }
717
718 return hRet;
719 }
720
721 static VOID
722 ITrayBandSiteImpl_BroadcastOleCommandExec(IN OUT ITrayBandSiteImpl *This,
723 const GUID *pguidCmdGroup,
724 DWORD nCmdID,
725 DWORD nCmdExecOpt,
726 VARIANTARG *pvaIn,
727 VARIANTARG *pvaOut)
728 {
729 IOleCommandTarget *pOct;
730 DWORD dwBandID;
731 UINT uBand = 0;
732
733 /* Enumerate all bands */
734 while (SUCCEEDED(IBandSite_EnumBands(This->BandSite,
735 uBand,
736 &dwBandID)))
737 {
738 if (SUCCEEDED(IBandSite_GetBandObject(This->BandSite,
739 dwBandID,
740 &IID_IOleCommandTarget,
741 (PVOID*)&pOct)))
742 {
743 /* Execute the command */
744 IOleCommandTarget_Exec(pOct,
745 pguidCmdGroup,
746 nCmdID,
747 nCmdExecOpt,
748 pvaIn,
749 pvaOut);
750
751 IOleCommandTarget_Release(pOct);
752 }
753
754 uBand++;
755 }
756 }
757
758 static HRESULT
759 ITrayBandSiteImpl_FinishInit(IN OUT ITrayBandSiteImpl *This)
760 {
761 /* Broadcast the DBID_FINISHINIT command */
762 ITrayBandSiteImpl_BroadcastOleCommandExec(This,
763 &IID_IDeskBand,
764 DBID_FINISHINIT,
765 0,
766 NULL,
767 NULL);
768
769 return S_OK;
770 }
771
772 static HRESULT
773 ITrayBandSiteImpl_Show(IN OUT ITrayBandSiteImpl *This,
774 IN BOOL bShow)
775 {
776 IDeskBarClient *pDbc;
777 HRESULT hRet;
778
779 hRet = IBandSite_QueryInterface(This->BandSite,
780 &IID_IDeskBarClient,
781 (PVOID*)&pDbc);
782 if (SUCCEEDED(hRet))
783 {
784 hRet = IDeskBarClient_UIActivateDBC(pDbc,
785 bShow ? DBC_SHOW : DBC_HIDE);
786 IDeskBarClient_Release(pDbc);
787 }
788
789 return hRet;
790 }
791
792 static HRESULT
793 ITrayBandSiteImpl_LoadFromStream(IN OUT ITrayBandSiteImpl *This,
794 IN OUT IStream *pStm)
795 {
796 IPersistStream *pPStm;
797 HRESULT hRet;
798
799 ASSERT(This->BandSite != NULL);
800
801 /* We implement the undocumented COM interface IBandSiteStreamCallback
802 that the shell will query so that we can intercept and custom-load
803 the task band when it finds the task band's CLSID (which is internal).
804 This way we can prevent the shell from attempting to CoCreateInstance
805 the (internal) task band, resulting in a failure... */
806 hRet = IBandSite_QueryInterface(This->BandSite,
807 &IID_IPersistStream,
808 (PVOID*)&pPStm);
809 if (SUCCEEDED(hRet))
810 {
811 hRet = IPersistStream_Load(pPStm,
812 pStm);
813 TRACE("IPersistStream_Load() returned 0x%x\n", hRet);
814 IPersistStream_Release(pPStm);
815 }
816
817 return hRet;
818 }
819
820 static IStream *
821 GetUserBandsStream(IN DWORD grfMode)
822 {
823 HKEY hkStreams;
824 IStream *Stream = NULL;
825
826 if (RegCreateKey(hkExplorer,
827 TEXT("Streams"),
828 &hkStreams) == ERROR_SUCCESS)
829 {
830 Stream = SHOpenRegStream(hkStreams,
831 TEXT("Desktop"),
832 TEXT("TaskbarWinXP"),
833 grfMode);
834
835 RegCloseKey(hkStreams);
836 }
837
838 return Stream;
839 }
840
841 static IStream *
842 GetDefaultBandsStream(IN DWORD grfMode)
843 {
844 HKEY hkStreams;
845 IStream *Stream = NULL;
846
847 if (RegCreateKey(HKEY_LOCAL_MACHINE,
848 TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Streams"),
849 &hkStreams) == ERROR_SUCCESS)
850 {
851 Stream = SHOpenRegStream(hkStreams,
852 TEXT("Desktop"),
853 TEXT("Default Taskbar"),
854 grfMode);
855
856 RegCloseKey(hkStreams);
857 }
858
859 return Stream;
860 }
861
862 static HRESULT
863 ITrayBandSiteImpl_Load(IN OUT ITrayBandSiteImpl *This)
864 {
865 IStream *pStm;
866 HRESULT hRet;
867
868 /* Try to load the user's settings */
869 pStm = GetUserBandsStream(STGM_READ);
870 if (pStm != NULL)
871 {
872 hRet = ITrayBandSiteImpl_LoadFromStream(This,
873 pStm);
874
875 TRACE("Loaded user bands settings: 0x%x\n", hRet);
876 IStream_Release(pStm);
877 }
878 else
879 hRet = E_FAIL;
880
881 /* If the user's settings couldn't be loaded, try with
882 default settings (ie. when the user logs in for the
883 first time! */
884 if (!SUCCEEDED(hRet))
885 {
886 pStm = GetDefaultBandsStream(STGM_READ);
887 if (pStm != NULL)
888 {
889 hRet = ITrayBandSiteImpl_LoadFromStream(This,
890 pStm);
891
892 TRACE("Loaded default user bands settings: 0x%x\n", hRet);
893 IStream_Release(pStm);
894 }
895 else
896 hRet = E_FAIL;
897 }
898
899 return hRet;
900 }
901
902 static ITrayBandSiteImpl *
903 ITrayBandSiteImpl_Construct(IN OUT ITrayWindow *Tray,
904 OUT HWND *phWndRebar,
905 OUT HWND *phwndTaskSwitch)
906 {
907 ITrayBandSiteImpl *This;
908 IDeskBarClient *pDbc;
909 IDeskBand *pDb;
910 IOleWindow *pOw;
911 HRESULT hRet;
912
913 *phWndRebar = NULL;
914 *phwndTaskSwitch = NULL;
915
916 This = HeapAlloc(hProcessHeap,
917 HEAP_ZERO_MEMORY,
918 sizeof(*This));
919 if (This == NULL)
920 return NULL;
921
922 This->lpVtbl = &ITrayBandSiteImpl_Vtbl;
923 This->lpBandSiteVtbl = &IBandSiteImpl_Vtbl;
924 This->Ref = 1;
925 This->Tray = Tray;
926
927 /* Create a RebarBandSite provided by the shell */
928 hRet = CoCreateInstance(&CLSID_RebarBandSite,
929 (LPUNKNOWN)IBandSite_from_ITrayBandSiteImpl(This),
930 CLSCTX_INPROC_SERVER,
931 &IID_IUnknown,
932 (LPVOID*)&This->punkInner);
933 if (!SUCCEEDED(hRet))
934 {
935 ITrayBandSiteImpl_Free(This);
936 return NULL;
937 }
938
939 hRet = IUnknown_QueryInterface(This->punkInner,
940 &IID_IBandSite,
941 (PVOID*)&This->BandSite);
942 if (!SUCCEEDED(hRet))
943 {
944 ITrayBandSiteImpl_Free(This);
945 return NULL;
946 }
947
948 hRet = IUnknown_QueryInterface(This->punkInner,
949 &IID_IWinEventHandler,
950 (PVOID*)&This->WindowEventHandler);
951 if (!SUCCEEDED(hRet))
952 {
953 ITrayBandSiteImpl_Free(This);
954 return NULL;
955 }
956
957 This->TaskBand = CreateTaskBand(Tray);
958 if (This->TaskBand != NULL)
959 {
960 /* Add the task band to the site */
961 hRet = IBandSite_QueryInterface(This->BandSite,
962 &IID_IDeskBarClient,
963 (PVOID*)&pDbc);
964 if (SUCCEEDED(hRet))
965 {
966 hRet = ITaskBand_QueryInterface(This->TaskBand,
967 &IID_IOleWindow,
968 (PVOID*)&pOw);
969 if (SUCCEEDED(hRet))
970 {
971 /* We cause IDeskBarClient to create the rebar control by passing the new
972 task band to it. The band reports the tray window handle as window handle
973 so that IDeskBarClient knows the parent window of the Rebar control that
974 it wants to create. */
975 hRet = IDeskBarClient_SetDeskBarSite(pDbc,
976 (IUnknown *)pOw);
977
978 if (SUCCEEDED(hRet))
979 {
980 /* The Rebar control is now created, we can query the window handle */
981 hRet = IDeskBarClient_GetWindow(pDbc,
982 &This->hWndRebar);
983
984 if (SUCCEEDED(hRet))
985 {
986 /* We need to manually remove the RBS_BANDBORDERS style! */
987 SetWindowStyle(This->hWndRebar,
988 RBS_BANDBORDERS,
989 0);
990 }
991 }
992
993 IOleWindow_Release(pOw);
994 }
995
996 if (SUCCEEDED(hRet))
997 {
998 DWORD dwMode = 0;
999
1000 /* Set the Desk Bar mode to the current one */
1001
1002 /* FIXME: We need to set the mode (and update) whenever the user docks
1003 the tray window to another monitor edge! */
1004
1005 if (!ITrayWindow_IsHorizontal(This->Tray))
1006 dwMode = DBIF_VIEWMODE_VERTICAL;
1007
1008 hRet = IDeskBarClient_SetModeDBC(pDbc,
1009 dwMode);
1010 }
1011
1012 IDeskBarClient_Release(pDbc);
1013 }
1014
1015 /* Load the saved state of the task band site */
1016 /* FIXME: We should delay loading shell extensions, also see DBID_DELAYINIT */
1017 ITrayBandSiteImpl_Load(This);
1018
1019 /* Add the task bar band if it hasn't been added already */
1020 hRet = ITrayBandSiteImpl_AddTaskBand(This);
1021 if (SUCCEEDED(hRet))
1022 {
1023 hRet = ITaskBand_QueryInterface(This->TaskBand,
1024 &IID_IDeskBand,
1025 (PVOID*)&pDb);
1026 if (SUCCEEDED(hRet))
1027 {
1028 hRet = IDeskBand_GetWindow(pDb,
1029 phwndTaskSwitch);
1030 if (!SUCCEEDED(hRet))
1031 *phwndTaskSwitch = NULL;
1032
1033 IDeskBand_Release(pDb);
1034 }
1035 }
1036
1037 /* Should we send this after showing it? */
1038 ITrayBandSiteImpl_Update(This);
1039
1040 /* FIXME: When should we send this? Does anyone care anyway? */
1041 ITrayBandSiteImpl_FinishInit(This);
1042
1043 /* Activate the band site */
1044 ITrayBandSiteImpl_Show(This,
1045 TRUE);
1046 }
1047
1048 *phWndRebar = This->hWndRebar;
1049
1050 return This;
1051 }
1052
1053 /*******************************************************************/
1054
1055 ITrayBandSite *
1056 CreateTrayBandSite(IN OUT ITrayWindow *Tray,
1057 OUT HWND *phWndRebar,
1058 OUT HWND *phWndTaskSwitch)
1059 {
1060 ITrayBandSiteImpl *This;
1061
1062 This = ITrayBandSiteImpl_Construct(Tray,
1063 phWndRebar,
1064 phWndTaskSwitch);
1065 if (This != NULL)
1066 {
1067 return ITrayBandSite_from_ITrayBandSiteImpl(This);
1068 }
1069
1070 return NULL;
1071 }