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