1 /* 2 * ReactOS Explorer 3 * 4 * Copyright 2009 Andrew Hill <ash77 at domain reactos.org> 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20 21 /* 22 This class handles the combo box of the address band. 23 */ 24 25 #include "precomp.h" 26 27 /* 28 TODO: 29 Add drag and drop of icon in edit box 30 Handle change notifies to update appropriately 31 */ 32 33 CAddressEditBox::CAddressEditBox() : 34 fCombobox(NULL, this, 1), 35 fEditWindow(NULL, this, 1), 36 fSite(NULL), 37 pidlLastParsed(NULL) 38 { 39 } 40 41 CAddressEditBox::~CAddressEditBox() 42 { 43 if (pidlLastParsed) 44 ILFree(pidlLastParsed); 45 } 46 47 HRESULT STDMETHODCALLTYPE CAddressEditBox::SetOwner(IUnknown *pOwner) 48 { 49 if (!pOwner) 50 { 51 CComPtr<IBrowserService> browserService; 52 HRESULT hResult = IUnknown_QueryService(fSite, SID_SShellBrowser, IID_PPV_ARG(IBrowserService, &browserService)); 53 if (SUCCEEDED(hResult)) 54 AtlUnadvise(browserService, DIID_DWebBrowserEvents, fAdviseCookie); 55 fSite = NULL; 56 } 57 // connect to browser connection point 58 return 0; 59 } 60 61 HRESULT STDMETHODCALLTYPE CAddressEditBox::FileSysChange(long param8, long paramC) 62 { 63 return E_NOTIMPL; 64 } 65 66 HRESULT STDMETHODCALLTYPE CAddressEditBox::Refresh(long param8) 67 { 68 return E_NOTIMPL; 69 } 70 71 HRESULT STDMETHODCALLTYPE CAddressEditBox::Init(HWND comboboxEx, HWND editControl, long param14, IUnknown *param18) 72 { 73 CComPtr<IBrowserService> browserService; 74 75 fCombobox.SubclassWindow(comboboxEx); 76 fEditWindow.SubclassWindow(editControl); 77 fSite = param18; 78 hComboBoxEx = comboboxEx; 79 80 SHAutoComplete(fEditWindow.m_hWnd, SHACF_FILESYSTEM | SHACF_URLALL | SHACF_USETAB); 81 82 // take advice to watch events 83 HRESULT hResult = IUnknown_QueryService(param18, SID_SShellBrowser, IID_PPV_ARG(IBrowserService, &browserService)); 84 if (SUCCEEDED(hResult)) 85 { 86 hResult = AtlAdvise(browserService, static_cast<IDispatch *>(this), DIID_DWebBrowserEvents, &fAdviseCookie); 87 } 88 89 return hResult; 90 } 91 92 HRESULT STDMETHODCALLTYPE CAddressEditBox::SetCurrentDir(long paramC) 93 { 94 return E_NOTIMPL; 95 } 96 97 HRESULT STDMETHODCALLTYPE CAddressEditBox::ParseNow(long paramC) 98 { 99 ULONG eaten; 100 ULONG attributes; 101 HRESULT hr; 102 HWND topLevelWindow; 103 PIDLIST_ABSOLUTE pidlCurrent= NULL; 104 PIDLIST_RELATIVE pidlRelative = NULL; 105 CComPtr<IShellFolder> psfCurrent; 106 107 CComPtr<IBrowserService> pbs; 108 hr = IUnknown_QueryService(fSite, SID_SShellBrowser, IID_PPV_ARG(IBrowserService, &pbs)); 109 if (FAILED_UNEXPECTEDLY(hr)) 110 return hr; 111 112 hr = IUnknown_GetWindow(pbs, &topLevelWindow); 113 if (FAILED_UNEXPECTEDLY(hr)) 114 return hr; 115 116 /* Get the path to browse and expand it if needed */ 117 LPWSTR input; 118 int inputLength = fCombobox.GetWindowTextLength() + 2; 119 120 input = new WCHAR[inputLength]; 121 fCombobox.GetWindowText(input, inputLength); 122 123 LPWSTR address; 124 int addressLength = ExpandEnvironmentStrings(input, NULL, 0); 125 126 if (addressLength <= 0) 127 { 128 address = input; 129 } 130 else 131 { 132 addressLength += 2; 133 address = new WCHAR[addressLength]; 134 if (!ExpandEnvironmentStrings(input, address, addressLength)) 135 { 136 delete[] address; 137 address = input; 138 } 139 } 140 141 /* Try to parse a relative path and if it fails, try to browse an absolute path */ 142 CComPtr<IShellFolder> psfDesktop; 143 hr = SHGetDesktopFolder(&psfDesktop); 144 if (FAILED_UNEXPECTEDLY(hr)) 145 goto cleanup; 146 147 hr = pbs->GetPidl(&pidlCurrent); 148 if (FAILED_UNEXPECTEDLY(hr)) 149 goto cleanup; 150 151 hr = psfDesktop->BindToObject(pidlCurrent, NULL, IID_PPV_ARG(IShellFolder, &psfCurrent)); 152 if (FAILED_UNEXPECTEDLY(hr)) 153 goto cleanup; 154 155 hr = psfCurrent->ParseDisplayName(topLevelWindow, NULL, address, &eaten, &pidlRelative, &attributes); 156 if (SUCCEEDED(hr)) 157 { 158 pidlLastParsed = ILCombine(pidlCurrent, pidlRelative); 159 ILFree(pidlRelative); 160 goto cleanup; 161 } 162 163 /* We couldn't parse a relative path, attempt to parse an absolute path */ 164 hr = psfDesktop->ParseDisplayName(topLevelWindow, NULL, address, &eaten, &pidlLastParsed, &attributes); 165 166 cleanup: 167 if (pidlCurrent) 168 ILFree(pidlCurrent); 169 if (address != input) 170 delete [] address; 171 delete [] input; 172 173 return hr; 174 } 175 176 HRESULT STDMETHODCALLTYPE CAddressEditBox::Execute(long paramC) 177 { 178 HRESULT hr; 179 180 /* 181 * Parse the path is it wasn't parsed 182 */ 183 if (!pidlLastParsed) 184 ParseNow(0); 185 186 if (!pidlLastParsed) 187 return E_FAIL; 188 189 /* 190 * Get the IShellBrowser and IBrowserService interfaces of the shell browser 191 */ 192 CComPtr<IShellBrowser> pisb; 193 hr = IUnknown_QueryService(fSite, SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &pisb)); 194 if (FAILED(hr)) 195 return hr; 196 197 CComPtr<IBrowserService> pbs; 198 pisb->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)); 199 if (FAILED(hr)) 200 return hr; 201 202 /* 203 * Get the current pidl of the shellbrowser and check if it is the same with the parsed one 204 */ 205 PIDLIST_ABSOLUTE pidl; 206 hr = pbs->GetPidl(&pidl); 207 if (FAILED(hr)) 208 return hr; 209 210 CComPtr<IShellFolder> psf; 211 hr = SHGetDesktopFolder(&psf); 212 if (FAILED(hr)) 213 return hr; 214 215 hr = psf->CompareIDs(0, pidl, pidlLastParsed); 216 217 SHFree(pidl); 218 if (hr == 0) 219 return S_OK; 220 221 /* 222 * Attempt to browse to the parsed pidl 223 */ 224 hr = pisb->BrowseObject(pidlLastParsed, 0); 225 if (SUCCEEDED(hr)) 226 return hr; 227 228 /* 229 * Browsing to the pidl failed so it's not a folder. So invoke its defaule command. 230 */ 231 HWND topLevelWindow; 232 hr = IUnknown_GetWindow(pisb, &topLevelWindow); 233 if (FAILED(hr)) 234 return hr; 235 236 LPCITEMIDLIST pidlChild; 237 CComPtr<IShellFolder> sf; 238 hr = SHBindToParent(pidlLastParsed, IID_PPV_ARG(IShellFolder, &sf), &pidlChild); 239 if (FAILED(hr)) 240 return hr; 241 242 hr = SHInvokeDefaultCommand(topLevelWindow, sf, pidlChild); 243 if (FAILED(hr)) 244 return hr; 245 246 return hr; 247 } 248 249 HRESULT STDMETHODCALLTYPE CAddressEditBox::Save(long paramC) 250 { 251 return E_NOTIMPL; 252 } 253 254 HRESULT STDMETHODCALLTYPE CAddressEditBox::OnWinEvent( 255 HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult) 256 { 257 LPNMHDR hdr; 258 259 if (theResult) 260 *theResult = 0; 261 262 switch (uMsg) 263 { 264 case WM_COMMAND: 265 { 266 if (HIWORD(wParam) == CBN_SELCHANGE) 267 { 268 UINT selectedIndex = SendMessageW((HWND)lParam, CB_GETCURSEL, 0, 0); 269 pidlLastParsed = ILClone((LPITEMIDLIST)SendMessageW((HWND)lParam, CB_GETITEMDATA, selectedIndex, 0)); 270 Execute(0); 271 } 272 break; 273 } 274 case WM_NOTIFY: 275 { 276 hdr = (LPNMHDR) lParam; 277 if (hdr->code == CBEN_ENDEDIT) 278 { 279 NMCBEENDEDITW *endEdit = (NMCBEENDEDITW*) lParam; 280 if (endEdit->iWhy == CBENF_RETURN) 281 { 282 Execute(0); 283 } 284 else if (endEdit->iWhy == CBENF_ESCAPE) 285 { 286 /* Reset the contents of the combo box */ 287 } 288 } 289 else if (hdr->code == CBEN_DELETEITEM) 290 { 291 PNMCOMBOBOXEX pCBEx = (PNMCOMBOBOXEX) lParam; 292 LPITEMIDLIST itemPidl = (LPITEMIDLIST)pCBEx->ceItem.lParam; 293 if (itemPidl) 294 { 295 ILFree(itemPidl); 296 } 297 } 298 break; 299 } 300 } 301 return S_OK; 302 } 303 304 HRESULT STDMETHODCALLTYPE CAddressEditBox::IsWindowOwner(HWND hWnd) 305 { 306 if (fCombobox.m_hWnd == hWnd) 307 return S_OK; 308 if (fEditWindow.m_hWnd == hWnd) 309 return S_OK; 310 return S_FALSE; 311 } 312 313 HRESULT STDMETHODCALLTYPE CAddressEditBox::QueryStatus( 314 const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[ ], OLECMDTEXT *pCmdText) 315 { 316 return E_NOTIMPL; 317 } 318 319 HRESULT STDMETHODCALLTYPE CAddressEditBox::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, 320 DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut) 321 { 322 return E_NOTIMPL; 323 } 324 325 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetTypeInfoCount(UINT *pctinfo) 326 { 327 return E_NOTIMPL; 328 } 329 330 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) 331 { 332 return E_NOTIMPL; 333 } 334 335 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetIDsOfNames( 336 REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) 337 { 338 return E_NOTIMPL; 339 } 340 341 HRESULT STDMETHODCALLTYPE CAddressEditBox::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, 342 WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) 343 { 344 CComPtr<IBrowserService> isb; 345 CComPtr<IShellFolder> sf; 346 HRESULT hr; 347 PIDLIST_ABSOLUTE absolutePIDL; 348 LPCITEMIDLIST pidlChild; 349 STRRET ret; 350 WCHAR buf[4096]; 351 352 if (pDispParams == NULL) 353 return E_INVALIDARG; 354 355 switch (dispIdMember) 356 { 357 case DISPID_NAVIGATECOMPLETE2: 358 case DISPID_DOCUMENTCOMPLETE: 359 360 if (pidlLastParsed) 361 ILFree(pidlLastParsed); 362 pidlLastParsed = NULL; 363 364 /* Get the current pidl of the browser */ 365 hr = IUnknown_QueryService(fSite, SID_STopLevelBrowser, IID_PPV_ARG(IBrowserService, &isb)); 366 if (FAILED_UNEXPECTEDLY(hr)) 367 return hr; 368 369 hr = isb->GetPidl(&absolutePIDL); 370 if (FAILED_UNEXPECTEDLY(hr)) 371 return hr; 372 373 /* Fill the combobox */ 374 PopulateComboBox(absolutePIDL); 375 376 /* Find the current item in the combobox and select it */ 377 CComPtr<IShellFolder> psfDesktop; 378 hr = SHGetDesktopFolder(&psfDesktop); 379 if (FAILED_UNEXPECTEDLY(hr)) 380 return S_OK; 381 382 hr = psfDesktop->GetDisplayNameOf(absolutePIDL, SHGDN_FORADDRESSBAR, &ret); 383 if (FAILED_UNEXPECTEDLY(hr)) 384 return S_OK; 385 386 hr = StrRetToBufW(&ret, absolutePIDL, buf, 4095); 387 if (FAILED_UNEXPECTEDLY(hr)) 388 return S_OK; 389 390 int index = SendMessageW(hComboBoxEx, CB_FINDSTRINGEXACT, 0, (LPARAM)buf); 391 if (index != -1) 392 SendMessageW(hComboBoxEx, CB_SETCURSEL, index, 0); 393 394 /* Add the item that will be visible when the combobox is not expanded */ 395 hr = SHBindToParent(absolutePIDL, IID_PPV_ARG(IShellFolder, &sf), &pidlChild); 396 if (FAILED_UNEXPECTEDLY(hr)) 397 return hr; 398 399 hr = sf->GetDisplayNameOf(pidlChild, SHGDN_FORADDRESSBAR | SHGDN_FORPARSING, &ret); 400 if (FAILED_UNEXPECTEDLY(hr)) 401 return hr; 402 403 hr = StrRetToBufW(&ret, pidlChild, buf, 4095); 404 if (FAILED_UNEXPECTEDLY(hr)) 405 return hr; 406 407 INT indexClosed, indexOpen; 408 indexClosed = SHMapPIDLToSystemImageListIndex(sf, pidlChild, &indexOpen); 409 410 COMBOBOXEXITEMW item = {0}; 411 item.mask = CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_TEXT | CBEIF_LPARAM; 412 item.iItem = -1; 413 item.iImage = indexClosed; 414 item.iSelectedImage = indexOpen; 415 item.pszText = buf; 416 item.lParam = reinterpret_cast<LPARAM>(absolutePIDL); 417 fCombobox.SendMessage(CBEM_SETITEM, 0, reinterpret_cast<LPARAM>(&item)); 418 } 419 return S_OK; 420 } 421 422 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetClassID(CLSID *pClassID) 423 { 424 if (pClassID == NULL) 425 return E_POINTER; 426 *pClassID = CLSID_AddressEditBox; 427 return S_OK; 428 } 429 430 HRESULT STDMETHODCALLTYPE CAddressEditBox::IsDirty() 431 { 432 return E_NOTIMPL; 433 } 434 435 HRESULT STDMETHODCALLTYPE CAddressEditBox::Load(IStream *pStm) 436 { 437 return E_NOTIMPL; 438 } 439 440 HRESULT STDMETHODCALLTYPE CAddressEditBox::Save(IStream *pStm, BOOL fClearDirty) 441 { 442 return E_NOTIMPL; 443 } 444 445 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetSizeMax(ULARGE_INTEGER *pcbSize) 446 { 447 return E_NOTIMPL; 448 } 449 450 void CAddressEditBox::PopulateComboBox(LPITEMIDLIST pidlCurrent) 451 { 452 HRESULT hr; 453 LPITEMIDLIST pidl; 454 int indent = 0; 455 int index; 456 457 index = SendMessageW(hComboBoxEx, CB_GETCOUNT, 0, 0); 458 for (int i = 0; i < index; i++) 459 SendMessageW(hComboBoxEx, CBEM_DELETEITEM, i, 0); 460 SendMessageW(hComboBoxEx, CB_RESETCONTENT, 0, 0); 461 462 /* Calculate the indent level. No need to clone the pidl */ 463 pidl = pidlCurrent; 464 do 465 { 466 if(!pidl->mkid.cb) 467 break; 468 pidl = ILGetNext(pidl); 469 indent++; 470 } while (pidl); 471 index = indent; 472 473 /* Add every id from the pidl in the combo box */ 474 pidl = ILClone(pidlCurrent); 475 do 476 { 477 AddComboBoxItem(pidl, 0, index); 478 ILRemoveLastID(pidl); 479 index--; 480 } while (index >= 0); 481 ILFree(pidl); 482 483 /* Add the items of the desktop */ 484 FillOneLevel(0, 1, indent); 485 486 /* Add the items of My Computer */ 487 hr = SHGetSpecialFolderLocation(0, CSIDL_DRIVES, &pidl); 488 if (FAILED_UNEXPECTEDLY(hr)) 489 return; 490 491 for(LPITEMIDLIST i = GetItemData(0); i; i = GetItemData(index)) 492 { 493 if (ILIsEqual(i, pidl)) 494 { 495 FillOneLevel(index, 2, indent); 496 break; 497 } 498 index++; 499 } 500 ILFree(pidl); 501 } 502 503 void CAddressEditBox::AddComboBoxItem(LPITEMIDLIST pidl, int index, int indent) 504 { 505 HRESULT hr; 506 WCHAR buf[4096]; 507 508 LPCITEMIDLIST pidlChild; 509 CComPtr<IShellFolder> sf; 510 hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &sf), &pidlChild); 511 if (FAILED_UNEXPECTEDLY(hr)) 512 return; 513 514 STRRET strret; 515 hr = sf->GetDisplayNameOf(pidlChild, SHGDN_FORADDRESSBAR, &strret); 516 if (FAILED_UNEXPECTEDLY(hr)) 517 return; 518 519 hr = StrRetToBufW(&strret, pidlChild, buf, 4095); 520 if (FAILED_UNEXPECTEDLY(hr)) 521 return; 522 523 COMBOBOXEXITEMW item = {0}; 524 item.mask = CBEIF_LPARAM | CBEIF_INDENT | CBEIF_SELECTEDIMAGE | CBEIF_IMAGE | CBEIF_TEXT; 525 item.iImage = SHMapPIDLToSystemImageListIndex(sf, pidlChild, &item.iSelectedImage); 526 item.pszText = buf; 527 item.lParam = (LPARAM)(ILClone(pidl)); 528 item.iIndent = indent; 529 item.iItem = index; 530 SendMessageW(hComboBoxEx, CBEM_INSERTITEMW, 0, (LPARAM)&item); 531 } 532 533 void CAddressEditBox::FillOneLevel(int index, int levelIndent, int indent) 534 { 535 HRESULT hr; 536 ULONG numObj; 537 int count; 538 LPITEMIDLIST pidl, pidl2, pidl3, pidl4; 539 540 count = index + 1; 541 pidl = GetItemData(index); 542 pidl2 = GetItemData(count); 543 if(pidl) 544 { 545 CComPtr<IShellFolder> psfDesktop; 546 CComPtr<IShellFolder> psfItem; 547 548 hr = SHGetDesktopFolder(&psfDesktop); 549 if (FAILED_UNEXPECTEDLY(hr)) 550 return; 551 552 if (!pidl->mkid.cb) 553 { 554 psfItem = psfDesktop; 555 } 556 else 557 { 558 hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfItem)); 559 if (FAILED_UNEXPECTEDLY(hr)) 560 return; 561 } 562 563 CComPtr<IEnumIDList> pEnumIDList; 564 hr = psfItem->EnumObjects(0, SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN, &pEnumIDList); 565 if (FAILED_UNEXPECTEDLY(hr)) 566 return; 567 568 do 569 { 570 hr = pEnumIDList->Next(1, &pidl3, &numObj); 571 if(hr != S_OK || !numObj) 572 break; 573 574 pidl4 = ILCombine(pidl, pidl3); 575 if (pidl2 && ILIsEqual(pidl4, pidl2)) 576 count += (indent - levelIndent); 577 else 578 AddComboBoxItem(pidl4, count, levelIndent); 579 count++; 580 ILFree(pidl3); 581 ILFree(pidl4); 582 } while (true); 583 } 584 } 585 586 LPITEMIDLIST CAddressEditBox::GetItemData(int index) 587 { 588 COMBOBOXEXITEMW item; 589 590 memset(&item, 0, sizeof(COMBOBOXEXITEMW)); 591 item.mask = CBEIF_LPARAM; 592 item.iItem = index; 593 SendMessageW(hComboBoxEx, CBEM_GETITEMW, 0, (LPARAM)&item); 594 return (LPITEMIDLIST)item.lParam; 595 } 596