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