Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

665 lines
19 KiB

  1. /**************************************************************\
  2. FILE: addrlist.cpp
  3. DESCRIPTION:
  4. This is a class that all Address Lists can inherite
  5. from. This will give them the IAddressList interface
  6. so they can work in the AddressBand/Bar.
  7. \**************************************************************/
  8. #include "priv.h"
  9. #include "util.h"
  10. #include "itbdrop.h"
  11. #include "autocomp.h"
  12. #include "addrlist.h"
  13. #include "apithk.h"
  14. #include "shui.h"
  15. #include "shlguid.h"
  16. CAddressList::CAddressList() : _cRef(1)
  17. {
  18. // This needs to be allocated in Zero Inited Memory.
  19. // Assert that all Member Variables are inited to Zero.
  20. ASSERT(!_pbp);
  21. }
  22. CAddressList::~CAddressList()
  23. {
  24. if (_pbp)
  25. _pbp->Release();
  26. if (_pbs)
  27. _pbs->Release();
  28. if (_pshuUrl)
  29. delete _pshuUrl;
  30. }
  31. ULONG CAddressList::AddRef()
  32. {
  33. _cRef++;
  34. return _cRef;
  35. }
  36. ULONG CAddressList::Release()
  37. {
  38. ASSERT(_cRef > 0);
  39. _cRef--;
  40. if (_cRef > 0)
  41. return _cRef;
  42. delete this;
  43. return 0;
  44. }
  45. HRESULT CAddressList::QueryInterface(REFIID riid, void **ppvObj)
  46. {
  47. if (IsEqualIID(riid, IID_IUnknown) ||
  48. IsEqualIID(riid, IID_IWinEventHandler) ||
  49. IsEqualIID(riid, IID_IAddressList))
  50. {
  51. *ppvObj = SAFECAST(this, IAddressList*);
  52. }
  53. else
  54. {
  55. *ppvObj = NULL;
  56. return E_NOINTERFACE;
  57. }
  58. AddRef();
  59. return S_OK;
  60. }
  61. //================================
  62. // ** IWinEventHandler Interface ***
  63. /****************************************************\
  64. FUNCTION: OnWinEvent
  65. DESCRIPTION:
  66. This function will give receive events from
  67. the parent ShellToolbar.
  68. \****************************************************/
  69. HRESULT CAddressList::OnWinEvent(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres)
  70. {
  71. LRESULT lres = 0;
  72. switch (uMsg)
  73. {
  74. case WM_COMMAND:
  75. lres = _OnCommand(wParam, lParam);
  76. break;
  77. case WM_NOTIFY:
  78. lres = _OnNotify((LPNMHDR)lParam);
  79. break;
  80. }
  81. if (plres)
  82. *plres = lres;
  83. return S_OK;
  84. }
  85. //================================
  86. // *** IAddressList Interface ***
  87. /****************************************************\
  88. FUNCTION: Connect
  89. DESCRIPTION:
  90. We are either becoming the selected list for
  91. the AddressBand's combobox, or lossing this status.
  92. We need to populate or unpopulate the combobox
  93. as appropriate.
  94. \****************************************************/
  95. HRESULT CAddressList::Connect(BOOL fConnect, HWND hwnd, IBrowserService * pbs, IBandProxy * pbp, IAutoComplete * pac)
  96. {
  97. HRESULT hr = S_OK;
  98. ASSERT(hwnd);
  99. _fVisible = fConnect;
  100. _hwnd = hwnd;
  101. // Copy the (IBandProxy *) parameter
  102. if (_pbp)
  103. _pbp->Release();
  104. _pbp = pbp;
  105. if (_pbp)
  106. _pbp->AddRef();
  107. if (_pbs)
  108. _pbs->Release();
  109. _pbs = pbs;
  110. if (_pbs)
  111. _pbs->AddRef();
  112. if (fConnect)
  113. {
  114. //
  115. // Initial combobox parameters.
  116. //
  117. _InitCombobox();
  118. }
  119. else
  120. {
  121. // Remove contents of the List
  122. _PurgeComboBox();
  123. }
  124. return hr;
  125. }
  126. /****************************************************\
  127. FUNCTION: _InitCombobox
  128. DESCRIPTION:
  129. Prepare the combo box for this list. This normally
  130. means that the indenting and icon are either turned
  131. on or off.
  132. \****************************************************/
  133. void CAddressList::_InitCombobox()
  134. {
  135. SendMessage(_hwnd, CB_SETDROPPEDWIDTH, 200, 0L);
  136. SendMessage(_hwnd, CB_SETEXTENDEDUI, TRUE, 0L);
  137. SendMessage(_hwnd, CBEM_SETEXTENDEDSTYLE, CBES_EX_NOSIZELIMIT, CBES_EX_NOSIZELIMIT);
  138. }
  139. /****************************************************\
  140. FUNCTION: _PurgeComboBox
  141. DESCRIPTION:
  142. Removes all items from the combobox.
  143. \****************************************************/
  144. void CAddressList::_PurgeComboBox()
  145. {
  146. if (_hwnd)
  147. {
  148. SendMessage(_hwnd, CB_RESETCONTENT, 0, 0L);
  149. }
  150. }
  151. /****************************************************\
  152. FUNCTION: _OnCommand
  153. DESCRIPTION:
  154. This function will handle WM_COMMAND messages.
  155. \****************************************************/
  156. LRESULT CAddressList::_OnCommand(WPARAM wParam, LPARAM lParam)
  157. {
  158. UINT uCmd = GET_WM_COMMAND_CMD(wParam, lParam);
  159. switch (uCmd)
  160. {
  161. case CBN_DROPDOWN:
  162. {
  163. HCURSOR hCursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
  164. //
  165. // DOH! The user wants to see the full combo contents.
  166. // Better go fill it in now.
  167. //
  168. _Populate();
  169. SetCursor(hCursorOld);
  170. }
  171. break;
  172. }
  173. return 0;
  174. }
  175. void ChangeUrl(LPCTSTR pszUrl, LPTSTR pszDisplayName, DWORD cchSize)
  176. {
  177. TCHAR szTemp[MAX_URL_STRING];
  178. StringCchCopy(szTemp, ARRAYSIZE(szTemp), pszUrl); // This is necessary because pszUrl points into the buffer of pszDisplayName
  179. StringCchCopy(pszDisplayName, cchSize, szTemp);
  180. }
  181. /****************************************************\
  182. FUNCTION: NavigationComplete
  183. DESCRIPTION:
  184. Update the URL in the Top of the list.
  185. \****************************************************/
  186. HRESULT CAddressList::NavigationComplete(LPVOID pvCShellUrl)
  187. {
  188. HRESULT hr = S_OK;
  189. TCHAR szDisplayName[MAX_URL_STRING];
  190. CShellUrl * psu = (CShellUrl *) pvCShellUrl;
  191. LPITEMIDLIST pidl;
  192. ASSERT(pvCShellUrl);
  193. hr = psu->GetDisplayName(szDisplayName, SIZECHARS(szDisplayName));
  194. if (SUCCEEDED(hr))
  195. {
  196. //
  197. // Don't display the url to internal error pages. The url that should get
  198. // displayed is appended after the #.
  199. //
  200. // All error urls start with res:// so do a quick check first.
  201. //
  202. BOOL fChangeURL = TRUE;
  203. if (TEXT('r') == szDisplayName[0] && TEXT('e') == szDisplayName[1])
  204. {
  205. if (IsErrorUrl(szDisplayName))
  206. {
  207. TCHAR* pszUrl = StrChr(szDisplayName, TEXT('#'));
  208. if (pszUrl)
  209. {
  210. pszUrl += 1;
  211. DWORD dwScheme = GetUrlScheme(pszUrl);
  212. fChangeURL = ((URL_SCHEME_HTTP == dwScheme) ||
  213. (URL_SCHEME_HTTPS == dwScheme) ||
  214. (URL_SCHEME_FTP == dwScheme) ||
  215. (URL_SCHEME_GOPHER == dwScheme));
  216. // Don't blast in the stuff after the # into address bar
  217. // unless it is a 'safe' url. If it's not safe leave the
  218. // addressbar alone to preserve what the user typed in.
  219. //
  220. // The issue here is that a web page could navigate to our internal
  221. // error page with "format c:" after the '#'. The error page
  222. // suggests that the user refreshed the page which would be very bad!
  223. if (fChangeURL)
  224. {
  225. ChangeUrl(pszUrl, szDisplayName, ARRAYSIZE(szDisplayName));
  226. }
  227. }
  228. }
  229. }
  230. if (fChangeURL)
  231. {
  232. SHRemoveURLTurd(szDisplayName);
  233. SHCleanupUrlForDisplay(szDisplayName);
  234. hr = psu->GetPidl(&pidl);
  235. if (SUCCEEDED(hr))
  236. {
  237. COMBOBOXEXITEM cbexItem = {0};
  238. cbexItem.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
  239. cbexItem.iItem = -1;
  240. cbexItem.pszText = szDisplayName;
  241. cbexItem.cchTextMax = ARRAYSIZE(szDisplayName);
  242. hr = _GetPidlIcon(pidl, &(cbexItem.iImage), &(cbexItem.iSelectedImage));
  243. SendMessage(_hwnd, CBEM_SETITEM, (WPARAM)0, (LPARAM)(LPVOID)&cbexItem);
  244. ILFree(pidl);
  245. }
  246. }
  247. }
  248. TraceMsg(TF_BAND|TF_GENERAL, "CAddressList: NavigationComplete(), URL=%s", szDisplayName);
  249. return hr;
  250. }
  251. /*******************************************************************
  252. FUNCTION: _MoveAddressToTopOfList
  253. PARAMETERS:
  254. iSel - index of item in combo box to move
  255. DESCRIPTION:
  256. Moves the specified selection in the combo box
  257. to be the first item in the combo box
  258. ********************************************************************/
  259. BOOL CAddressList::_MoveAddressToTopOfList(int iSel)
  260. {
  261. BOOL fRet = FALSE;
  262. ASSERT(iSel >= 0); // must have valid index
  263. COMBOBOXEXITEM cbexItem = {0};
  264. TCHAR szAddress[MAX_URL_STRING+1];
  265. cbexItem.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
  266. cbexItem.pszText = szAddress;
  267. cbexItem.cchTextMax = ARRAYSIZE(szAddress);
  268. cbexItem.iItem = iSel;
  269. // get the specified item from combo box
  270. if (SendMessage(_hwnd,CBEM_GETITEM,0,(LPARAM) &cbexItem)) {
  271. SendMessage(_hwnd, CBEM_DELETEITEM, (WPARAM)iSel, (LPARAM)0);
  272. // re-insert it at index 0 to put it at the top
  273. cbexItem.iItem = 0;
  274. // sending CBEM_INSERTITEM should return the index we specified
  275. // (0) if successful
  276. fRet = (SendMessage(_hwnd, CBEM_INSERTITEM, (WPARAM)0,
  277. (LPARAM)(LPVOID)&cbexItem) == 0);
  278. }
  279. return fRet;
  280. }
  281. /*******************************************************************
  282. FUNCTION: _ComboBoxInsertURL
  283. DESCRIPTION:
  284. Adds the specified URL to the top of the address bar
  285. combo box. Limits the number of URLs in combo box to
  286. nMaxComboBoxSize.
  287. ********************************************************************/
  288. void CAddressList::_ComboBoxInsertURL(LPCTSTR pszURL, int cchStrSize, int nMaxComboBoxSize)
  289. {
  290. // Since we own it and it's populated,
  291. // we will add it directly to the ComboBox.
  292. int iPrevInstance;
  293. int iImage, iSelectedImage ;
  294. COMBOBOXEXITEM cbexItem = {0};
  295. cbexItem.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
  296. cbexItem.iItem = 0;
  297. cbexItem.cchTextMax = cchStrSize;
  298. cbexItem.pszText = (LPTSTR)pszURL;
  299. _GetUrlUI(NULL, (LPTSTR)pszURL, &iImage, &iSelectedImage);
  300. cbexItem.iImage = iImage;
  301. cbexItem.iSelectedImage = iSelectedImage;
  302. iPrevInstance = (int)SendMessage(_hwnd, CB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)pszURL);
  303. if (iPrevInstance != CB_ERR) {
  304. _MoveAddressToTopOfList(iPrevInstance);
  305. return;
  306. }
  307. // insert the URL as the first item in combo box
  308. SendMessage(_hwnd, CBEM_INSERTITEM, (WPARAM)0, (LPARAM)(LPVOID)&cbexItem);
  309. // limit number of items in combo box to nMaxComboBoxSize
  310. if (ComboBox_GetCount(_hwnd) > nMaxComboBoxSize)
  311. {
  312. // if we're ever over the limit, we should only be over the limit
  313. // by exactly one item
  314. ASSERT(ComboBox_GetCount(_hwnd) == nMaxComboBoxSize+1);
  315. // if over the limit, delete the least recently used
  316. // (the one with the highest index)
  317. SendMessage(_hwnd, CBEM_DELETEITEM, (WPARAM)nMaxComboBoxSize, (LPARAM)0);
  318. }
  319. }
  320. /*******************************************************************
  321. FUNCTION: SetToListIndex
  322. DESCRIPTION:
  323. This function will set the CShellUrl parameter to the item
  324. in the Drop Down list that is indexed by nIndex.
  325. ********************************************************************/
  326. HRESULT CAddressList::SetToListIndex(int nIndex, LPVOID pvShelLUrl)
  327. {
  328. HRESULT hr = S_OK;
  329. TCHAR szBuffer[MAX_URL_STRING];
  330. CShellUrl * psuURL = (CShellUrl *) pvShelLUrl;
  331. if (SUCCEEDED(GetCBListIndex(_hwnd, nIndex, szBuffer, SIZECHARS(szBuffer))))
  332. {
  333. hr = psuURL->ParseFromOutsideSource(szBuffer, SHURL_FLAGS_NOUI);
  334. ASSERT(SUCCEEDED(hr)); // We should not have added it to the Drop Down list if it's invalid.
  335. }
  336. return hr;
  337. }
  338. HRESULT CAddressList::FileSysChangeAL(DWORD dw, LPCITEMIDLIST *ppidl)
  339. {
  340. return S_OK;
  341. }
  342. /****************************************************\
  343. FUNCTION: GetCBListIndex
  344. DESCRIPTION:
  345. This function will get the text of a specified
  346. element in the combobox.
  347. \****************************************************/
  348. HRESULT GetCBListIndex(HWND hwnd, int iItem, LPTSTR szAddress, int cchAddressSize)
  349. {
  350. HRESULT hr = E_FAIL;
  351. COMBOBOXEXITEM cbexItem = {0};
  352. cbexItem.mask = CBEIF_TEXT;
  353. cbexItem.pszText = szAddress;
  354. cbexItem.cchTextMax = cchAddressSize;
  355. cbexItem.iItem = iItem;
  356. if (SendMessage(hwnd, CBEM_GETITEM, 0, (LPARAM) &cbexItem))
  357. hr = S_OK;
  358. return hr;
  359. }
  360. // Helper Function
  361. // We need to really becareful of perf in this function.
  362. HRESULT CAddressList::_GetUrlUI(CShellUrl *psu, LPCTSTR szUrl, int *piImage, int *piSelectedImage)
  363. {
  364. CShellUrl * psuUrl;
  365. HRESULT hr = E_FAIL;
  366. if (psu)
  367. psuUrl = psu;
  368. else
  369. {
  370. psuUrl = new CShellUrl();
  371. if (psuUrl)
  372. {
  373. // Set the parent for error messageboxes. Note that this could end up disabing the taskbar.
  374. // If this is deemed to be a problem we can first check to see where the addressbar is hosted.
  375. psuUrl->SetMessageBoxParent(_hwnd);
  376. SetDefaultShellPath(psuUrl);
  377. }
  378. }
  379. //Initialize the values to 0
  380. *piImage = 0;
  381. *piSelectedImage = 0;
  382. //if object is not created return with default value
  383. if (!psuUrl)
  384. return E_OUTOFMEMORY;
  385. #ifdef DISABLED // Why not show the correct icon for removable drives?
  386. int iDrive;
  387. // See if we have a drive specified in the path
  388. if ((iDrive = PathGetDriveNumber(szUrl)) >= 0)
  389. {
  390. // See if the drive is removable ?
  391. if(DriveType(iDrive) == DRIVE_REMOVABLE)
  392. hr = S_OK; //Drive is removable so pass the default icons
  393. }
  394. #endif
  395. // Do we still need to get the icons?
  396. if (FAILED(hr))
  397. {
  398. // Yes, so try the fast way first.
  399. hr = _GetFastPathIcons(szUrl, piImage, piSelectedImage);
  400. if (FAILED(hr))
  401. {
  402. LPITEMIDLIST pidl = NULL;
  403. // If that failed because it the string probably uses advanced parsing,
  404. // let CShellUrl do it the slower but more thurough way.
  405. hr = psuUrl->ParseFromOutsideSource(szUrl, SHURL_FLAGS_NOUI);
  406. if(SUCCEEDED(hr))
  407. hr = psuUrl->GetPidl(&pidl);
  408. if(SUCCEEDED(hr))
  409. {
  410. hr = _GetPidlIcon(pidl, piImage, piSelectedImage);
  411. ILFree(pidl);
  412. }
  413. }
  414. }
  415. if (psu != psuUrl)
  416. delete psuUrl;
  417. return hr;
  418. }
  419. // IECreateFromPath() and CShellUrl::ParseFromOutsideSource() both
  420. // touch the disk which causes unconnected network cases to be really
  421. // slow. This will create icons for file system paths w/o hitting
  422. // the disk.
  423. HRESULT CAddressList::_GetFastPathIcons(LPCTSTR pszPath, int *piImage, int *piSelectedImage)
  424. {
  425. SHFILEINFO shFileInfo = {0};
  426. // SHGetFileInfo() with those flags will be fast because it's won't filter out
  427. // garbage passed to it. So it will think URLs are actually relative paths
  428. // and accept them. We will fall back to the slow advanced parser which is still
  429. // fast with URLs.
  430. if (PathIsRelative(pszPath))
  431. return E_FAIL;
  432. HIMAGELIST himl = (HIMAGELIST) SHGetFileInfo(pszPath, FILE_ATTRIBUTE_DIRECTORY, &shFileInfo, sizeof(shFileInfo), (SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES));
  433. if (!himl || !shFileInfo.iIcon)
  434. return E_FAIL;
  435. *piImage = shFileInfo.iIcon;
  436. *piSelectedImage = shFileInfo.iIcon;
  437. // I don't need to free himl.
  438. return S_OK;
  439. }
  440. HRESULT CAddressList::_GetPidlIcon(LPCITEMIDLIST pidl, int *piImage, int *piSelectedImage)
  441. {
  442. IShellFolder *psfParent;
  443. LPCITEMIDLIST pidlChild;
  444. HRESULT hr = IEBindToParentFolder(pidl, &psfParent, &pidlChild);
  445. if (SUCCEEDED(hr))
  446. {
  447. *piImage = IEMapPIDLToSystemImageListIndex(psfParent, pidlChild, piSelectedImage);
  448. psfParent->Release();
  449. }
  450. return hr;
  451. }
  452. LPITEMIDLIST CAddressList::_GetDragDropPidl(LPNMCBEDRAGBEGINW pnmcbe)
  453. {
  454. LPITEMIDLIST pidl = NULL;
  455. CShellUrl *psuUrl = new CShellUrl();
  456. if (psuUrl)
  457. {
  458. // Set the parent for error messageboxes. Note that this could end up disabing the taskbar.
  459. // If this is deemed to be a problem we can first check to see where the addressbar is hosted.
  460. psuUrl->SetMessageBoxParent(_hwnd);
  461. HRESULT hr = SetDefaultShellPath(psuUrl);
  462. if (SUCCEEDED(hr))
  463. {
  464. hr = psuUrl->ParseFromOutsideSource(pnmcbe->szText, SHURL_FLAGS_NOUI, NULL);
  465. if (SUCCEEDED(hr))
  466. {
  467. hr = psuUrl->GetPidl(&pidl);
  468. }
  469. }
  470. delete psuUrl;
  471. }
  472. return pidl;
  473. }
  474. LRESULT CAddressList::_OnDragBeginA(LPNMCBEDRAGBEGINA pnmcbe)
  475. {
  476. NMCBEDRAGBEGINW nmcbew;
  477. nmcbew.hdr = pnmcbe->hdr;
  478. nmcbew.iItemid = pnmcbe->iItemid;
  479. SHAnsiToUnicode(pnmcbe->szText, nmcbew.szText, SIZECHARS(nmcbew.szText));
  480. return _OnDragBeginW(&nmcbew);
  481. }
  482. LRESULT CAddressList::_OnDragBeginW(LPNMCBEDRAGBEGINW pnmcbe)
  483. {
  484. LPITEMIDLIST pidl = _GetDragDropPidl(pnmcbe);
  485. if (pidl)
  486. {
  487. IOleCommandTarget *pcmdt = NULL;
  488. IUnknown *punk;
  489. if (_pbp && SUCCEEDED(_pbp->GetBrowserWindow(&punk)))
  490. {
  491. punk->QueryInterface(IID_IOleCommandTarget, (void **)&pcmdt);
  492. punk->Release();
  493. }
  494. DoDragDropWithInternetShortcut(pcmdt, pidl, _hwnd);
  495. if (pcmdt)
  496. pcmdt->Release();
  497. ILFree(pidl);
  498. }
  499. return 0;
  500. }
  501. // handle WM_NOTIFY messages.
  502. LRESULT CAddressList::_OnNotify(LPNMHDR pnm)
  503. {
  504. LRESULT lReturn = 0;
  505. switch (pnm->code)
  506. {
  507. case NM_SETCURSOR:
  508. if (!(SendMessage(_hwnd, CBEM_GETEXTENDEDSTYLE, 0, 0) & CBES_EX_NOEDITIMAGE))
  509. {
  510. RECT rc;
  511. POINT pt;
  512. int cx, cy;
  513. GetCursorPos(&pt);
  514. GetClientRect(_hwnd, &rc);
  515. MapWindowRect(_hwnd, HWND_DESKTOP, &rc);
  516. ImageList_GetIconSize((HIMAGELIST)SendMessage(_hwnd, CBEM_GETIMAGELIST, 0, 0), &cx, &cy);
  517. rc.right = rc.left + cx + GetSystemMetrics(SM_CXEDGE);
  518. if (PtInRect(&rc, pt))
  519. {
  520. // this means there's an image, which means we can drag
  521. SetCursor(LoadHandCursor(0));
  522. return 1;
  523. }
  524. }
  525. break;
  526. case CBEN_DRAGBEGINA:
  527. lReturn = _OnDragBeginA((LPNMCBEDRAGBEGINA)pnm);
  528. break;
  529. case CBEN_DRAGBEGINW:
  530. lReturn = _OnDragBeginW((LPNMCBEDRAGBEGINW)pnm);
  531. break;
  532. }
  533. return lReturn;
  534. }