1 /*
2  *  Rebar band site
3  *
4  *  Copyright 2007  Herv� Poussineau
5  *  Copyright 2009  Andrew Hill
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include "shellbars.h"
23 
24 #ifndef ASSERT
25 #define ASSERT(cond) \
26     if (!(cond)) \
27         ERR ("ASSERTION %s AT %s:%d FAILED!\n", #cond, __FILE__, __LINE__)
28 #endif
29 
30 
31 /*
32 TODO:
33     ** Fix tasks band gripper not appearing when quick launch is added
34     ** Fix hiding grippers in locked mode
35     ** The context menu should include the menu of both the site and the band
36     ** The chevron should be shown only when needed
37 */
38 
39 CBandSiteBase::CBandSiteBase()
40 {
41     fBandsCount = 0;
42     fBandsAllocated = 0;
43     fBands = NULL;
44     fRebarWindow = NULL;
45 }
46 
47 UINT CBandSiteBase::GetBandID(struct BandObject *Band)
48 {
49     return (UINT)(Band - fBands);
50 }
51 
52 struct CBandSiteBase::BandObject *CBandSiteBase::GetBandByID(DWORD dwBandID)
53 {
54     if ((LONG)dwBandID >= fBandsAllocated)
55         return NULL;
56 
57     if (fBands[dwBandID].DeskBand == NULL)
58         return NULL;
59 
60     return &fBands[dwBandID];
61 }
62 
63 void CBandSiteBase::FreeBand(struct BandObject *Band)
64 {
65     ATLASSERT(Band->DeskBand != NULL);
66     ATLASSERT(Band->OleWindow != NULL);
67     ATLASSERT(Band->WndEvtHandler != NULL);
68     Band->DeskBand->Release();
69     Band->OleWindow->Release();
70     Band->WndEvtHandler->Release();
71     memset(Band, 0, sizeof(*Band));
72     fBandsCount--;
73 }
74 
75 DWORD CBandSiteBase::GetBandSiteViewMode()
76 {
77     DWORD                                   dwStyle;
78 
79     /* FIXME: What about DBIF_VIEWMODE_FLOATING and DBIF_VIEWMODE_TRANSPARENT? */
80     dwStyle = GetWindowLongPtr(fRebarWindow, GWL_STYLE);
81 
82     if (dwStyle & CCS_VERT)
83         return DBIF_VIEWMODE_VERTICAL;
84     else
85         return DBIF_VIEWMODE_NORMAL;
86 }
87 
88 VOID CBandSiteBase::BuildRebarBandInfo(struct BandObject *Band, REBARBANDINFOW *prbi)
89 {
90     ZeroMemory(prbi, sizeof(*prbi));
91     prbi->cbSize = sizeof(*prbi);
92 
93     prbi->fMask = RBBIM_ID;
94     prbi->wID = GetBandID(Band);
95 
96     if (Band->dbi.dwMask & DBIM_MINSIZE)
97     {
98         prbi->fMask |= RBBIM_CHILDSIZE;
99         prbi->cxMinChild = Band->dbi.ptMinSize.x;
100         prbi->cyMinChild = Band->dbi.ptMinSize.y;
101     }
102 
103     if (Band->dbi.dwMask & DBIM_MAXSIZE)
104     {
105         prbi->fMask |= RBBIM_CHILDSIZE;
106         prbi->cyMaxChild = Band->dbi.ptMaxSize.y;
107     }
108 
109     if ((Band->dbi.dwMask & (DBIM_INTEGRAL | DBIM_MODEFLAGS)) == (DBIM_INTEGRAL | DBIM_MODEFLAGS) &&
110         (Band->dbi.dwModeFlags & DBIMF_VARIABLEHEIGHT))
111     {
112         prbi->fMask |= RBBIM_CHILDSIZE;
113         prbi->cyIntegral = Band->dbi.ptIntegral.y;
114     }
115 
116     if (Band->dbi.dwMask & DBIM_ACTUAL)
117     {
118         prbi->fMask |= RBBIM_IDEALSIZE | RBBIM_SIZE | RBBIM_CHILDSIZE;
119         prbi->cxIdeal = Band->dbi.ptActual.x;
120         prbi->cx = Band->dbi.ptActual.x;
121         prbi->cyChild = Band->dbi.ptActual.y;
122     }
123 
124     if (Band->dbi.dwMask & DBIM_TITLE)
125     {
126         prbi->fMask |= RBBIM_TEXT;
127         prbi->lpText = Band->dbi.wszTitle;
128         prbi->cch = wcslen(Band->dbi.wszTitle);
129     }
130 
131     if (Band->dbi.dwMask & DBIM_MODEFLAGS)
132     {
133         prbi->fMask |= RBBIM_STYLE;
134 
135         if (Band->dbi.dwModeFlags & DBIMF_FIXED)
136             prbi->fStyle |= RBBS_FIXEDSIZE | RBBS_NOGRIPPER;
137         if (Band->dbi.dwModeFlags & DBIMF_FIXEDBMP)
138             prbi->fStyle |= RBBS_FIXEDBMP;
139         if (Band->dbi.dwModeFlags & DBIMF_VARIABLEHEIGHT)
140             prbi->fStyle |= RBBS_VARIABLEHEIGHT;
141         if (Band->dbi.dwModeFlags & DBIMF_DEBOSSED)
142             prbi->fStyle |= RBBS_CHILDEDGE;
143         if (Band->dbi.dwModeFlags & DBIMF_USECHEVRON)
144             prbi->fStyle |= RBBS_USECHEVRON;
145         if (Band->dbi.dwModeFlags & DBIMF_BREAK)
146             prbi->fStyle |= RBBS_BREAK;
147         if (Band->dbi.dwModeFlags & DBIMF_TOPALIGN)
148             prbi->fStyle |= RBBS_TOPALIGN;
149         if (Band->dbi.dwModeFlags & DBIMF_NOGRIPPER)
150             prbi->fStyle |= RBBS_NOGRIPPER;
151         if (Band->dbi.dwModeFlags & DBIMF_ALWAYSGRIPPER)
152             prbi->fStyle |= RBBS_GRIPPERALWAYS;
153     }
154 
155     if (Band->bHiddenTitle)
156     {
157         prbi->fMask |= RBBIM_STYLE;
158         prbi->fStyle |= RBBS_HIDETITLE;
159     }
160 
161     if ((Band->dbi.dwMask & (DBIM_BKCOLOR | DBIM_MODEFLAGS)) == (DBIM_BKCOLOR | DBIM_MODEFLAGS) &&
162         (Band->dbi.dwModeFlags & DBIMF_BKCOLOR))
163     {
164         prbi->fMask |= RBBIM_COLORS;
165         prbi->clrFore = (COLORREF)(COLOR_WINDOWTEXT + 1);
166         prbi->clrBack = Band->dbi.crBkgnd;
167     }
168 }
169 
170 HRESULT CBandSiteBase::UpdateSingleBand(struct BandObject *Band)
171 {
172     REBARBANDINFOW                          rbi;
173     DWORD                                   dwViewMode;
174     UINT                                    uBand;
175     HRESULT                                 hRet;
176 
177     ZeroMemory(&Band->dbi, sizeof(Band->dbi));
178     Band->dbi.dwMask = DBIM_MINSIZE | DBIM_MAXSIZE | DBIM_INTEGRAL |
179         DBIM_ACTUAL | DBIM_TITLE | DBIM_MODEFLAGS | DBIM_BKCOLOR;
180 
181     dwViewMode = GetBandSiteViewMode();
182 
183     hRet = Band->DeskBand->GetBandInfo((DWORD)GetBandID(Band), dwViewMode, &Band->dbi);
184     if (SUCCEEDED(hRet))
185     {
186         BuildRebarBandInfo(Band, &rbi);
187         if (SUCCEEDED(Band->OleWindow->GetWindow(&rbi.hwndChild)) &&
188             rbi.hwndChild != NULL)
189         {
190             rbi.fMask |= RBBIM_CHILD;
191             WARN ("ReBar band uses child window 0x%p\n", rbi.hwndChild);
192         }
193 
194         uBand = (UINT)SendMessageW(fRebarWindow, RB_IDTOINDEX, (WPARAM)rbi.wID, 0);
195         if (uBand != (UINT)-1)
196         {
197             if (!SendMessageW(fRebarWindow, RB_SETBANDINFOW, (WPARAM)uBand, reinterpret_cast<LPARAM>(&rbi)))
198             {
199                 WARN("Failed to update the rebar band!\n");
200             }
201         }
202         else
203             WARN("Failed to map rebar band id to index!\n");
204 
205     }
206 
207     return hRet;
208 }
209 
210 HRESULT CBandSiteBase::UpdateAllBands()
211 {
212     LONG                                    i;
213     HRESULT                                 hRet;
214 
215     for (i = 0; i < fBandsAllocated; i++)
216     {
217         if (fBands[i].DeskBand != NULL)
218         {
219             hRet = UpdateSingleBand(&fBands[i]);
220             if (FAILED_UNEXPECTEDLY(hRet))
221                 return hRet;
222         }
223     }
224 
225     return S_OK;
226 }
227 
228 HRESULT CBandSiteBase::UpdateBand(DWORD dwBandID)
229 {
230     struct BandObject                       *Band;
231 
232     Band = GetBandByID(dwBandID);
233     if (Band == NULL)
234         return E_FAIL;
235 
236     return UpdateSingleBand(Band);
237 }
238 
239 HRESULT CBandSiteBase::_IsBandDeletable(DWORD dwBandID)
240 {
241     CComPtr<IBandSite> pbs;
242 
243     /* Use QueryInterface so that we get the outer object in case we have one */
244     HRESULT hr = this->QueryInterface(IID_PPV_ARG(IBandSite, &pbs));
245     if (FAILED_UNEXPECTEDLY(hr))
246         return hr;
247 
248     DWORD dwState;
249     hr = pbs->QueryBand(dwBandID, NULL, &dwState, NULL, NULL);
250     if (FAILED_UNEXPECTEDLY(hr))
251         return hr;
252 
253     return ((dwState & BSSF_UNDELETEABLE) != 0) ? S_FALSE : S_OK;
254 }
255 
256 HRESULT CBandSiteBase::OnContextMenu(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plrResult)
257 {
258     /* Find the index fo the band that was clicked */
259     int x = GET_X_LPARAM(lParam);
260     int y = GET_Y_LPARAM(lParam);
261 
262     RBHITTESTINFO htInfo = {{x, y}};
263     ScreenToClient(fRebarWindow, &htInfo.pt);
264     int iBand = SendMessageW(fRebarWindow, RB_HITTEST, 0, (LPARAM)&htInfo);
265     if (iBand < 0)
266     {
267         /* FIXME: what to do here? */
268         return S_OK;
269     }
270 
271     /* Now get the id of the band that was clicked */
272     REBARBANDINFOW bandInfo = {sizeof(bandInfo), RBBIM_ID};
273     SendMessageW(fRebarWindow, RB_GETBANDINFOW, htInfo.iBand, (LPARAM)&bandInfo);
274 
275     /* Finally get the band */
276     DWORD dwBandID = bandInfo.wID;
277     struct BandObject *Band = GetBandByID(dwBandID);
278     if (Band == NULL)
279         return E_FAIL;
280 
281     HMENU hMenu = CreatePopupMenu();
282     if (hMenu == NULL)
283         return E_OUTOFMEMORY;
284 
285     /* Try to load the menu of the band */
286     UINT idBandLast = 0;
287     CComPtr<IContextMenu> pcm;
288     HRESULT hr = Band->DeskBand->QueryInterface(IID_PPV_ARG(IContextMenu, &pcm));
289     if (SUCCEEDED(hr))
290     {
291         hr = pcm->QueryContextMenu(hMenu, 0, 0, UINT_MAX, CMF_NORMAL);
292         if (SUCCEEDED(hr))
293         {
294             idBandLast = HRESULT_CODE(hr);
295         }
296     }
297 
298     /* Load the static part of the menu */
299     HMENU hMenuStatic = LoadMenuW(GetModuleHandleW(L"browseui.dll"), MAKEINTRESOURCEW(IDM_BAND_MENU));
300 
301     if (hMenuStatic)
302     {
303         Shell_MergeMenus(hMenu, hMenuStatic, UINT_MAX, 0, UINT_MAX, MM_DONTREMOVESEPS | MM_SUBMENUSHAVEIDS);
304 
305         ::DestroyMenu(hMenuStatic);
306 
307         hr = _IsBandDeletable(dwBandID);
308         if (FAILED_UNEXPECTEDLY(hr))
309             return hr;
310 
311         /* Remove the close item if it is not deletable */
312         if (hr == S_FALSE || (Band->dbi.dwModeFlags & DBIMF_UNDELETEABLE) != 0)
313             DeleteMenu(hMenu, IDM_BAND_CLOSE, MF_BYCOMMAND);
314 
315         if ((Band->dbi.dwMask & DBIM_TITLE) == 0)
316             DeleteMenu(hMenu, IDM_BAND_TITLE, MF_BYCOMMAND);
317         else
318             CheckMenuItem(hMenu, IDM_BAND_TITLE, Band->bHiddenTitle ? MF_UNCHECKED : MF_CHECKED);
319     }
320 
321     /* TODO: Query the menu of our site */
322 
323     UINT uCommand = ::TrackPopupMenuEx(hMenu, TPM_RETURNCMD, x, y, fRebarWindow, NULL);
324     if (uCommand < idBandLast)
325     {
326         CMINVOKECOMMANDINFO cmi = { sizeof(cmi), 0, fRebarWindow, MAKEINTRESOURCEA(uCommand)};
327         hr = pcm->InvokeCommand(&cmi);
328         if (FAILED_UNEXPECTEDLY(hr))
329             return hr;
330     }
331     else
332     {
333         if (uCommand == IDM_BAND_TITLE)
334         {
335             Band->bHiddenTitle = !Band->bHiddenTitle;
336 
337             hr = UpdateBand(dwBandID);
338             if (FAILED_UNEXPECTEDLY(hr))
339                 return hr;
340         }
341         else if(uCommand == IDM_BAND_CLOSE)
342         {
343             hr = RemoveBand(dwBandID);
344             if (FAILED_UNEXPECTEDLY(hr))
345                 return hr;
346         }
347     }
348 
349     return S_OK;
350 }
351 
352 struct CBandSiteBase::BandObject *CBandSiteBase::GetBandFromHwnd(HWND hwnd)
353 {
354     HRESULT                                 hRet;
355     HWND                                    hWndBand;
356     LONG                                    i;
357 
358     for (i = 0; i < fBandsAllocated; i++)
359     {
360         if (fBands[i].DeskBand != NULL)
361         {
362             ASSERT(fBands[i].OleWindow);
363 
364             hWndBand = NULL;
365             hRet = fBands[i].OleWindow->GetWindow(&hWndBand);
366             if (SUCCEEDED(hRet) && hWndBand == hwnd)
367                 return &fBands[i];
368         }
369     }
370 
371     return NULL;
372 }
373 
374 CBandSiteBase::~CBandSiteBase()
375 {
376 
377     TRACE("destroying %p\n", this);
378 
379     if (fRebarWindow != NULL)
380     {
381         DestroyWindow(fRebarWindow);
382         fRebarWindow = NULL;
383     }
384 
385     if (fBands != NULL)
386     {
387         for (INT i = 0; i < fBandsAllocated; i++)
388         {
389             if (fBands[i].DeskBand != NULL)
390                 FreeBand(&fBands[i]);
391         }
392         CoTaskMemFree(fBands);
393         fBands = NULL;
394     }
395 }
396 
397 HRESULT STDMETHODCALLTYPE CBandSiteBase::AddBand(IUnknown *punk)
398 {
399     LONG                                    NewAllocated;
400     struct BandObject                       *NewBand = NULL;
401     CComPtr<IDeskBand>                      DeskBand;
402     CComPtr<IObjectWithSite>                ObjWithSite;
403     CComPtr<IOleWindow>                     OleWindow;
404     CComPtr<IWinEventHandler>               WndEvtHandler;
405     REBARBANDINFOW                          rbi;
406     HRESULT                                 hRet;
407     UINT                                    uBand;
408 
409     TRACE("(%p, %p)\n", this, punk);
410 
411     if (punk == NULL || fRebarWindow == NULL)
412         return E_FAIL;
413 
414     hRet = punk->QueryInterface(IID_PPV_ARG(IDeskBand, &DeskBand));
415     if (!SUCCEEDED(hRet) || DeskBand == NULL)
416         goto Cleanup;
417     hRet = punk->QueryInterface(IID_PPV_ARG(IObjectWithSite, &ObjWithSite));
418     if (!SUCCEEDED(hRet) || ObjWithSite == NULL)
419         goto Cleanup;
420     hRet = punk->QueryInterface(IID_PPV_ARG(IOleWindow, &OleWindow));
421     if (!SUCCEEDED(hRet) || OleWindow == NULL)
422         goto Cleanup;
423     hRet = punk->QueryInterface(IID_PPV_ARG(IWinEventHandler, &WndEvtHandler));
424     if (!SUCCEEDED(hRet) || WndEvtHandler == NULL)
425         goto Cleanup;
426 
427     hRet = S_OK;
428     if (fBandsAllocated > fBandsCount)
429     {
430         /* Search for a free band object */
431         for (INT i = 0; i < fBandsAllocated; i++)
432         {
433             if (fBands[i].DeskBand == NULL)
434             {
435                 NewBand = &fBands[i];
436                 break;
437             }
438         }
439     }
440     else if (fBandsAllocated > 0)
441     {
442         ASSERT (fBands != NULL);
443 
444         /* Reallocate the band object array */
445         NewAllocated = fBandsAllocated + 8;
446         if (NewAllocated > 0xFFFF)
447             NewAllocated = 0xFFFF;
448         if (NewAllocated == fBandsAllocated)
449         {
450             hRet = E_OUTOFMEMORY;
451             goto Cleanup;
452         }
453 
454 
455         NewBand = static_cast<struct BandObject *>(CoTaskMemAlloc(NewAllocated * sizeof(struct BandObject)));
456         if (NewBand == NULL)
457         {
458             hRet = E_OUTOFMEMORY;
459             goto Cleanup;
460         }
461 
462         /* Copy the old array */
463         memcpy(NewBand, fBands, fBandsAllocated * sizeof(struct BandObject));
464 
465         /* Initialize the added bands */
466         memset(&NewBand[fBandsAllocated], 0, (NewAllocated - fBandsAllocated) * sizeof(struct BandObject));
467 
468         fBandsAllocated = NewAllocated;
469         CoTaskMemFree(fBands);
470         fBands = NewBand;
471     }
472     else
473     {
474         ASSERT(fBands == NULL);
475         ASSERT(fBandsAllocated == 0);
476         ASSERT(fBandsCount == 0);
477 
478         /* Allocate new array */
479         fBands = static_cast<struct BandObject *>(CoTaskMemAlloc(8 * sizeof(struct BandObject)));
480         if (fBands == NULL)
481         {
482             hRet = E_OUTOFMEMORY;
483             goto Cleanup;
484         }
485 
486         /* Initialize the added bands */
487         memset(fBands, 0, 8 * sizeof(struct BandObject));
488 
489         fBandsAllocated += 8;
490         NewBand = &fBands[0];
491     }
492 
493     if (SUCCEEDED(hRet))
494     {
495         ASSERT(NewBand != NULL);
496 
497         fBandsCount++;
498         NewBand->DeskBand = DeskBand.Detach();
499         NewBand->OleWindow = OleWindow.Detach();
500         NewBand->WndEvtHandler = WndEvtHandler.Detach();
501 
502         /* Create the ReBar band */
503         hRet = ObjWithSite->SetSite(static_cast<IOleWindow *>(this));
504         if (SUCCEEDED(hRet))
505         {
506             uBand = 0xffffffff;
507             if (SUCCEEDED(UpdateSingleBand(NewBand)))
508             {
509                 if (NewBand->dbi.dwMask & DBIM_MODEFLAGS)
510                 {
511                     if (NewBand->dbi.dwModeFlags & DBIMF_ADDTOFRONT)
512                         uBand = 0;
513                 }
514             }
515 
516             BuildRebarBandInfo(NewBand, &rbi);
517 
518             if (SUCCEEDED(NewBand->OleWindow->GetWindow(&rbi.hwndChild)) &&
519                 rbi.hwndChild != NULL)
520             {
521                 rbi.fMask |= RBBIM_CHILD;
522                 WARN ("ReBar band uses child window 0x%p\n", rbi.hwndChild);
523             }
524 
525             if (!SendMessageW(fRebarWindow, RB_INSERTBANDW, (WPARAM)uBand, reinterpret_cast<LPARAM>(&rbi)))
526                 return E_FAIL;
527 
528             hRet = (HRESULT)((USHORT)GetBandID(NewBand));
529         }
530         else
531         {
532             WARN("IBandSite::AddBand(): Call to IDeskBand::SetSite() failed: %x\n", hRet);
533 
534             /* Remove the band from the ReBar control */
535             BuildRebarBandInfo(NewBand, &rbi);
536             uBand = (UINT)SendMessageW(fRebarWindow, RB_IDTOINDEX, (WPARAM)rbi.wID, 0);
537             if (uBand != (UINT)-1)
538             {
539                 if (!SendMessageW(fRebarWindow, RB_DELETEBAND, (WPARAM)uBand, 0))
540                 {
541                     ERR("Failed to delete band!\n");
542                 }
543             }
544             else
545                 ERR("Failed to map band id to index!\n");
546 
547             FreeBand(NewBand);
548 
549             hRet = E_FAIL;
550             /* goto Cleanup; */
551         }
552     }
553 Cleanup:
554     return hRet;
555 }
556 
557 HRESULT STDMETHODCALLTYPE CBandSiteBase::EnumBands(UINT uBand, DWORD *pdwBandID)
558 {
559     DWORD                                   i;
560 
561     TRACE("(%p, %u, %p)\n", this, uBand, pdwBandID);
562 
563     if (uBand == 0xffffffff)
564         return (UINT)fBandsCount;
565 
566     if (uBand >= (UINT)fBandsCount)
567         return E_FAIL;
568 
569     for (i = 0; i < (DWORD)fBandsAllocated; i++)
570     {
571         if (fBands[i].DeskBand != NULL)
572         {
573             if (uBand == 0)
574             {
575                 *pdwBandID = i;
576                 return S_OK;
577             }
578 
579             uBand--;
580         }
581     }
582 
583     return E_FAIL;
584 }
585 
586 HRESULT STDMETHODCALLTYPE CBandSiteBase::QueryBand(DWORD dwBandID, IDeskBand **ppstb,
587     DWORD *pdwState, LPWSTR pszName, int cchName)
588 {
589     struct BandObject                       *Band;
590 
591     TRACE("(%p, %u, %p, %p, %p, %d)\n", this, dwBandID, ppstb, pdwState, pszName, cchName);
592 
593     Band = GetBandByID(dwBandID);
594     if (Band == NULL)
595         return E_FAIL;
596 
597     if (ppstb != NULL)
598     {
599         Band->DeskBand->AddRef();
600         *ppstb = Band->DeskBand;
601     }
602 
603     if (pdwState != NULL)
604     {
605         FIXME("IBandSite::QueryBand() requests band state!\n");
606         *pdwState = 0;
607     }
608 
609     if (pszName != NULL && cchName > 0)
610     {
611         FIXME("IBandSite::QueryBand() requests band name!\n");
612         pszName[0] = 0;
613     }
614     return S_OK;
615 }
616 
617 HRESULT STDMETHODCALLTYPE CBandSiteBase::SetBandState(DWORD dwBandID, DWORD dwMask, DWORD dwState)
618 {
619     struct BandObject                       *Band;
620 
621     TRACE("(%p, %u, %x, %x)\n", this, dwBandID, dwMask, dwState);
622 
623     Band = GetBandByID(dwBandID);
624     if (Band == NULL)
625         return E_FAIL;
626 
627     FIXME("Stub\n");
628     return E_NOTIMPL;
629 }
630 
631 HRESULT STDMETHODCALLTYPE CBandSiteBase::RemoveBand(DWORD dwBandID)
632 {
633     struct BandObject                       *Band;
634     UINT                                    uBand;
635 
636     TRACE("(%p, %u)\n", this, dwBandID);
637 
638     if (fRebarWindow == NULL)
639         return E_FAIL;
640 
641     Band = GetBandByID(dwBandID);
642     if (Band == NULL)
643         return E_FAIL;
644 
645     uBand = (UINT)SendMessageW(fRebarWindow, RB_IDTOINDEX, (WPARAM)GetBandID(Band), 0);
646     if (uBand != (UINT)-1)
647     {
648         if (!SendMessageW(fRebarWindow, RB_DELETEBAND, (WPARAM)uBand, 0))
649         {
650             ERR("Could not delete band!\n");
651         }
652     }
653     else
654         ERR("Could not map band id to index!\n");
655 
656     FreeBand(Band);
657     return S_OK;
658 }
659 
660 HRESULT STDMETHODCALLTYPE CBandSiteBase::GetBandObject(DWORD dwBandID, REFIID riid, VOID **ppv)
661 {
662     struct BandObject                       *Band;
663 
664     TRACE("(%p, %u, %s, %p)\n", this, dwBandID, debugstr_guid(&riid), ppv);
665 
666     Band = GetBandByID(dwBandID);
667     if (Band == NULL)
668     {
669         *ppv = NULL;
670         return E_FAIL;
671     }
672 
673     return Band->DeskBand->QueryInterface(riid, ppv);
674 }
675 
676 HRESULT STDMETHODCALLTYPE CBandSiteBase::SetBandSiteInfo(const BANDSITEINFO *pbsinfo)
677 {
678     FIXME("(%p, %p)\n", this, pbsinfo);
679     return E_NOTIMPL;
680 }
681 
682 HRESULT STDMETHODCALLTYPE CBandSiteBase::GetBandSiteInfo(BANDSITEINFO *pbsinfo)
683 {
684     FIXME("(%p, %p)\n", this, pbsinfo);
685     return E_NOTIMPL;
686 }
687 
688 HRESULT STDMETHODCALLTYPE CBandSiteBase::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plrResult)
689 {
690     struct BandObject                       *Band;
691 
692     TRACE("(%p, %p, %u, %p, %p, %p)\n", this, hWnd, uMsg, wParam, lParam, plrResult);
693 
694     *plrResult = 0;
695     if (fRebarWindow == NULL)
696         return E_FAIL;
697 
698     if (uMsg == WM_CONTEXTMENU)
699     {
700         HRESULT hr = OnContextMenu(hWnd, uMsg, wParam, lParam, plrResult);
701         if (FAILED_UNEXPECTEDLY(hr))
702             return hr;
703 
704         return S_OK;
705     }
706     else if (uMsg == WM_COMMAND && lParam)
707     {
708         hWnd = reinterpret_cast<HWND>(lParam);
709     }
710     else if (uMsg == WM_NOTIFY)
711     {
712         NMHDR* pnmhdr = reinterpret_cast<NMHDR*>(lParam);
713         if (pnmhdr->hwndFrom != fRebarWindow)
714         {
715             hWnd = pnmhdr->hwndFrom;
716         }
717         else
718         {
719             for (int i = 0; i < fBandsAllocated; i++)
720             {
721                 if (fBands[i].WndEvtHandler && fBands[i].OleWindow)
722                 {
723                     HWND hwndBand;
724                     if (SUCCEEDED(fBands[i].OleWindow->GetWindow(&hwndBand)))
725                     {
726                         fBands[i].WndEvtHandler->OnWinEvent(hwndBand, uMsg, wParam, lParam, plrResult);
727                     }
728                 }
729             }
730             return S_OK;
731         }
732     }
733 
734     Band = GetBandFromHwnd(hWnd);
735     if (Band != NULL)
736     {
737         return Band->WndEvtHandler->OnWinEvent(hWnd, uMsg, wParam, lParam, plrResult);
738     }
739 
740     return E_FAIL;
741 }
742 
743 HRESULT STDMETHODCALLTYPE CBandSiteBase::IsWindowOwner(HWND hWnd)
744 {
745     struct BandObject                       *Band;
746 
747     TRACE("(%p, %p)\n", this, hWnd);
748 
749     if (fRebarWindow == NULL)
750         return E_FAIL;
751 
752     Band = GetBandFromHwnd(hWnd);
753     if (Band != NULL)
754         return S_OK;
755 
756     return S_FALSE;
757 }
758 
759 HRESULT STDMETHODCALLTYPE CBandSiteBase::GetWindow(HWND *phWnd)
760 {
761     TRACE("(%p, %p)\n", this, phWnd);
762 
763     *phWnd = fRebarWindow;
764     if (fRebarWindow != NULL)
765         return S_OK;
766 
767     return E_FAIL;
768 }
769 
770 HRESULT STDMETHODCALLTYPE CBandSiteBase::ContextSensitiveHelp(BOOL fEnterMode)
771 {
772     FIXME("(%p, %d)\n", this, fEnterMode);
773     return E_NOTIMPL;
774 }
775 
776 HRESULT STDMETHODCALLTYPE CBandSiteBase::SetDeskBarSite(IUnknown *pUnk)
777 {
778     HWND                                    hWndParent;
779     HRESULT                                 hRet;
780     DWORD                                   style;
781 
782     TRACE("(%p, %p)\n", this, pUnk);
783 
784     fOleWindow.Release();
785 
786     hRet = pUnk->QueryInterface(IID_PPV_ARG(IOleWindow, &fOleWindow));
787     if (FAILED_UNEXPECTEDLY(hRet))
788         return E_FAIL;
789 
790     hRet = fOleWindow->GetWindow(&hWndParent);
791     if (FAILED_UNEXPECTEDLY(hRet))
792         return E_FAIL;
793 
794     style = WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | RBS_VARHEIGHT | RBS_AUTOSIZE |
795         RBS_BANDBORDERS | CCS_NODIVIDER | /*CCS_NORESIZE |*/ CCS_NOPARENTALIGN;
796 
797     fRebarWindow = CreateWindowExW(WS_EX_TOOLWINDOW,
798                                    REBARCLASSNAMEW,
799                                    NULL,
800                                    style,
801                                    0, 0, 0, 0,
802                                    hWndParent,
803                                    NULL,
804                                    _AtlBaseModule.GetModuleInstance(),
805                                    NULL);
806     if (fRebarWindow == NULL)
807     {
808         fOleWindow.Release();
809         WARN("IDeskbarClient::SetDeskBarSite() failed to create ReBar control!\n");
810         return E_FAIL;
811     }
812 
813     return S_OK;
814 }
815 
816 HRESULT STDMETHODCALLTYPE CBandSiteBase::SetModeDBC(DWORD dwMode)
817 {
818     LONG                                    dwStyle;
819     LONG                                    dwPrevStyle;
820 
821     TRACE("(%p, %x)\n", this, dwMode);
822 
823     if (fRebarWindow == NULL)
824         return E_FAIL;
825 
826     dwStyle = dwPrevStyle = GetWindowLongPtr(fRebarWindow, GWL_STYLE);
827     if (dwMode & DBIF_VIEWMODE_VERTICAL)
828         dwStyle |= CCS_VERT;
829 
830     if (dwMode & ~DBIF_VIEWMODE_VERTICAL)
831         FIXME("IDeskBarClient::SetModeDBC() unhandled modes: %x\n", dwStyle & ~DBIF_VIEWMODE_VERTICAL);
832 
833     if (dwStyle != dwPrevStyle)
834     {
835         SetWindowLongPtr(fRebarWindow, GWL_STYLE, dwPrevStyle);
836     }
837 
838     return S_OK;
839 }
840 
841 HRESULT STDMETHODCALLTYPE CBandSiteBase::UIActivateDBC(DWORD dwState)
842 {
843     TRACE("(%p, %x)\n", this, dwState);
844 
845     if (fRebarWindow == NULL)
846         return E_FAIL;
847 
848     ShowWindow(fRebarWindow, (dwState & DBC_SHOW) ? SW_SHOW : SW_HIDE);
849     //FIXME: Properly notify bands?
850     return S_OK;
851 }
852 
853 HRESULT STDMETHODCALLTYPE CBandSiteBase::GetSize(DWORD unknown1, LPRECT unknown2)
854 {
855     FIXME("(%p, %x, %p)\n", this, unknown1, unknown2);
856     return E_NOTIMPL;
857 }
858 
859 HRESULT STDMETHODCALLTYPE CBandSiteBase::QueryStatus(const GUID *pguidCmdGroup,
860     DWORD cCmds, OLECMD *prgCmds, OLECMDTEXT *pCmdText)
861 {
862     FIXME("(%p, %p, %u, %p, %p)\n", this, pguidCmdGroup, cCmds, prgCmds, pCmdText);
863     return E_NOTIMPL;
864 }
865 
866 HRESULT STDMETHODCALLTYPE CBandSiteBase::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
867     DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
868 {
869     HRESULT                                 hRet = S_OK;
870 
871     TRACE("(%p, %p, %u, %u, %p, %p)\n", this, pguidCmdGroup, nCmdID, nCmdExecOpt, pvaIn, pvaOut);
872 
873     if (fRebarWindow == NULL)
874         return E_FAIL;
875 
876     if (IsEqualIID(*pguidCmdGroup, IID_IDeskBand))
877     {
878         switch (nCmdID)
879         {
880             case DBID_BANDINFOCHANGED:
881                 if (pvaIn == NULL)
882                     hRet = UpdateAllBands();
883                 else
884                 {
885                     /* Update a single band */
886                     if (pvaIn->n1.n2.vt == VT_I4)
887                         hRet = UpdateBand(pvaIn->n1.n2.n3.lVal);
888                     else
889                         hRet = E_FAIL;
890                 }
891                 break;
892 
893             case DBID_SHOWONLY:
894             case DBID_MAXIMIZEBAND:
895             case DBID_PUSHCHEVRON:
896                 FIXME("IOleCommandTarget::Exec(): Unsupported command ID %d\n", nCmdID);
897                 return E_NOTIMPL;
898             default:
899                 return E_FAIL;
900         }
901         return hRet;
902     }
903     else
904         WARN("IOleCommandTarget::Exec(): Unsupported command group GUID\n");
905 
906     return E_NOTIMPL;
907 }
908 
909 HRESULT STDMETHODCALLTYPE CBandSiteBase::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
910 {
911     return E_NOTIMPL;
912 }
913 
914 HRESULT STDMETHODCALLTYPE CBandSiteBase::HasFocusIO()
915 {
916     return E_NOTIMPL;
917 }
918 
919 HRESULT STDMETHODCALLTYPE CBandSiteBase::TranslateAcceleratorIO(LPMSG lpMsg)
920 {
921     return E_NOTIMPL;
922 }
923 
924 HRESULT STDMETHODCALLTYPE CBandSiteBase::OnFocusChangeIS(struct IUnknown *paramC, int param10)
925 {
926     return E_NOTIMPL;
927 }
928 
929 HRESULT STDMETHODCALLTYPE CBandSiteBase::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
930 {
931     return E_NOTIMPL;
932 }
933 
934 HRESULT STDMETHODCALLTYPE CBandSiteBase::GetClassID(CLSID *pClassID)
935 {
936     return E_NOTIMPL;
937 }
938 
939 HRESULT STDMETHODCALLTYPE CBandSiteBase::IsDirty()
940 {
941     return E_NOTIMPL;
942 }
943 
944 HRESULT STDMETHODCALLTYPE CBandSiteBase::Load(IStream *pStm)
945 {
946     return E_NOTIMPL;
947 }
948 
949 HRESULT STDMETHODCALLTYPE CBandSiteBase::Save(IStream *pStm, BOOL fClearDirty)
950 {
951     return E_NOTIMPL;
952 }
953 
954 HRESULT STDMETHODCALLTYPE CBandSiteBase::GetSizeMax(ULARGE_INTEGER *pcbSize)
955 {
956     return E_NOTIMPL;
957 }
958 
959 HRESULT STDMETHODCALLTYPE CBandSiteBase::DragEnter(
960     IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
961 {
962     return E_NOTIMPL;
963 }
964 
965 HRESULT STDMETHODCALLTYPE CBandSiteBase::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
966 {
967     return E_NOTIMPL;
968 }
969 
970 HRESULT STDMETHODCALLTYPE CBandSiteBase::DragLeave()
971 {
972     return E_NOTIMPL;
973 }
974 
975 HRESULT STDMETHODCALLTYPE CBandSiteBase::Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
976 {
977     return E_NOTIMPL;
978 }
979 
980 HRESULT STDMETHODCALLTYPE CBandSiteBase::LoadFromStreamBS(IStream *, const GUID &, void **)
981 {
982     return E_NOTIMPL;
983 }
984 
985 HRESULT STDMETHODCALLTYPE CBandSiteBase::SaveToStreamBS(IUnknown *, IStream *)
986 {
987     return E_NOTIMPL;
988 }
989 
990 extern "C"
991 HRESULT WINAPI RSHELL_CBandSite_CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void **ppv)
992 {
993     return CBandSite::_CreatorClass::CreateInstance(pUnkOuter, riid, ppv);
994 }
995