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