[EXPLORER-NEW]
[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, &IID_IUnknown) ||
139 IsEqualIID(riid, &IID_IBandSiteStreamCallback))
140 {
141 /* NOTE: IID_IBandSiteStreamCallback is queried by the shell, we
142 implement this interface directly */
143 *ppvObj = IUnknown_from_ITrayBandSiteImpl(This);
144 }
145 else if (IsEqualIID(riid, &IID_IBandSite))
146 {
147 *ppvObj = IBandSite_from_ITrayBandSiteImpl(This);
148 }
149 else if (IsEqualIID(riid, &IID_IWinEventHandler))
150 {
151 TRACE("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 TRACE("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 TRACE("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 TRACE("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 //TRACE("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 /*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);*/
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 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);
374 }
375 else
376 {
377 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);
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 TRACE("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 TRACE("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 TRACE("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 TRACE("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 HEAP_ZERO_MEMORY,
914 sizeof(*This));
915 if (This == NULL)
916 return NULL;
917
918 This->lpVtbl = &ITrayBandSiteImpl_Vtbl;
919 This->lpBandSiteVtbl = &IBandSiteImpl_Vtbl;
920 This->Ref = 1;
921 This->Tray = Tray;
922
923 /* Create a RebarBandSite provided by the shell */
924 hRet = CoCreateInstance(&CLSID_RebarBandSite,
925 (LPUNKNOWN)IBandSite_from_ITrayBandSiteImpl(This),
926 CLSCTX_INPROC_SERVER,
927 &IID_IUnknown,
928 (LPVOID*)&This->punkInner);
929 if (!SUCCEEDED(hRet))
930 {
931 ITrayBandSiteImpl_Free(This);
932 return NULL;
933 }
934
935 hRet = IUnknown_QueryInterface(This->punkInner,
936 &IID_IBandSite,
937 (PVOID*)&This->BandSite);
938 if (!SUCCEEDED(hRet))
939 {
940 ITrayBandSiteImpl_Free(This);
941 return NULL;
942 }
943
944 hRet = IUnknown_QueryInterface(This->punkInner,
945 &IID_IWinEventHandler,
946 (PVOID*)&This->WindowEventHandler);
947 if (!SUCCEEDED(hRet))
948 {
949 ITrayBandSiteImpl_Free(This);
950 return NULL;
951 }
952
953 This->TaskBand = CreateTaskBand(Tray);
954 if (This->TaskBand != NULL)
955 {
956 /* Add the task band to the site */
957 hRet = IBandSite_QueryInterface(This->BandSite,
958 &IID_IDeskBarClient,
959 (PVOID*)&pDbc);
960 if (SUCCEEDED(hRet))
961 {
962 hRet = ITaskBand_QueryInterface(This->TaskBand,
963 &IID_IOleWindow,
964 (PVOID*)&pOw);
965 if (SUCCEEDED(hRet))
966 {
967 /* We cause IDeskBarClient to create the rebar control by passing the new
968 task band to it. The band reports the tray window handle as window handle
969 so that IDeskBarClient knows the parent window of the Rebar control that
970 it wants to create. */
971 hRet = IDeskBarClient_SetDeskBarSite(pDbc,
972 (IUnknown *)pOw);
973
974 if (SUCCEEDED(hRet))
975 {
976 /* The Rebar control is now created, we can query the window handle */
977 hRet = IDeskBarClient_GetWindow(pDbc,
978 &This->hWndRebar);
979
980 if (SUCCEEDED(hRet))
981 {
982 /* We need to manually remove the RBS_BANDBORDERS style! */
983 SetWindowStyle(This->hWndRebar,
984 RBS_BANDBORDERS,
985 0);
986 }
987 }
988
989 IOleWindow_Release(pOw);
990 }
991
992 if (SUCCEEDED(hRet))
993 {
994 DWORD dwMode = 0;
995
996 /* Set the Desk Bar mode to the current one */
997
998 /* FIXME: We need to set the mode (and update) whenever the user docks
999 the tray window to another monitor edge! */
1000
1001 if (!ITrayWindow_IsHorizontal(This->Tray))
1002 dwMode = DBIF_VIEWMODE_VERTICAL;
1003
1004 hRet = IDeskBarClient_SetModeDBC(pDbc,
1005 dwMode);
1006 }
1007
1008 IDeskBarClient_Release(pDbc);
1009 }
1010
1011 /* Load the saved state of the task band site */
1012 /* FIXME: We should delay loading shell extensions, also see DBID_DELAYINIT */
1013 ITrayBandSiteImpl_Load(This);
1014
1015 /* Add the task bar band if it hasn't been added already */
1016 hRet = ITrayBandSiteImpl_AddTaskBand(This);
1017 if (SUCCEEDED(hRet))
1018 {
1019 hRet = ITaskBand_QueryInterface(This->TaskBand,
1020 &IID_IDeskBand,
1021 (PVOID*)&pDb);
1022 if (SUCCEEDED(hRet))
1023 {
1024 hRet = IDeskBand_GetWindow(pDb,
1025 phwndTaskSwitch);
1026 if (!SUCCEEDED(hRet))
1027 *phwndTaskSwitch = NULL;
1028
1029 IDeskBand_Release(pDb);
1030 }
1031 }
1032
1033 /* Should we send this after showing it? */
1034 ITrayBandSiteImpl_Update(This);
1035
1036 /* FIXME: When should we send this? Does anyone care anyway? */
1037 ITrayBandSiteImpl_FinishInit(This);
1038
1039 /* Activate the band site */
1040 ITrayBandSiteImpl_Show(This,
1041 TRUE);
1042 }
1043
1044 *phWndRebar = This->hWndRebar;
1045
1046 return This;
1047 }
1048
1049 /*******************************************************************/
1050
1051 ITrayBandSite *
1052 CreateTrayBandSite(IN OUT ITrayWindow *Tray,
1053 OUT HWND *phWndRebar,
1054 OUT HWND *phWndTaskSwitch)
1055 {
1056 ITrayBandSiteImpl *This;
1057
1058 This = ITrayBandSiteImpl_Construct(Tray,
1059 phWndRebar,
1060 phWndTaskSwitch);
1061 if (This != NULL)
1062 {
1063 return ITrayBandSite_from_ITrayBandSiteImpl(This);
1064 }
1065
1066 return NULL;
1067 }