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 662