Source code of Windows XP (NT5)
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.

1286 lines
37 KiB

  1. /**************************************************************\
  2. FILE: snslist.cpp
  3. DESCRIPTION:
  4. SNSList implements the Shell Name Space List or DriveList.
  5. This will store a pidl and be able to populate the AddressBand
  6. combobox with the shell name space that includes that PIDL.
  7. \**************************************************************/
  8. #include "priv.h"
  9. #ifndef UNIX
  10. #include "addrlist.h"
  11. #include "itbar.h"
  12. #include "itbdrop.h"
  13. #include "util.h"
  14. #include "autocomp.h"
  15. #include <urlhist.h>
  16. #include <winbase.h>
  17. #include <wininet.h>
  18. ///////////////////////////////////////////////////////////////////
  19. // Data Structures
  20. typedef struct {
  21. LPITEMIDLIST pidl; // the pidl
  22. TCHAR szName[MAX_URL_STRING]; // pidl's display name
  23. int iImage; // pidl's icon
  24. int iSelectedImage; // pidl's selected icon
  25. } PIDLCACHE, *PPIDLCACHE;
  26. /**************************************************************\
  27. CLASS: CSNSList
  28. DESCRIPTION:
  29. This object supports IAddressList and can populate
  30. the Address Band/Bar with the Shell Name Space (DriveList)
  31. heirarchy.
  32. \**************************************************************/
  33. class CSNSList : public CAddressList
  34. {
  35. public:
  36. //////////////////////////////////////////////////////
  37. // Public Interfaces
  38. //////////////////////////////////////////////////////
  39. // *** IAddressList methods ***
  40. virtual STDMETHODIMP Connect(BOOL fConnect, HWND hwnd, IBrowserService * pbs, IBandProxy * pbp, IAutoComplete * pac);
  41. virtual STDMETHODIMP NavigationComplete(LPVOID pvCShellUrl);
  42. virtual STDMETHODIMP Refresh(DWORD dwType);
  43. virtual STDMETHODIMP SetToListIndex(int nIndex, LPVOID pvShelLUrl);
  44. virtual STDMETHODIMP FileSysChangeAL(DWORD dw, LPCITEMIDLIST* ppidl);
  45. protected:
  46. //////////////////////////////////////////////////////
  47. // Private Member Functions
  48. //////////////////////////////////////////////////////
  49. // Constructor / Destructor
  50. CSNSList();
  51. ~CSNSList(void); // This is now an OLE Object and cannot be used as a normal Class.
  52. // Address Band Specific Functions
  53. LRESULT _OnNotify(LPNMHDR pnm);
  54. LRESULT _OnCommand(WPARAM wParam, LPARAM lParam);
  55. // Address List Modification Functions
  56. void _AddItem(LPITEMIDLIST pidl, int iInsert, int iIndent);
  57. LPITEMIDLIST _GetFullIDList(int iItem);
  58. int _GetIndent(int iItem);
  59. void _FillOneLevel(int iItem, int iIndent, int iDepth);
  60. void _ExpandMyComputer(int iDepth);
  61. LPITEMIDLIST _GetSelectedPidl(void);
  62. int _FindItem(LPITEMIDLIST pidl);
  63. BOOL _SetCachedPidl(LPCITEMIDLIST pidl);
  64. BOOL _GetPidlUI(LPCITEMIDLIST pidl, LPTSTR pszName, int cchName, int *piImage, int *piSelectedImage, DWORD dwFlags, BOOL fIgnoreCache);
  65. BOOL _GetPidlImage(LPCITEMIDLIST pidl, int *piImage, int *piSelectedImage);
  66. LRESULT _OnGetDispInfoA(PNMCOMBOBOXEXA pnmce);
  67. LRESULT _OnGetDispInfoW(PNMCOMBOBOXEXW pnmce);
  68. void _PurgeComboBox();
  69. void _PurgeAndResetComboBox();
  70. LPITEMIDLIST CSNSList::_GetDragDropPidl(LPNMCBEDRAGBEGINW pnmcbe);
  71. HRESULT _GetURLToolTip(LPTSTR pszUrl, DWORD dwStrSize);
  72. HRESULT _GetPIDL(LPITEMIDLIST* ppidl);
  73. BOOL _IsSelectionValid(void);
  74. HRESULT _PopulateOneItem(BOOL fIgnoreCache = FALSE);
  75. HRESULT _Populate(void);
  76. void _InitCombobox(void);
  77. // Friend Functions
  78. friend IAddressList * CSNSList_Create(void);
  79. //////////////////////////////////////////////////////
  80. // Private Member Variables
  81. //////////////////////////////////////////////////////
  82. PIDLCACHE _cache; // cache of pidl UI information
  83. BOOL _fFullListValid:1; // TRUE when the full combo is correctly populated
  84. BOOL _fPurgePending:1; // TRUE if we should purge when the combo closes up
  85. BOOL _fInPopulate; // TRUE when we're currently doing a _PopulateOneItem
  86. };
  87. //=================================================================
  88. // Implementation of CSNSList
  89. //=================================================================
  90. /****************************************************\
  91. FUNCTION: CSNSList_Create
  92. DESCRIPTION:
  93. This function will create an instance of the
  94. CSNSList COM object.
  95. \****************************************************/
  96. IAddressList * CSNSList_Create(void)
  97. {
  98. CSNSList * p = new CSNSList();
  99. return p;
  100. }
  101. /****************************************************\
  102. Address Band Constructor
  103. \****************************************************/
  104. CSNSList::CSNSList()
  105. {
  106. // This needs to be allocated in Zero Inited Memory.
  107. // Assert that all Member Variables are inited to Zero.
  108. ASSERT(!_cache.pidl);
  109. }
  110. /****************************************************\
  111. Address Band destructor
  112. \****************************************************/
  113. CSNSList::~CSNSList()
  114. {
  115. if (_cache.pidl)
  116. ILFree(_cache.pidl);
  117. _PurgeComboBox();
  118. TraceMsg(TF_SHDLIFE, "dtor CSNSList %x", this);
  119. }
  120. //================================
  121. // *** IAddressList Interface ***
  122. void CSNSList::_PurgeComboBox()
  123. {
  124. if (_hwnd)
  125. {
  126. // Deleting items from the combobox trashes the edit button if something was
  127. // previously selected from the combobox. So we want to restore the editbox
  128. // when we are done
  129. WCHAR szBuf[MAX_URL_STRING];
  130. *szBuf = NULL;
  131. GetWindowText(_hwnd, szBuf, ARRAYSIZE(szBuf));
  132. SendMessage(_hwnd, WM_SETREDRAW, FALSE, 0);
  133. // Delete the PIDL of every item and then free the item
  134. INT iMax = (int)SendMessage(_hwnd, CB_GETCOUNT, 0, 0);
  135. while(iMax > 0)
  136. {
  137. // Each call to DeleteItem results in a callback
  138. // which frees the corresponding PIDL
  139. // if you simply use CB_RESETCONTENT - you don't get the callback
  140. iMax = (int)SendMessage(_hwnd, CBEM_DELETEITEM, (WPARAM)0, (LPARAM)0);
  141. }
  142. // Restore the contents of the editbox
  143. SetWindowText(_hwnd, szBuf);
  144. SendMessage(_hwnd, WM_SETREDRAW, TRUE, 0);
  145. InvalidateRect(_hwnd, NULL, FALSE);
  146. }
  147. _fFullListValid = FALSE;
  148. }
  149. void CSNSList::_PurgeAndResetComboBox()
  150. {
  151. _PurgeComboBox();
  152. if (_hwnd)
  153. {
  154. SendMessage(_hwnd, CB_RESETCONTENT, 0, 0L);
  155. }
  156. }
  157. /****************************************************\
  158. DESCRIPTION:
  159. We are either becoming the selected list for
  160. the AddressBand's combobox, or lossing this status.
  161. We need to populate or unpopulate the combobox
  162. as appropriate.
  163. \****************************************************/
  164. HRESULT CSNSList::Connect(BOOL fConnect, HWND hwnd, IBrowserService * pbs, IBandProxy * pbp, IAutoComplete * pac)
  165. {
  166. _PurgeComboBox();
  167. HRESULT hr = CAddressList::Connect(fConnect, hwnd, pbs, pbp, pac);
  168. if (fConnect)
  169. {
  170. _PopulateOneItem();
  171. }
  172. else
  173. {
  174. // Get the pidl of the currently displayed item and destroy it
  175. COMBOBOXEXITEM cbexItem = {0};
  176. cbexItem.iItem = -1;
  177. cbexItem.mask = CBEIF_LPARAM;
  178. SendMessage(_hwnd, CBEM_GETITEM, 0, (LPARAM)&cbexItem);
  179. LPITEMIDLIST pidlPrev = (LPITEMIDLIST)cbexItem.lParam;
  180. if (pidlPrev)
  181. {
  182. ILFree(pidlPrev);
  183. cbexItem.lParam = NULL;
  184. SendMessage(_hwnd, CBEM_SETITEM, 0, (LPARAM)&cbexItem);
  185. }
  186. }
  187. return hr;
  188. }
  189. /****************************************************\
  190. FUNCTION: _InitCombobox
  191. DESCRIPTION:
  192. Prepare the combo box for this list. This normally
  193. means that the indenting and icon are either turned
  194. on or off.
  195. \****************************************************/
  196. void CSNSList::_InitCombobox()
  197. {
  198. HIMAGELIST himlSysSmall;
  199. Shell_GetImageLists(NULL, &himlSysSmall);
  200. SendMessage(_hwnd, CBEM_SETIMAGELIST, 0, (LPARAM)himlSysSmall);
  201. SendMessage(_hwnd, CBEM_SETEXSTYLE, 0, 0);
  202. CAddressList::_InitCombobox();
  203. }
  204. /****************************************************\
  205. FUNCTION: _IsSelectionValid
  206. DESCRIPTION:
  207. Is the current selection valid?
  208. \****************************************************/
  209. BOOL CSNSList::_IsSelectionValid(void)
  210. {
  211. LPITEMIDLIST pidlCur, pidlSel;
  212. BOOL fValid = S_OK;
  213. _GetPIDL(&pidlCur);
  214. pidlSel = _GetSelectedPidl();
  215. if (pidlCur == pidlSel)
  216. {
  217. fValid = TRUE;
  218. }
  219. else if ((pidlCur == NULL) || (pidlSel == NULL))
  220. {
  221. fValid = FALSE;
  222. }
  223. else
  224. {
  225. //
  226. // ILIsEqual faults on NULL pidls, sigh
  227. //
  228. fValid = ILIsEqual(pidlCur, pidlSel);
  229. }
  230. ILFree(pidlCur);
  231. return fValid;
  232. }
  233. /****************************************************\
  234. FUNCTION: NavigationComplete
  235. DESCRIPTION:
  236. Update the URL in the Top of the list.
  237. \****************************************************/
  238. HRESULT CSNSList::NavigationComplete(LPVOID pvCShellUrl)
  239. {
  240. CShellUrl * psu = (CShellUrl *) pvCShellUrl;
  241. ASSERT(pvCShellUrl);
  242. LPITEMIDLIST pidl;
  243. HRESULT hr = psu->GetPidl(&pidl);
  244. if (SUCCEEDED(hr))
  245. {
  246. // Update current PIDL.
  247. if (_SetCachedPidl(pidl))
  248. hr = _PopulateOneItem();
  249. ILFree(pidl);
  250. }
  251. return hr;
  252. }
  253. /****************************************************\
  254. FUNCTION: Refresh
  255. DESCRIPTION:
  256. This call will invalidate the contents of the
  257. contents of the drop down as well as refresh the
  258. Top Most icon and URL.
  259. \****************************************************/
  260. HRESULT CSNSList::Refresh(DWORD dwType)
  261. {
  262. if (!_hwnd)
  263. return S_OK; // Don't need to do any work.
  264. // Full refresh (ignore the cache) because the full path
  265. // style bit may have changed
  266. return _PopulateOneItem(TRUE);
  267. }
  268. /****************************************************\
  269. DESCRIPTION:
  270. Puts the current pidl into the combobox.
  271. This is a sneaky perf win. Since most of the time users
  272. don't drop down the combo, we only fill it in with the
  273. (visible) current selection.
  274. We need to destroy the PIDL of the currently displayed item
  275. first though
  276. \****************************************************/
  277. HRESULT CSNSList::_PopulateOneItem(BOOL fIgnoreCache)
  278. {
  279. HRESULT hr = S_OK;
  280. _fFullListValid = FALSE;
  281. // we can get reentered here when we do our sendmessages, which lets other notifies come in
  282. // and we get called between "LPITEMIDLIST pidlPrev = (LPITEMIDLIST)cbexItem.lParam" and
  283. // "ILFree(pidlPrev)". since we dont have a refcounted pidl this causes a double-free.
  284. // since its not trivial to change the refcounting, block out all reentrant callers. this
  285. // is okay since multiple calls are redundant anyway.
  286. if (!_fInPopulate)
  287. {
  288. _fInPopulate = TRUE;
  289. // First easy out - if there is no current pidl,
  290. // do nothing.
  291. LPITEMIDLIST pidlCur;
  292. if (SUCCEEDED(_GetPIDL(&pidlCur)) && pidlCur)
  293. {
  294. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  295. TraceMsg(TF_BAND|TF_GENERAL, "CSNSList: _PopulateOneItem(), and Pidl not in ComboBox. PIDL=>%s<", Dbg_PidlStr(pidlCur, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  296. ASSERT(_hwnd);
  297. TCHAR szURL[MAX_URL_STRING];
  298. COMBOBOXEXITEM cbexItem = {0};
  299. // Get the pidl of the currently displayed item and destroy it
  300. cbexItem.iItem = -1;
  301. cbexItem.mask = CBEIF_LPARAM;
  302. SendMessage(_hwnd, CBEM_GETITEM, 0, (LPARAM)&cbexItem);
  303. // we only free pidlPrev if we can sucessfully set the new item in...
  304. LPITEMIDLIST pidlPrev = (LPITEMIDLIST)cbexItem.lParam;
  305. // Done - so go insert the new item
  306. cbexItem.iItem = -1;
  307. cbexItem.pszText = szURL;
  308. cbexItem.cchTextMax = ARRAYSIZE(szURL);
  309. cbexItem.iIndent = 0;
  310. cbexItem.lParam = (LPARAM)ILClone(pidlCur);
  311. cbexItem.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_INDENT | CBEIF_LPARAM;
  312. _GetPidlUI(pidlCur, szURL, cbexItem.cchTextMax, &cbexItem.iImage,
  313. &cbexItem.iSelectedImage, SHGDN_FORPARSING, fIgnoreCache);
  314. if (!*szURL)
  315. {
  316. // Navigating to a net unc in browser-only doesn't work so try again without cache and FORPARSING
  317. _GetPidlUI(pidlCur, szURL, cbexItem.cchTextMax, &cbexItem.iImage,
  318. &cbexItem.iSelectedImage, SHGDN_NORMAL, TRUE);
  319. }
  320. TraceMsg(TF_BAND|TF_GENERAL, "CSNSList::_PopulateOneItem(), Name=>%s<", cbexItem.pszText);
  321. // We need to set the current selection to -1 or the icon of the current selection
  322. // will be displayed instead of this new one
  323. SendMessage(_hwnd, CB_SETCURSEL, (WPARAM)-1, 0L);
  324. LRESULT lRes = SendMessage(_hwnd, CBEM_SETITEM, 0, (LPARAM)&cbexItem);
  325. if ((CB_ERR == lRes) || (0 == lRes))
  326. {
  327. if (cbexItem.lParam)
  328. {
  329. // Since we didn't insert the item, free the cloned pidl
  330. ILFree((LPITEMIDLIST) cbexItem.lParam);
  331. }
  332. }
  333. else
  334. {
  335. // since we inserted the item, free the previous one
  336. if (pidlPrev)
  337. {
  338. ILFree(pidlPrev);
  339. }
  340. }
  341. ILFree(pidlCur);
  342. }
  343. _fInPopulate = FALSE;
  344. }
  345. return hr;
  346. }
  347. /****************************************************\
  348. DESCRIPTION:
  349. fills in the entire combo.
  350. WARNING!!!!!!!!:
  351. *** This is expensive, don't do it unless absolutely necessary! ***
  352. \****************************************************/
  353. HRESULT CSNSList::_Populate(void)
  354. {
  355. LPITEMIDLIST pidl = NULL;
  356. int iIndent, iDepth;
  357. HRESULT hr = S_OK;
  358. if (_fFullListValid)
  359. return S_OK; // Not needed, the drop down is already up todate.
  360. ASSERT(_hwnd);
  361. _PurgeAndResetComboBox();
  362. //
  363. // Fill in the current pidl and all it's parents.
  364. //
  365. hr = _GetPIDL(&pidl);
  366. iDepth = 0;
  367. iIndent = 0;
  368. if (pidl)
  369. {
  370. //
  371. // Compute the relative depth of pidl from the root.
  372. //
  373. LPITEMIDLIST pidlChild = pidl;
  374. if (ILIsRooted(pidl))
  375. pidlChild = ILGetNext(pidl);
  376. ASSERT(pidlChild);
  377. if (pidlChild)
  378. {
  379. //
  380. // Compute the maximum indentation level.
  381. //
  382. while (!ILIsEmpty(pidlChild))
  383. {
  384. pidlChild = _ILNext(pidlChild);
  385. iIndent++;
  386. }
  387. //
  388. // Save the maximum level.
  389. //
  390. iDepth = iIndent;
  391. //
  392. // Insert all those pidls.
  393. //
  394. LPITEMIDLIST pidlTemp = ILClone(pidl);
  395. do
  396. {
  397. _AddItem(pidlTemp, 0, iIndent);
  398. ILRemoveLastID(pidlTemp);
  399. iIndent--;
  400. } while (iIndent >= 0);
  401. ILFree(pidlTemp);
  402. }
  403. // Expand the root item.
  404. _FillOneLevel(0, 1, iDepth);
  405. // If this is not a rooted explorer, we expand MyComputer as well.
  406. // This is where we get our name "the drives dropdown".
  407. if (!ILIsRooted(pidl))
  408. _ExpandMyComputer(iDepth);
  409. }
  410. ILFree(pidl);
  411. _fFullListValid = TRUE;
  412. return hr;
  413. }
  414. //================================
  415. // *** Internal/Private Methods ***
  416. //=================================================================
  417. // General Band Functions
  418. //=================================================================
  419. /****************************************************\
  420. FUNCTION: _OnNotify
  421. DESCRIPTION:
  422. This function will handle WM_NOTIFY messages.
  423. \****************************************************/
  424. LRESULT CSNSList::_OnNotify(LPNMHDR pnm)
  425. {
  426. LRESULT lReturn = 0;
  427. // HACKHACK: combobox (comctl32\comboex.c) will pass a LPNMHDR, but it's really
  428. // a PNMCOMBOBOXEX (which has a first element of LPNMHDR). This function
  429. // can use this type cast iff it's guaranteed that this will only come from
  430. // a function that behaves in this perverse way.
  431. PNMCOMBOBOXEX pnmce = (PNMCOMBOBOXEX)pnm;
  432. ASSERT(pnm);
  433. switch (pnm->code)
  434. {
  435. case TTN_NEEDTEXT:
  436. {
  437. LPTOOLTIPTEXT pnmTT = (LPTOOLTIPTEXT)pnm;
  438. _GetURLToolTip(pnmTT->szText, ARRAYSIZE(pnmTT->szText));
  439. break;
  440. }
  441. case CBEN_DRAGBEGINA:
  442. {
  443. LPNMCBEDRAGBEGINA pnmbd = (LPNMCBEDRAGBEGINA)pnm;
  444. _OnDragBeginA(pnmbd);
  445. break;
  446. }
  447. case CBEN_DRAGBEGINW:
  448. {
  449. LPNMCBEDRAGBEGINW pnmbd = (LPNMCBEDRAGBEGINW)pnm;
  450. _OnDragBeginW(pnmbd);
  451. break;
  452. }
  453. case CBEN_GETDISPINFOW:
  454. _OnGetDispInfoW((PNMCOMBOBOXEXW)pnmce);
  455. break;
  456. case CBEN_GETDISPINFOA:
  457. _OnGetDispInfoA((PNMCOMBOBOXEXA) pnmce);
  458. break;
  459. case CBEN_DELETEITEM:
  460. if (pnmce->ceItem.lParam)
  461. ILFree((LPITEMIDLIST)pnmce->ceItem.lParam);
  462. break;
  463. default:
  464. lReturn = CAddressList::_OnNotify(pnm);
  465. break;
  466. }
  467. return lReturn;
  468. }
  469. /****************************************************\
  470. FUNCTION: _OnCommand
  471. DESCRIPTION:
  472. This function will handle WM_COMMAND messages.
  473. \****************************************************/
  474. LRESULT CSNSList::_OnCommand(WPARAM wParam, LPARAM lParam)
  475. {
  476. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  477. {
  478. case CBN_CLOSEUP:
  479. if (_fPurgePending)
  480. {
  481. _fPurgePending = FALSE;
  482. _PurgeAndResetComboBox();
  483. }
  484. break;
  485. }
  486. return CAddressList::_OnCommand(wParam, lParam);
  487. }
  488. /****************************************************\
  489. PARAMETERS:
  490. LPSTR pszUrl - String Buffer that will contain the
  491. URL as output.
  492. DWORD dwStrSize - Size of String buffer in characters.
  493. DESCRIPTION:
  494. Get the current URL.
  495. \****************************************************/
  496. HRESULT CSNSList::_GetURLToolTip(LPTSTR pszUrl, DWORD dwStrSize)
  497. {
  498. ASSERT(pszUrl);
  499. if (!pszUrl)
  500. return E_INVALIDARG;
  501. LPITEMIDLIST pidlCur;
  502. HRESULT hr = _GetPIDL(&pidlCur);
  503. if (S_OK == hr)
  504. {
  505. TCHAR szPidlName[MAX_URL_STRING];
  506. _GetPidlUI(pidlCur, szPidlName, ARRAYSIZE(szPidlName), NULL, NULL, SHGDN_FORPARSING, FALSE);
  507. lstrcpyn(pszUrl, szPidlName, dwStrSize);
  508. ILFree(pidlCur);
  509. }
  510. else
  511. pszUrl[0] = 0;
  512. return hr;
  513. }
  514. /****************************************************\
  515. FUNCTION: _GetPIDL
  516. DESCRIPTION:
  517. This function returns a pointer to the current
  518. PIDL. The caller will need to free the PIDL when
  519. it's no longer needed. S_FALSE will be returned
  520. if there isn't a current PIDL.
  521. \****************************************************/
  522. HRESULT CSNSList::_GetPIDL(LPITEMIDLIST * ppidl)
  523. {
  524. TraceMsg(TF_BAND|TF_GENERAL, "CSNSList: _GetPIDL() Begin");
  525. ASSERT(ppidl);
  526. if (!ppidl)
  527. return E_INVALIDARG;
  528. *ppidl = NULL;
  529. if (!_pbs)
  530. {
  531. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  532. TraceMsg(TF_BAND|TF_GENERAL, "CSNSList: _GetPIDL(), _cache.pidl=>%s<", Dbg_PidlStr(_cache.pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  533. if (_cache.pidl)
  534. *ppidl = ILClone(_cache.pidl);
  535. }
  536. else
  537. {
  538. _pbs->GetPidl(ppidl);
  539. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  540. TraceMsg(TF_BAND|TF_GENERAL, "CSNSList: _GetPIDL(), Current Pidl in TravelLog. PIDL=>%s<", Dbg_PidlStr(*ppidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  541. }
  542. if (*ppidl)
  543. return S_OK;
  544. TraceMsg(TF_BAND|TF_GENERAL, "CSNSList: _GetPIDL() End");
  545. return S_FALSE;
  546. }
  547. /****************************************************\
  548. _AddItem - Adds one pidl to the address window
  549. Input:
  550. pidl - the pidl to add
  551. iInsert - where to insert
  552. iIndent - indentation level of pidl
  553. \****************************************************/
  554. void CSNSList::_AddItem(LPITEMIDLIST pidl, int iInsert, int iIndent)
  555. {
  556. COMBOBOXEXITEM cei;
  557. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  558. TraceMsg(TF_BAND|TF_GENERAL, "CSNSList: _AddItem(). PIDL=>%s<", Dbg_PidlStr(pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  559. cei.pszText = LPSTR_TEXTCALLBACK;
  560. cei.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_INDENT | CBEIF_LPARAM;
  561. cei.lParam = (LPARAM)ILClone(pidl);
  562. cei.iIndent = iIndent;
  563. cei.iItem = iInsert;
  564. cei.iImage = I_IMAGECALLBACK;
  565. cei.iSelectedImage = I_IMAGECALLBACK;
  566. ASSERT(_hwnd);
  567. SendMessage(_hwnd, CBEM_INSERTITEM, 0, (LPARAM)&cei);
  568. }
  569. /****************************************************\
  570. _GetFullIDList - Get the pidl associated with a combo index
  571. Input:
  572. iItem - the item to retrieve
  573. Return:
  574. The pidl at that index.
  575. NULL on error.
  576. \****************************************************/
  577. LPITEMIDLIST CSNSList::_GetFullIDList(int iItem)
  578. {
  579. LPITEMIDLIST pidl;
  580. ASSERT(_hwnd);
  581. pidl = (LPITEMIDLIST)SendMessage(_hwnd, CB_GETITEMDATA, iItem, 0);
  582. if (pidl == (LPITEMIDLIST)CB_ERR)
  583. {
  584. pidl = NULL;
  585. }
  586. return pidl;
  587. }
  588. /****************************************************\
  589. _GetIndent - Get the indentation level of a combo index
  590. Input:
  591. iItem - the item to retrieve
  592. Return:
  593. The indentation level.
  594. -1 on error.
  595. \****************************************************/
  596. int CSNSList::_GetIndent(int iItem)
  597. {
  598. int iIndent;
  599. COMBOBOXEXITEM cbexItem;
  600. cbexItem.mask = CBEIF_INDENT;
  601. cbexItem.iItem = iItem;
  602. ASSERT(_hwnd);
  603. if (SendMessage(_hwnd, CBEM_GETITEM, 0, (LPARAM)&cbexItem))
  604. {
  605. iIndent = cbexItem.iIndent;
  606. }
  607. else
  608. {
  609. iIndent = -1;
  610. }
  611. return iIndent;
  612. }
  613. /****************************************************\
  614. FUNCTION: _ExpandMyComputer
  615. DESCRIPTION:
  616. Find the "My Computer" entry in the drop down
  617. list and expand it.
  618. \****************************************************/
  619. void CSNSList::_ExpandMyComputer(int iDepth)
  620. {
  621. LPITEMIDLIST pidlMyComputer = NULL;
  622. SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &pidlMyComputer);
  623. if (pidlMyComputer)
  624. {
  625. LPITEMIDLIST pidl = NULL;
  626. BOOL fFound = FALSE;
  627. int nIndex = 0;
  628. while (pidl = _GetFullIDList(nIndex))
  629. {
  630. if (ILIsEqual(pidl, pidlMyComputer))
  631. {
  632. fFound = TRUE;
  633. break;
  634. }
  635. nIndex++;
  636. }
  637. if (fFound)
  638. {
  639. _FillOneLevel(nIndex, 2, iDepth);
  640. }
  641. ILFree(pidlMyComputer);
  642. }
  643. }
  644. /****************************************************\
  645. _FillOneLevel - find and add all of the children of one combo item
  646. Input:
  647. iItem - the item to expand
  648. iIndent - the indentation level of the children to add
  649. iDepth - the deepest indented item currently in the list
  650. \****************************************************/
  651. void CSNSList::_FillOneLevel(int iItem, int iIndent, int iDepth)
  652. {
  653. LPITEMIDLIST pidl;
  654. pidl = _GetFullIDList(iItem);
  655. if (pidl)
  656. {
  657. HDPA hdpa;
  658. //
  659. // Fill hdps with all the children of this pidl.
  660. //
  661. hdpa = GetSortedIDList(pidl);
  662. if (hdpa)
  663. {
  664. int iCount, iInsert, i;
  665. LPITEMIDLIST pidlAlreadyThere;
  666. iCount = DPA_GetPtrCount(hdpa);
  667. //
  668. // The insert point starts right after parent.
  669. //
  670. iInsert = iItem + 1;
  671. //
  672. // Examine the next item. If it is at the same level as
  673. // our soon-to-be-added children, remember it so we don't add
  674. // it twice.
  675. //
  676. pidlAlreadyThere = _GetFullIDList(iInsert);
  677. if (pidlAlreadyThere && (_GetIndent(iInsert) != iIndent))
  678. {
  679. pidlAlreadyThere = NULL;
  680. }
  681. //
  682. // Loop through each child.
  683. //
  684. for (i=0; i<iCount; i++, iInsert++)
  685. {
  686. LPITEMIDLIST pidlChild = (LPITEMIDLIST)DPA_GetPtr(hdpa, i);
  687. LPITEMIDLIST pidlInsert = ILClone(pidl);
  688. if (pidlInsert)
  689. {
  690. ASSERT((LPVOID)pidlChild == (LPVOID)&pidlChild->mkid);
  691. pidlInsert = ILAppendID(pidlInsert, &pidlChild->mkid, TRUE);
  692. //
  693. // If this item was already added, we need to skip over it
  694. // and all of its children that have been inserted.
  695. // Because we know how this list was constructed,
  696. // we know the number of items is iDepth-iIndent.
  697. //
  698. if (pidlAlreadyThere && ILIsEqual(pidlInsert, pidlAlreadyThere))
  699. {
  700. //
  701. // Skip over this item (it's already been added) and
  702. // its children.
  703. //
  704. iInsert += iDepth - iIndent;
  705. }
  706. else
  707. {
  708. _AddItem(pidlInsert, iInsert, iIndent);
  709. }
  710. ILFree(pidlInsert);
  711. }
  712. }
  713. FreeSortedIDList(hdpa);
  714. }
  715. }
  716. }
  717. /****************************************************\
  718. _GetSelectedPidl - return the pidl of the combo selection
  719. Return:
  720. The selected pidl.
  721. NULL on error.
  722. \****************************************************/
  723. LPITEMIDLIST CSNSList::_GetSelectedPidl(void)
  724. {
  725. LPITEMIDLIST pidl = NULL;
  726. int iSel;
  727. ASSERT(_hwnd);
  728. iSel = ComboBox_GetCurSel(_hwnd);
  729. if (iSel >= 0)
  730. {
  731. pidl = _GetFullIDList(iSel);
  732. }
  733. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  734. TraceMsg(TF_BAND|TF_GENERAL, "CSNSList: _GetSelectedPidl(). PIDL=>%s<", Dbg_PidlStr(pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  735. return pidl;
  736. }
  737. /****************************************************\
  738. _FindItem - return the combo index associated with a pidl.
  739. Input:
  740. pidl - the pidl to find.
  741. Return:
  742. The combo index.
  743. -1 on error.
  744. \****************************************************/
  745. int CSNSList::_FindItem(LPITEMIDLIST pidl)
  746. {
  747. LPITEMIDLIST pidlCombo;
  748. int i = 0;
  749. int iRet = -1;
  750. int iMax;
  751. if (!pidl)
  752. return iRet; // Return -1 to show that nothing is selected.
  753. ASSERT(_hwnd);
  754. iMax = (int)SendMessage(_hwnd, CB_GETCOUNT, 0, 0);
  755. for (i=0; i<iMax; i++)
  756. {
  757. pidlCombo = (LPITEMIDLIST)SendMessage(_hwnd, CB_GETITEMDATA, i, 0);
  758. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  759. TraceMsg(TF_BAND|TF_GENERAL, "CSNSList: _FindItem(), ENUM ComboBox Pidls. PIDL=>%s<", Dbg_PidlStr(pidlCombo, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  760. if (pidlCombo && IEILIsEqual(pidl, pidlCombo, TRUE))
  761. {
  762. iRet = i;
  763. break;
  764. }
  765. }
  766. return iRet;
  767. }
  768. /****************************************************\
  769. FUNCTION: _GetPidlImage
  770. PARAMETERS:
  771. pidl - the pidl to get the icon index.
  772. piImage - Pointer to location to store result. (OPTIONAL)
  773. piSelectedImage - Pointer to location to store result. (OPTIONAL)
  774. DESCRIPTION:
  775. This function will retrieve information about the
  776. icon index for the pidl.
  777. \****************************************************/
  778. BOOL CSNSList::_GetPidlImage(LPCITEMIDLIST pidl, int *piImage, int *piSelectedImage)
  779. {
  780. int * piImagePriv = piImage;
  781. int * piSelectedImagePriv = piSelectedImage;
  782. int iNotUsed;
  783. BOOL fFound = FALSE;
  784. if (!piImagePriv)
  785. piImagePriv = &iNotUsed;
  786. if (!piSelectedImagePriv)
  787. piSelectedImagePriv = &iNotUsed;
  788. *piImagePriv = -1;
  789. *piSelectedImagePriv = -1;
  790. // PERF OPTIMIZATION: We will call directly to the browser window
  791. // which is a performance savings. We can only do this in the
  792. // following situation:
  793. // 1. We are connected to a browser window. (Bar only).
  794. // 2. The current pidl in the browser window is equal to the pidlParent.
  795. if (_pbp && (_pbp->IsConnected() == S_OK) && _cache.pidl)
  796. {
  797. if (ILIsEqual(pidl, _cache.pidl))
  798. {
  799. IOleCommandTarget * pcmdt;
  800. if (SUCCEEDED(_pbs->QueryInterface(IID_IOleCommandTarget, (LPVOID*)&pcmdt)))
  801. {
  802. VARIANT var = {0};
  803. HRESULT hresT = pcmdt->Exec(&CGID_ShellDocView, SHDVID_GETSYSIMAGEINDEX, 0, NULL, &var);
  804. if (SUCCEEDED(hresT))
  805. {
  806. if (var.vt==VT_I4)
  807. {
  808. *piImagePriv = var.lVal;
  809. *piSelectedImagePriv = var.lVal;
  810. }
  811. else
  812. {
  813. ASSERT(0);
  814. VariantClearLazy(&var);
  815. }
  816. }
  817. pcmdt->Release();
  818. }
  819. }
  820. }
  821. if (-1 == *piImagePriv || -1 == *piSelectedImagePriv)
  822. {
  823. _GetPidlIcon(pidl, piImagePriv, piSelectedImagePriv) ;
  824. }
  825. return TRUE;
  826. }
  827. // NOTE: show full file system path if we're running with IE4's shell32
  828. // (the Win95/NT4 shell and the Win2000 shell don't show the
  829. // full file system path in the address bar by default)
  830. HRESULT _GetAddressBarText(LPCITEMIDLIST pidl, DWORD dwFlags, LPTSTR pszName, UINT cchName)
  831. {
  832. HRESULT hr;
  833. *pszName = 0;
  834. if ((GetUIVersion() >= 5) &&
  835. ((dwFlags & (SHGDN_INFOLDER | SHGDN_FORPARSING)) == SHGDN_FORPARSING))
  836. {
  837. // NOTE: we are under GetUIVersion() >= 5 so we can use the "SH" versions of these API
  838. DWORD dwAttrib = SFGAO_FOLDER | SFGAO_LINK;
  839. SHGetAttributesOf(pidl, &dwAttrib);
  840. if (dwAttrib & SFGAO_FOLDER)
  841. {
  842. // folder objects respect the FullPathAddress flag, files (.htm) do not
  843. BOOL bFullTitle = TRUE; // As of WinXP, we default to true for Show Full Path In Address Bar
  844. DWORD cbData = SIZEOF(bFullTitle);
  845. SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER TEXT( "\\CabinetState"), TEXT("FullPathAddress"), NULL, &bFullTitle, &cbData);
  846. if (!bFullTitle)
  847. dwFlags = SHGDN_INFOLDER; // convert parsing name into normal name
  848. if ((dwFlags & SHGDN_FORPARSING) && (dwAttrib & SFGAO_LINK))
  849. {
  850. // folder shortcut special case
  851. IShellLinkA *psl; // Use A version for W95.
  852. if (SUCCEEDED(SHGetUIObjectFromFullPIDL(pidl, NULL, IID_PPV_ARG(IShellLinkA, &psl))))
  853. {
  854. LPITEMIDLIST pidlTarget;
  855. if (SUCCEEDED(psl->GetIDList(&pidlTarget)) && pidlTarget)
  856. {
  857. hr = SHGetNameAndFlags(pidlTarget, dwFlags | SHGDN_FORADDRESSBAR, pszName, cchName, NULL);
  858. ILFree(pidlTarget);
  859. }
  860. psl->Release();
  861. }
  862. }
  863. }
  864. }
  865. if (0 == *pszName)
  866. {
  867. if (!ILIsRooted(pidl))
  868. dwFlags |= SHGDN_FORADDRESSBAR;
  869. hr = IEGetNameAndFlags(pidl, dwFlags, pszName, cchName, NULL);
  870. if (SUCCEEDED(hr))
  871. {
  872. SHRemoveURLTurd(pszName);
  873. SHCleanupUrlForDisplay(pszName);
  874. }
  875. }
  876. return hr;
  877. }
  878. // This function will retrieve information about the
  879. // pidl so the ComboBox item can be displayed.
  880. // pidl - the pidl to examine.
  881. // pszName - gets the name. (OPTIONAL)
  882. // cchName - size of pszName buffer. (OPTIONAL)
  883. // piImage - gets the icon index. (OPTIONAL)
  884. // dwFlags - SHGDN_ flags
  885. // piSelectedImage - gets selected icon index. (OPTIONAL)
  886. BOOL CSNSList::_GetPidlUI(LPCITEMIDLIST pidl, LPTSTR pszName, int cchName, int *piImage, int *piSelectedImage, DWORD dwFlags, BOOL fIgnoreCache)
  887. {
  888. ASSERT(pidl);
  889. if (pszName && cchName)
  890. *pszName = 0;
  891. if (!fIgnoreCache && _cache.pidl && (pidl == _cache.pidl || ILIsEqual(pidl, _cache.pidl)))
  892. {
  893. lstrcpyn(pszName, _cache.szName, cchName);
  894. if (piImage)
  895. *piImage = _cache.iImage;
  896. if (piSelectedImage)
  897. *piSelectedImage = _cache.iSelectedImage;
  898. }
  899. else
  900. {
  901. if (pszName && cchName)
  902. _GetAddressBarText(pidl, dwFlags, pszName, cchName);
  903. if (piImage || piSelectedImage)
  904. {
  905. _GetPidlImage(pidl, piImage, piSelectedImage);
  906. }
  907. }
  908. return TRUE;
  909. }
  910. /****************************************************\
  911. PARAMETERS:
  912. pidl - the pidl to examine.
  913. RETURN:
  914. TRUE if cached pidl is changed, FALSE o/w.
  915. DESCRIPTION:
  916. This function will set the cache to the pidl
  917. that was passed in. The cached pidl will be freeded.
  918. The caller still needs to free the pidl that was passed
  919. in because it will be cloned.
  920. \****************************************************/
  921. BOOL CSNSList::_SetCachedPidl(LPCITEMIDLIST pidl)
  922. {
  923. BOOL fCacheChanged = FALSE;
  924. if ((_cache.pidl == NULL) || !ILIsEqual(_cache.pidl, pidl))
  925. {
  926. fCacheChanged = TRUE;
  927. _GetPidlUI(pidl, _cache.szName, ARRAYSIZE(_cache.szName),
  928. &_cache.iImage, &_cache.iSelectedImage, SHGDN_FORPARSING, FALSE);
  929. if (_cache.pidl)
  930. ILFree(_cache.pidl);
  931. _cache.pidl = ILClone(pidl);
  932. }
  933. return fCacheChanged;
  934. }
  935. /****************************************************\
  936. PARAMETER:
  937. pnmce - PNMCOMBOBOXEXA which will come from the ComboBoxEx
  938. when in AddressBand mode. The AddressBar uses
  939. the ANSI version of this data structure.
  940. DESCRIPTION:
  941. Handle the WM_NOTIFY/CBEN_GETDISPINFO message.
  942. We will call into _OnGetDispInfoW() to handle the
  943. call and then thunk the Text back into ANSI on
  944. the way out.
  945. Return:
  946. Standard WM_NOTIFY result.
  947. \****************************************************/
  948. LRESULT CSNSList::_OnGetDispInfoA(PNMCOMBOBOXEXA pnmce)
  949. {
  950. LRESULT lResult = 0;
  951. LPWSTR pszUniTemp;
  952. LPSTR pszAnsiDest;
  953. if (pnmce->ceItem.mask & (CBEIF_TEXT))
  954. {
  955. pszUniTemp = (LPWSTR)LocalAlloc(LPTR, pnmce->ceItem.cchTextMax * SIZEOF(WCHAR));
  956. if (pszUniTemp)
  957. {
  958. pszAnsiDest = pnmce->ceItem.pszText;
  959. ((PNMCOMBOBOXEXW)pnmce)->ceItem.pszText = pszUniTemp;
  960. lResult = _OnGetDispInfoW((PNMCOMBOBOXEXW)pnmce);
  961. SHUnicodeToAnsi(pszUniTemp, pszAnsiDest, pnmce->ceItem.cchTextMax);
  962. pnmce->ceItem.pszText = pszAnsiDest;
  963. LocalFree((VOID*)pszUniTemp);
  964. }
  965. }
  966. return lResult;
  967. }
  968. /****************************************************\
  969. Handle the WM_NOTIFY/CBEN_GETDISPINFO message.
  970. Input:
  971. pnmce - the notify message.
  972. Return:
  973. Standard WM_NOTIFY result.
  974. \****************************************************/
  975. LRESULT CSNSList::_OnGetDispInfoW(PNMCOMBOBOXEXW pnmce)
  976. {
  977. if (pnmce->ceItem.lParam &&
  978. pnmce->ceItem.mask & (CBEIF_SELECTEDIMAGE | CBEIF_IMAGE | CBEIF_TEXT))
  979. {
  980. LPITEMIDLIST pidl = (LPITEMIDLIST)pnmce->ceItem.lParam;
  981. // Normal case - ask shell to give us icon and text of a pidl.
  982. if (_GetPidlUI(pidl, pnmce->ceItem.pszText, pnmce->ceItem.cchTextMax,
  983. &pnmce->ceItem.iImage, &pnmce->ceItem.iSelectedImage,
  984. SHGDN_INFOLDER, TRUE))
  985. {
  986. pnmce->ceItem.mask = CBEIF_DI_SETITEM | CBEIF_SELECTEDIMAGE |
  987. CBEIF_IMAGE | CBEIF_TEXT;
  988. }
  989. }
  990. return 0;
  991. }
  992. /*******************************************************************
  993. DESCRIPTION:
  994. This function will set the CShellUrl parameter to the item
  995. in the Drop Down list that is indexed by nIndex.
  996. ********************************************************************/
  997. HRESULT CSNSList::SetToListIndex(int nIndex, LPVOID pvShelLUrl)
  998. {
  999. HRESULT hr = E_FAIL;
  1000. LPITEMIDLIST pidl = _GetFullIDList(nIndex);
  1001. CShellUrl * psuURL = (CShellUrl *) pvShelLUrl;
  1002. if (pidl)
  1003. hr = psuURL->SetPidl(pidl);
  1004. ASSERT(SUCCEEDED(hr)); // Should Always Succeed.
  1005. return hr;
  1006. }
  1007. LPITEMIDLIST CSNSList::_GetDragDropPidl(LPNMCBEDRAGBEGINW pnmcbe)
  1008. {
  1009. LPITEMIDLIST pidl;
  1010. if (pnmcbe->iItemid == -1)
  1011. {
  1012. pidl = ILClone(_cache.pidl);
  1013. }
  1014. else
  1015. {
  1016. pidl = ILClone(_GetFullIDList(pnmcbe->iItemid));
  1017. }
  1018. return pidl;
  1019. }
  1020. HRESULT CSNSList::FileSysChangeAL(DWORD dw, LPCITEMIDLIST *ppidl)
  1021. {
  1022. switch (dw)
  1023. {
  1024. case SHCNE_UPDATEIMAGE:
  1025. case SHCNE_UPDATEITEM:
  1026. _PopulateOneItem(TRUE);
  1027. break;
  1028. default:
  1029. // Don't purge the combo box if it is dropped; that confuses
  1030. // too many people. For example, addrlist.cpp caches the
  1031. // *index* of the current item, and purging causes all the indexes
  1032. // to change...
  1033. if (SendMessage(_hwnd, CB_GETDROPPEDSTATE, 0, 0)) {
  1034. _fPurgePending = TRUE;
  1035. } else {
  1036. _PurgeAndResetComboBox();
  1037. }
  1038. break;
  1039. }
  1040. return S_OK;
  1041. }
  1042. #endif