670e647a23c660d05048490845d561a59b622db3
[reactos.git] / dll / win32 / browseui / shellbars / CISFBand.cpp
1 /*
2 * PROJECT: ReactOS shell extensions
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/shellext/qcklnch/CISFBand.cpp
5 * PURPOSE: Quick Launch Toolbar (Taskbar Shell Extension)
6 * PROGRAMMERS: Shriraj Sawant a.k.a SR13 <sr.official@hotmail.com>
7 */
8
9 #include "shellbars.h"
10
11 #include <commoncontrols.h>
12 #include <shellapi.h>
13
14 /*
15 TODO:
16 ** drag and drop support
17 ** tooltips
18 ** handle change notifications
19 ** Fix position of the items context menu
20 ** Implement responding to theme change
21 */
22
23 //*****************************************************************************************
24 // *** CISFBand ***
25
26 CISFBand::CISFBand() :
27 m_BandID(0),
28 m_pidl(NULL),
29 m_textFlag(true),
30 m_iconFlag(true),
31 m_QLaunch(false)
32 {
33 }
34
35 CISFBand::~CISFBand()
36 {
37 CloseDW(0);
38 }
39
40 // Toolbar
41 /*++
42 * @name CreateSimpleToolbar
43 *
44 * Creates a toolbar and fills it up with buttons for enumerated objects.
45 *
46 * @param hWndParent
47 * Handle to the parent window, which receives the appropriate messages from child toolbar.
48 *
49 * @return The error code.
50 *
51 *--*/
52 HRESULT CISFBand::CreateSimpleToolbar(HWND hWndParent)
53 {
54 // Declare and initialize local constants.
55 const DWORD buttonStyles = BTNS_AUTOSIZE;
56
57 // Create the toolbar.
58 m_hWnd = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
59 WS_CHILD | TBSTYLE_FLAT | TBSTYLE_LIST | CCS_NORESIZE | CCS_NODIVIDER, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0,
60 hWndParent, NULL, 0, NULL);
61 if (m_hWnd == NULL)
62 return E_FAIL;
63
64 if (!m_textFlag)
65 SendMessage(m_hWnd, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_MIXEDBUTTONS);
66
67 // Set the image list.
68 HIMAGELIST* piml;
69 HRESULT hr = SHGetImageList(SHIL_SMALL, IID_IImageList, (void**)&piml);
70 if (FAILED_UNEXPECTEDLY(hr))
71 {
72 DestroyWindow();
73 return hr;
74 }
75 SendMessage(m_hWnd, TB_SETIMAGELIST, 0, (LPARAM)piml);
76
77 // Enumerate objects
78 CComPtr<IEnumIDList> pEndl;
79 LPITEMIDLIST pidl;
80 STRRET stret;
81 hr = m_pISF->EnumObjects(0, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS, &pEndl);
82 if (FAILED_UNEXPECTEDLY(hr))
83 {
84 DestroyWindow();
85 return hr;
86 }
87
88 for (int i=0; pEndl->Next(1, &pidl, NULL) != S_FALSE; i++)
89 {
90 WCHAR sz[MAX_PATH];
91 int index = SHMapPIDLToSystemImageListIndex(m_pISF, pidl, NULL);
92 hr = m_pISF->GetDisplayNameOf(pidl, SHGDN_NORMAL, &stret);
93 if (FAILED_UNEXPECTEDLY(hr))
94 {
95 StringCchCopyW(sz, MAX_PATH, L"<Unknown-Name>");
96 }
97 else
98 StrRetToBuf(&stret, pidl, sz, _countof(sz));
99
100 TBBUTTON tb = { MAKELONG(index, 0), i, TBSTATE_ENABLED, buttonStyles,{ 0 }, (DWORD_PTR)pidl, (INT_PTR)sz };
101 SendMessage(m_hWnd, TB_INSERTBUTTONW, i, (LPARAM)&tb);
102 }
103
104 // Resize the toolbar, and then show it.
105 SendMessage(m_hWnd, TB_AUTOSIZE, 0, 0);
106
107 return hr;
108 }
109
110 /*****************************************************************************/
111
112 // *** IObjectWithSite ***
113 STDMETHODIMP CISFBand::SetSite(IUnknown *pUnkSite)
114 {
115 HRESULT hr;
116 HWND hwndParent;
117
118 TRACE("CISFBand::SetSite(0x%p)\n", pUnkSite);
119
120 hr = IUnknown_GetWindow(pUnkSite, &hwndParent);
121 if (FAILED(hr))
122 {
123 TRACE("Querying site window failed: 0x%x\n", hr);
124 return hr;
125 }
126 m_Site = pUnkSite;
127
128 hr = CreateSimpleToolbar(hwndParent);
129 if (FAILED_UNEXPECTEDLY(hr))
130 return hr;
131
132 return S_OK;
133 }
134
135 STDMETHODIMP CISFBand::GetSite(IN REFIID riid, OUT VOID **ppvSite)
136 {
137 TRACE("CISFBand::GetSite(0x%p,0x%p)\n", riid, ppvSite);
138
139 HRESULT hr;
140 if (m_Site != NULL)
141 {
142 hr = m_Site->QueryInterface(riid, ppvSite);
143 if (FAILED(hr)) return hr;
144 }
145
146 *ppvSite = NULL;
147 return E_FAIL;
148 }
149
150 /*****************************************************************************/
151 // *** IDeskBand ***
152 STDMETHODIMP CISFBand::GetWindow(OUT HWND *phwnd)
153 {
154 if (!m_hWnd)
155 return E_FAIL;
156 if (!phwnd)
157 return E_POINTER;
158 *phwnd = m_hWnd;
159
160 return S_OK;
161 }
162
163 STDMETHODIMP CISFBand::ContextSensitiveHelp(IN BOOL fEnterMode)
164 {
165 /* FIXME: Implement */
166 return E_NOTIMPL;
167 }
168
169 STDMETHODIMP CISFBand::ShowDW(IN BOOL bShow)
170 {
171 if (m_hWnd)
172 {
173 ShowWindow(bShow ? SW_SHOW : SW_HIDE);
174 return S_OK;
175 }
176
177 return E_FAIL;
178 }
179
180 STDMETHODIMP CISFBand::CloseDW(IN DWORD dwReserved)
181 {
182 if (m_hWnd)
183 {
184 ShowWindow(SW_HIDE);
185
186 TBBUTTON tb;
187 for (int i = 0; SendMessage(m_hWnd, TB_GETBUTTON, i, (LPARAM)&tb); i++)
188 {
189 CoTaskMemFree((LPITEMIDLIST)tb.dwData);
190 }
191
192 DestroyWindow();
193 m_hWnd = NULL;
194 return S_OK;
195 }
196
197 return E_FAIL;
198 }
199
200 STDMETHODIMP CISFBand::ResizeBorderDW(LPCRECT prcBorder, IUnknown *punkToolbarSite, BOOL fReserved)
201 {
202 /* No need to implement this method */
203
204 return E_NOTIMPL;
205 }
206
207 STDMETHODIMP CISFBand::GetBandInfo(IN DWORD dwBandID, IN DWORD dwViewMode, IN OUT DESKBANDINFO *pdbi)
208 {
209 TRACE("CTaskBand::GetBandInfo(0x%x,0x%x,0x%p) hWnd=0x%p\n", dwBandID, dwViewMode, pdbi, m_hWnd);
210
211 if (m_hWnd && pdbi)
212 {
213 m_BandID = dwBandID;
214
215 RECT actualRect;
216 POINTL actualSize;
217 POINTL idealSize;
218 POINTL maxSize;
219 POINTL itemSize;
220
221 GetWindowRect(&actualRect);
222 actualSize.x = actualRect.right - actualRect.left;
223 actualSize.y = actualRect.bottom - actualRect.top;
224
225 // Obtain the ideal size, to be used as min and max
226 SendMessageW(m_hWnd, TB_AUTOSIZE, 0, 0);
227 SendMessageW(m_hWnd, TB_GETMAXSIZE, 0, reinterpret_cast<LPARAM>(&maxSize));
228
229 idealSize = maxSize;
230 SendMessageW(m_hWnd, TB_GETIDEALSIZE, FALSE, reinterpret_cast<LPARAM>(&idealSize));
231
232 // Obtain the button size, to be used as the integral size
233 DWORD size = SendMessageW(m_hWnd, TB_GETBUTTONSIZE, 0, 0);
234 itemSize.x = GET_X_LPARAM(size);
235 itemSize.y = GET_Y_LPARAM(size);
236
237 if (pdbi->dwMask & DBIM_MINSIZE)
238 {
239 if (m_QLaunch)
240 pdbi->ptMinSize.x = idealSize.x;
241 else
242 pdbi->ptMinSize.x = -1;
243 pdbi->ptMinSize.y = idealSize.y;
244 }
245 if (pdbi->dwMask & DBIM_MAXSIZE)
246 {
247 pdbi->ptMaxSize = maxSize;
248 }
249 if (pdbi->dwMask & DBIM_INTEGRAL)
250 {
251 pdbi->ptIntegral = itemSize;
252 }
253 if (pdbi->dwMask & DBIM_ACTUAL)
254 {
255 pdbi->ptActual = actualSize;
256 }
257 if (pdbi->dwMask & DBIM_TITLE)
258 {
259 if (m_QLaunch || !ILGetDisplayNameEx(NULL, m_pidl, pdbi->wszTitle, ILGDN_INFOLDER))
260 {
261 pdbi->dwMask &= ~DBIM_TITLE;
262 }
263 }
264 if (pdbi->dwMask & DBIM_MODEFLAGS)
265 {
266 pdbi->dwModeFlags = DBIMF_NORMAL | DBIMF_VARIABLEHEIGHT | DBIMF_USECHEVRON | DBIMF_NOMARGINS | DBIMF_BKCOLOR;
267 if (m_QLaunch)
268 {
269 pdbi->dwModeFlags |= DBIMF_ADDTOFRONT;
270 }
271 }
272 if (pdbi->dwMask & DBIM_BKCOLOR)
273 pdbi->dwMask &= ~DBIM_BKCOLOR;
274
275 return S_OK;
276 }
277
278 return E_FAIL;
279 }
280
281 /*****************************************************************************/
282 // *** IPersistStream ***
283 STDMETHODIMP CISFBand::GetClassID(OUT CLSID *pClassID)
284 {
285 *pClassID = CLSID_ISFBand;
286
287 return S_OK;
288 }
289
290 STDMETHODIMP CISFBand::IsDirty()
291 {
292 /* The object hasn't changed since the last save! */
293
294 return S_FALSE;
295 }
296
297 STDMETHODIMP CISFBand::Load(IN IStream *pStm)
298 {
299 TRACE("CISFBand::Load called\n");
300 /* Nothing to do */
301
302 return S_OK;
303 }
304
305 STDMETHODIMP CISFBand::Save(IN IStream *pStm, IN BOOL fClearDirty)
306 {
307 /* Nothing to do */
308
309 return S_OK;
310 }
311
312 STDMETHODIMP CISFBand::GetSizeMax(OUT ULARGE_INTEGER *pcbSize)
313 {
314 TRACE("CISFBand::GetSizeMax called\n");
315
316 return S_OK;
317 }
318
319 /*****************************************************************************/
320 // *** IWinEventHandler ***
321 STDMETHODIMP CISFBand::ContainsWindow(IN HWND hWnd)
322 {
323 if (hWnd == m_hWnd || IsChild(hWnd))
324 {
325 TRACE("CISFBand::ContainsWindow(0x%p) returns S_OK\n", hWnd);
326 return S_OK;
327 }
328
329 return S_FALSE;
330 }
331
332 STDMETHODIMP CISFBand::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
333 {
334 switch (uMsg)
335 {
336 case WM_COMMAND:
337 {
338 TBBUTTON tb;
339 bool chk = SendMessage(m_hWnd, TB_GETBUTTON, LOWORD(wParam), (LPARAM)&tb);
340 if (chk)
341 SHInvokeDefaultCommand(m_hWnd, m_pISF, (LPITEMIDLIST)tb.dwData);
342
343 *theResult = TRUE;
344 break;
345 }
346 case WM_NOTIFY:
347 {
348 switch (((LPNMHDR)lParam)->code)
349 {
350 case NM_RCLICK:
351 {
352 HRESULT hr;
353 POINT pt = ((LPNMMOUSE)lParam)->pt;
354 CComPtr<IContextMenu> picm;
355 HMENU fmenu = CreatePopupMenu();
356 TBBUTTON tb;
357
358 bool chk = SendMessage(m_hWnd, TB_GETBUTTON, ((LPNMMOUSE)lParam)->dwItemSpec, (LPARAM)&tb);
359 LPITEMIDLIST pidl = (LPITEMIDLIST)tb.dwData;
360
361 if (chk)
362 {
363 ClientToScreen(&pt);
364 hr = m_pISF->GetUIObjectOf(m_hWnd, 1, &pidl, IID_NULL_PPV_ARG(IContextMenu, &picm));
365 if (FAILED_UNEXPECTEDLY(hr))
366 return hr;
367
368 hr = picm->QueryContextMenu(fmenu, 0, 1, 0x7FFF, CMF_DEFAULTONLY);
369 if (FAILED_UNEXPECTEDLY(hr))
370 return hr;
371
372 int id = TrackPopupMenuEx(fmenu, TPM_LEFTALIGN | TPM_BOTTOMALIGN | TPM_RETURNCMD, pt.x, pt.y, m_hWnd, 0);
373 if (id > 0)
374 {
375 CMINVOKECOMMANDINFOEX info = { 0 };
376 info.cbSize = sizeof(info);
377 info.fMask = CMIC_MASK_PTINVOKE;
378 if (GetKeyState(VK_CONTROL) < 0)
379 {
380 info.fMask |= CMIC_MASK_CONTROL_DOWN;
381 }
382 if (GetKeyState(VK_SHIFT) < 0)
383 {
384 info.fMask |= CMIC_MASK_SHIFT_DOWN;
385 }
386 info.hwnd = m_hWnd;
387 info.lpVerb = MAKEINTRESOURCEA(id - 1);
388 info.nShow = SW_SHOWNORMAL;
389 info.ptInvoke = pt;
390 picm->InvokeCommand((LPCMINVOKECOMMANDINFO)&info);
391 }
392 }
393 DestroyMenu(fmenu);
394
395 *theResult = TRUE;
396 break;
397 }
398 default:
399 *theResult = FALSE;
400 }
401
402 break;
403 }
404 default:
405 *theResult = FALSE;
406 }
407
408 return S_OK;
409 }
410
411 STDMETHODIMP CISFBand::IsWindowOwner(HWND hWnd)
412 {
413 return (hWnd == m_hWnd) ? S_OK : S_FALSE;
414 }
415
416 /*****************************************************************************/
417 // *** IOleCommandTarget methods ***
418 STDMETHODIMP CISFBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText)
419 {
420 UNIMPLEMENTED;
421
422 return E_NOTIMPL;
423 }
424
425 STDMETHODIMP CISFBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
426 {
427 if (IsEqualIID(*pguidCmdGroup, IID_IBandSite))
428 {
429 return S_OK;
430 }
431
432 if (IsEqualIID(*pguidCmdGroup, IID_IDeskBand))
433 {
434 return S_OK;
435 }
436
437 UNIMPLEMENTED;
438
439 return E_NOTIMPL;
440 }
441
442 /*****************************************************************************/
443 // *** IShellFolderBand ***
444 STDMETHODIMP CISFBand::GetBandInfoSFB(PBANDINFOSFB pbi)
445 {
446 if (pbi->dwMask == ISFB_MASK_IDLIST)
447 {
448 pbi->pidl = ILClone(m_pidl);
449 if (!pbi->pidl)
450 return E_OUTOFMEMORY;
451 return S_OK;
452 }
453
454 return E_NOTIMPL;
455 }
456
457 STDMETHODIMP CISFBand::InitializeSFB(IShellFolder *psf, PCIDLIST_ABSOLUTE pidl)
458 {
459 HRESULT hr;
460
461 if (!psf && !pidl)
462 return E_INVALIDARG;
463
464 if (psf && pidl)
465 return E_INVALIDARG;
466
467 if (pidl != NULL)
468 {
469 CComPtr<IShellFolder> psfDesktop;
470 hr = SHGetDesktopFolder(&psfDesktop);
471 if (FAILED_UNEXPECTEDLY(hr))
472 return hr;
473
474 if (_ILIsDesktop(pidl))
475 {
476 m_pISF = psfDesktop;
477 }
478 else
479 {
480 hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &m_pISF));
481 if (FAILED_UNEXPECTEDLY(hr))
482 return hr;
483 }
484
485 m_pidl = ILClone(pidl);
486 }
487
488 if (psf != NULL)
489 {
490 CComPtr<IPersistFolder2> ppf2;
491 hr = psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
492 if (FAILED_UNEXPECTEDLY(hr))
493 return hr;
494
495 hr = ppf2->GetCurFolder(&m_pidl);
496 if (FAILED_UNEXPECTEDLY(hr))
497 return hr;
498
499 m_pISF = psf;
500 }
501
502 return S_OK;
503 }
504
505 STDMETHODIMP CISFBand::SetBandInfoSFB( PBANDINFOSFB pbi)
506 {
507 if ((pbi->dwMask & ISFB_MASK_STATE) &&
508 (pbi->dwState & ISFB_STATE_QLINKSMODE) &&
509 (pbi->dwStateMask & ISFB_STATE_QLINKSMODE))
510 {
511 m_QLaunch = true;
512 m_textFlag = false;
513 if (m_hWnd)
514 SendMessage(m_hWnd, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_MIXEDBUTTONS);
515 }
516
517 return E_NOTIMPL;
518 }
519
520 /*****************************************************************************/
521 // *** IContextMenu ***
522 STDMETHODIMP CISFBand::GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax)
523 {
524 /*HRESULT hr = E_INVALIDARG;
525
526 if (idCmd == IDM_DISPLAY)
527 {
528 switch (uFlags)
529 {
530 case GCS_HELPTEXTW:
531 // Only useful for pre-Vista versions of Windows that
532 // have a Status bar.
533 hr = StringCchCopyW(reinterpret_cast<PWSTR>(pszName),
534 cchMax,
535 L"Display File Name");
536 break;
537
538 case GCS_VERBW:
539 // GCS_VERBW is an optional feature that enables a caller
540 // to discover the canonical name for the verb that is passed in
541 // through idCommand.
542 hr = StringCchCopyW(reinterpret_cast<PWSTR>(pszName),
543 cchMax,
544 L"DisplayFileName");
545 break;
546 }
547 }
548 return hr; */
549
550 return S_OK;
551 }
552
553 STDMETHODIMP CISFBand::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
554 {
555 if (!HIWORD(pici->lpVerb))
556 {
557 switch (LOWORD(pici->lpVerb))
558 {
559 case IDM_LARGE_ICONS:
560 {
561 m_iconFlag = false;
562
563 HIMAGELIST* piml = (HIMAGELIST*) SendMessage(m_hWnd, TB_GETIMAGELIST, 0, 0);
564 HRESULT hr = SHGetImageList(SHIL_LARGE, IID_IImageList, (void**)&piml);
565 if (FAILED_UNEXPECTEDLY(hr)) return hr;
566 SendMessage(m_hWnd, TB_SETIMAGELIST, 0, (LPARAM)piml);
567 hr = IUnknown_Exec(m_Site, IID_IDeskBand, DBID_BANDINFOCHANGED, 0, NULL, NULL);
568 if (FAILED_UNEXPECTEDLY(hr)) return hr;
569 break;
570 }
571 case IDM_SMALL_ICONS:
572 {
573 m_iconFlag = true;
574
575 HIMAGELIST* piml = (HIMAGELIST*)SendMessage(m_hWnd, TB_GETIMAGELIST, 0, 0);
576 HRESULT hr = SHGetImageList(SHIL_SMALL, IID_IImageList, (void**)&piml);
577 if (FAILED_UNEXPECTEDLY(hr)) return hr;
578 SendMessage(m_hWnd, TB_SETIMAGELIST, 0, (LPARAM)piml);
579 hr = IUnknown_Exec(m_Site, IID_IDeskBand, DBID_BANDINFOCHANGED, 0, NULL, NULL);
580 if (FAILED_UNEXPECTEDLY(hr)) return hr;
581 break;
582 }
583 case IDM_OPEN_FOLDER:
584 {
585 SHELLEXECUTEINFO shexinfo;
586
587 memset(&shexinfo, 0x0, sizeof(shexinfo));
588
589 shexinfo.cbSize = sizeof(shexinfo);
590 shexinfo.fMask = SEE_MASK_IDLIST;
591 shexinfo.lpVerb = _T("open");
592 shexinfo.lpIDList = m_pidl;
593 shexinfo.nShow = SW_SHOW;
594
595 if (!ShellExecuteEx(&shexinfo))
596 return E_FAIL;
597
598 break;
599 }
600 case IDM_SHOW_TEXT:
601 {
602 if (m_textFlag)
603 {
604 m_textFlag = false;
605 SendMessage(m_hWnd, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_MIXEDBUTTONS);
606 HRESULT hr = IUnknown_Exec(m_Site, IID_IDeskBand, DBID_BANDINFOCHANGED, 0, NULL, NULL);
607 if (FAILED_UNEXPECTEDLY(hr)) return hr;
608 }
609 else
610 {
611 m_textFlag = true;
612 SendMessage(m_hWnd, TB_SETEXTENDEDSTYLE, 0, 0);
613 HRESULT hr = IUnknown_Exec(m_Site, IID_IDeskBand, DBID_BANDINFOCHANGED, 0, NULL, NULL);
614 if (FAILED_UNEXPECTEDLY(hr)) return hr;
615 }
616 break;
617 }
618 default:
619 return E_FAIL;
620 }
621 }
622
623 return S_OK;
624 }
625
626 STDMETHODIMP CISFBand::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
627 {
628 HMENU qMenu = LoadMenu(GetModuleHandleW(L"browseui.dll"), MAKEINTRESOURCE(IDM_POPUPMENU));
629
630 if(m_textFlag)
631 CheckMenuItem(qMenu, IDM_SHOW_TEXT, MF_CHECKED);
632 else
633 CheckMenuItem(qMenu, IDM_SHOW_TEXT, MF_UNCHECKED);
634
635 if (m_iconFlag)
636 {
637 CheckMenuItem(qMenu, IDM_SMALL_ICONS, MF_CHECKED);
638 CheckMenuItem(qMenu, IDM_LARGE_ICONS, MF_UNCHECKED);
639 }
640 else
641 {
642 CheckMenuItem(qMenu, IDM_LARGE_ICONS, MF_CHECKED);
643 CheckMenuItem(qMenu, IDM_SMALL_ICONS, MF_UNCHECKED);
644 }
645
646 if (_ILIsDesktop(m_pidl))
647 DeleteMenu(qMenu, IDM_OPEN_FOLDER, MF_BYCOMMAND);
648
649 UINT idMax = Shell_MergeMenus(hmenu, GetSubMenu(qMenu, 0), indexMenu, idCmdFirst, idCmdLast, MM_SUBMENUSHAVEIDS);
650 DestroyMenu(qMenu);
651 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(idMax - idCmdFirst +1));
652 }
653
654 /*****************************************************************************/
655 // C Constructor
656 extern "C"
657 HRESULT WINAPI RSHELL_CISFBand_CreateInstance(REFIID riid, void** ppv)
658 {
659 return ShellObjectCreator<CISFBand>(riid, ppv);
660 }
661