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.

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