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