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.

7973 lines
251 KiB

  1. #include "priv.h"
  2. #include "nsc.h"
  3. #include "resource.h"
  4. #include "subsmgr.h"
  5. #include "favorite.h" //for IsSubscribed()
  6. #include "chanmgr.h"
  7. #include "chanmgrp.h"
  8. #include <mstask.h> // TASK_TRIGGER
  9. #include "dpastuff.h"
  10. #include <findhlp.h>
  11. #include <ntquery.h> // defines some values used for fmtid and pid
  12. #include "nsctask.h"
  13. #include <mluisupp.h>
  14. #include <varutil.h>
  15. #include <dobjutil.h>
  16. #define IDH_ORGFAVS_LIST 50490 // defined in iehelpid.h (can't include due to conflicts)
  17. #define TF_NSC 0x00002000
  18. #define ID_NSC_SUBCLASS 359
  19. #define ID_NSCTREE (DWORD)'NSC'
  20. #define IDT_SELECTION 135
  21. #ifndef UNIX
  22. #define DEFAULT_PATHSTR "C:\\"
  23. #else
  24. #define DEFAULT_PATHSTR "/"
  25. #endif
  26. #define LOGOGAP 2 // all kinds of things
  27. #define DYITEM 17
  28. #define DXYFRAMESEL 1
  29. const DEFAULTORDERPOSITION = 32000;
  30. // HTML displays hard scripting errors if methods on automation interfaces
  31. // return FAILED(). This macro will fix these.
  32. #define FIX_SCRIPTING_ERRORS(hr) (FAILED(hr) ? S_FALSE : hr)
  33. #define DEFINE_SCID(name, fmtid, pid) const SHCOLUMNID name = { fmtid, pid }
  34. DEFINE_SCID(SCID_NAME, PSGUID_STORAGE, PID_STG_NAME); // defined in shell32!prop.cpp
  35. DEFINE_SCID(SCID_ATTRIBUTES, PSGUID_STORAGE, PID_STG_ATTRIBUTES);
  36. DEFINE_SCID(SCID_TYPE, PSGUID_STORAGE, PID_STG_STORAGETYPE);
  37. DEFINE_SCID(SCID_SIZE, PSGUID_STORAGE, PID_STG_SIZE);
  38. DEFINE_SCID(SCID_CREATETIME, PSGUID_STORAGE, PID_STG_CREATETIME);
  39. #define IsEqualSCID(a, b) (((a).pid == (b).pid) && IsEqualIID((a).fmtid, (b).fmtid))
  40. HRESULT CheckForExpandOnce(HWND hwndTree, HTREEITEM hti);
  41. // from util.cpp
  42. // same guid as in bandisf.cpp
  43. // {F47162A0-C18F-11d0-A3A5-00C04FD706EC}
  44. static const GUID TOID_ExtractImage = { 0xf47162a0, 0xc18f, 0x11d0, { 0xa3, 0xa5, 0x0, 0xc0, 0x4f, 0xd7, 0x6, 0xec } };
  45. //from nsctask.cpp
  46. EXTERN_C const GUID TASKID_IconExtraction; // = { 0xeb30900c, 0x1ac4, 0x11d2, { 0x83, 0x83, 0x0, 0xc0, 0x4f, 0xd9, 0x18, 0xd0 } };
  47. BOOL IsChannelFolder(LPCWSTR pwzPath, LPWSTR pwzChannelURL);
  48. typedef struct
  49. {
  50. DWORD iIcon : 12;
  51. DWORD iOpenIcon : 12;
  52. DWORD nFlags : 4;
  53. DWORD nMagic : 4;
  54. } NSC_ICONCALLBACKINFO;
  55. typedef struct
  56. {
  57. DWORD iOverlayIndex : 28;
  58. DWORD nMagic : 4;
  59. } NSC_OVERLAYCALLBACKINFO;
  60. struct NSC_BKGDENUMDONEDATA
  61. {
  62. ~NSC_BKGDENUMDONEDATA()
  63. {
  64. ILFree(pidl);
  65. ILFree(pidlExpandingTo);
  66. OrderList_Destroy(&hdpa, TRUE);
  67. }
  68. NSC_BKGDENUMDONEDATA * pNext;
  69. LPITEMIDLIST pidl;
  70. HTREEITEM hitem;
  71. DWORD dwSig;
  72. HDPA hdpa;
  73. LPITEMIDLIST pidlExpandingTo;
  74. DWORD dwOrderSig;
  75. UINT uDepth;
  76. BOOL fUpdate;
  77. BOOL fUpdatePidls;
  78. };
  79. //if you don't remove the selection, treeview will expand everything below the current selection
  80. void TreeView_DeleteAllItemsQuickly(HWND hwnd)
  81. {
  82. TreeView_SelectItem(hwnd, NULL);
  83. TreeView_DeleteAllItems(hwnd);
  84. }
  85. #define NSC_CHILDREN_REMOVE 0
  86. #define NSC_CHILDREN_ADD 1
  87. #define NSC_CHILDREN_FORCE 2
  88. #define NSC_CHILDREN_CALLBACK 3
  89. void TreeView_SetChildren(HWND hwnd, HTREEITEM hti, UINT uFlag)
  90. {
  91. TV_ITEM tvi;
  92. tvi.mask = TVIF_CHILDREN | TVIF_HANDLE; // only change the number of children
  93. tvi.hItem = hti;
  94. switch (uFlag)
  95. {
  96. case NSC_CHILDREN_REMOVE:
  97. tvi.cChildren = IsOS(OS_WHISTLERORGREATER) ? I_CHILDRENAUTO : 0;
  98. break;
  99. case NSC_CHILDREN_ADD:
  100. tvi.cChildren = IsOS(OS_WHISTLERORGREATER) ? I_CHILDRENAUTO : 1;
  101. break;
  102. case NSC_CHILDREN_FORCE:
  103. tvi.cChildren = 1;
  104. break;
  105. case NSC_CHILDREN_CALLBACK:
  106. tvi.cChildren = I_CHILDRENCALLBACK;
  107. break;
  108. default:
  109. ASSERTMSG(FALSE, "wrong parameter passed to TreeView_SetChildren in nsc");
  110. break;
  111. }
  112. TreeView_SetItem(hwnd, &tvi);
  113. }
  114. void TreeView_DeleteChildren(HWND hwnd, HTREEITEM hti)
  115. {
  116. for (HTREEITEM htiTemp = TreeView_GetChild(hwnd, hti); htiTemp;)
  117. {
  118. HTREEITEM htiDelete = htiTemp;
  119. htiTemp = TreeView_GetNextSibling(hwnd, htiTemp);
  120. TreeView_DeleteItem(hwnd, htiDelete);
  121. }
  122. }
  123. BOOL IsParentOfItem(HWND hwnd, HTREEITEM htiParent, HTREEITEM htiChild)
  124. {
  125. for (HTREEITEM hti = htiChild; (hti != TVI_ROOT) && (hti != NULL); hti = TreeView_GetParent(hwnd, hti))
  126. if (hti == htiParent)
  127. return TRUE;
  128. return FALSE;
  129. }
  130. STDAPI CNscTree_CreateInstance(IUnknown * punkOuter, IUnknown ** ppunk, LPCOBJECTINFO poi)
  131. {
  132. HRESULT hr;
  133. CComObject<CNscTree> *pnsct;
  134. CComObject<CNscTree>::CreateInstance(&pnsct);
  135. if (pnsct)
  136. {
  137. hr = S_OK;
  138. *ppunk = pnsct->GetUnknown();
  139. ASSERT(*ppunk);
  140. (*ppunk)->AddRef(); // atl doesn't addref in create instance or getunknown about
  141. }
  142. else
  143. {
  144. *ppunk = NULL;
  145. hr = E_OUTOFMEMORY;
  146. }
  147. return hr;
  148. }
  149. INSCTree2 *CNscTree_CreateInstance(void)
  150. {
  151. INSCTree2 *pnsct = NULL;
  152. IUnknown *punk;
  153. if (SUCCEEDED(CNscTree_CreateInstance(NULL, &punk, NULL)))
  154. {
  155. punk->QueryInterface(IID_PPV_ARG(INSCTree2, &pnsct));
  156. punk->Release();
  157. }
  158. return pnsct;
  159. }
  160. //////////////////////////////////////////////////////////////////////////////
  161. CNscTree::CNscTree() : _iDragSrc(-1), _iDragDest(-1), _fOnline(!SHIsGlobalOffline())
  162. {
  163. // This object is a COM object so it will always be on the heap.
  164. // ASSERT that our member variables were zero initialized.
  165. ASSERT(!_fInitialized);
  166. ASSERT(!_dwTVFlags);
  167. ASSERT(!_hdpaColumns);
  168. ASSERT(!_hdpaViews);
  169. m_bWindowOnly = TRUE;
  170. _mode = MODE_FAVORITES | MODE_CONTROL; //everyone sets the mode except organize favorites
  171. _csidl = CSIDL_FAVORITES;
  172. _dwFlags = NSS_DROPTARGET | NSS_BROWSERSELECT; //this should be default only in control mode
  173. _grfFlags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
  174. _ulSortCol = _ulDisplayCol = (ULONG)-1;
  175. // Enable the notifications from wininet that tell us when to gray items
  176. // or update a pinned glyph
  177. _inetNotify.Enable();
  178. InitializeCriticalSection(&_csBackgroundData);
  179. }
  180. CNscTree::~CNscTree()
  181. {
  182. Pidl_Set(&_pidlSelected, NULL);
  183. // This needs to be destroyed or we leak the icon handle.
  184. if (_hicoPinned)
  185. DestroyIcon(_hicoPinned);
  186. if (_hdpaColumns)
  187. {
  188. DPA_DestroyCallback(_hdpaColumns, DPADeleteItemCB, NULL);
  189. _hdpaColumns = NULL;
  190. }
  191. if (_hdpaViews)
  192. {
  193. DPA_DestroyCallback(_hdpaViews, DPADeletePidlsCB, NULL);
  194. _hdpaViews = NULL;
  195. }
  196. EnterCriticalSection(&_csBackgroundData);
  197. while (_pbeddList)
  198. {
  199. // Extract the first element of the list
  200. NSC_BKGDENUMDONEDATA * pbedd = _pbeddList;
  201. _pbeddList = pbedd->pNext;
  202. delete pbedd;
  203. }
  204. LeaveCriticalSection(&_csBackgroundData);
  205. DeleteCriticalSection(&_csBackgroundData);
  206. }
  207. void CNscTree::_ReleaseCachedShellFolder()
  208. {
  209. ATOMICRELEASE(_psfCache);
  210. ATOMICRELEASE(_psf2Cache);
  211. _ulSortCol = _ulDisplayCol = (ULONG)-1;
  212. _htiCache = NULL;
  213. }
  214. #ifdef DEBUG
  215. void CNscTree::TraceHTREE(HTREEITEM hti, LPCTSTR pszDebugMsg)
  216. {
  217. TCHAR szDebug[MAX_PATH] = TEXT("Root");
  218. if (hti != TVI_ROOT && hti)
  219. {
  220. TVITEM tvi;
  221. tvi.mask = TVIF_TEXT | TVIF_HANDLE;
  222. tvi.hItem = hti;
  223. tvi.pszText = szDebug;
  224. tvi.cchTextMax = MAX_PATH;
  225. TreeView_GetItem(_hwndTree, &tvi);
  226. }
  227. TraceMsg(TF_NSC, "NSCBand: %s - %s", pszDebugMsg, szDebug);
  228. }
  229. void CNscTree::TracePIDL(LPCITEMIDLIST pidl, LPCTSTR pszDebugMsg)
  230. {
  231. TCHAR szDebugName[MAX_URL_STRING] = TEXT("Desktop");
  232. STRRET str;
  233. if (_psfCache &&
  234. SUCCEEDED(_psfCache->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str)))
  235. {
  236. StrRetToBuf(&str, pidl, szDebugName, ARRAYSIZE(szDebugName));
  237. }
  238. TraceMsg(TF_NSC, "NSCBand: %s - %s", pszDebugMsg, szDebugName);
  239. }
  240. void CNscTree::TracePIDLAbs(LPCITEMIDLIST pidl, LPCTSTR pszDebugMsg)
  241. {
  242. TCHAR szDebugName[MAX_URL_STRING] = TEXT("Desktop");
  243. IEGetDisplayName(pidl, szDebugName, SHGDN_FORPARSING);
  244. TraceMsg(TF_NSC, "NSCBand: %s - %s", pszDebugMsg, szDebugName);
  245. }
  246. #endif
  247. void CNscTree::_AssignPidl(PORDERITEM poi, LPITEMIDLIST pidlNew)
  248. {
  249. if (poi && pidlNew)
  250. {
  251. // We are assuming that its only replacing the last element...
  252. ASSERT(ILFindLastID(pidlNew) == pidlNew);
  253. LPITEMIDLIST pidlParent = ILCloneParent(poi->pidl);
  254. if (pidlParent)
  255. {
  256. LPITEMIDLIST pidlT = ILCombine(pidlParent, pidlNew);
  257. if (pidlT)
  258. {
  259. Pidl_Set(&poi->pidl, pidlT);
  260. ILFree(pidlT);
  261. }
  262. ILFree(pidlParent);
  263. }
  264. }
  265. }
  266. /*****************************************************\
  267. DESCRIPTION:
  268. We want to unsubclass/subclass everytime we
  269. change roots so we get the correct notifications
  270. for everything in that subtree of the shell
  271. name space.
  272. \*****************************************************/
  273. void CNscTree::_SubClass(LPCITEMIDLIST pidlRoot)
  274. {
  275. LPITEMIDLIST pidlToFree = NULL;
  276. if (NULL == pidlRoot) // (NULL == CSIDL_DESKTOP)
  277. {
  278. SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, (LPITEMIDLIST *) &pidlRoot);
  279. pidlToFree = (LPITEMIDLIST) pidlRoot;
  280. }
  281. // It's necessary
  282. if (!_fSubClassed && pidlRoot)
  283. {
  284. if (_SubclassWindow(_hwndTree))
  285. {
  286. _RegisterWindow(_hwndTree, pidlRoot,
  287. SHCNE_DRIVEADD|SHCNE_CREATE|SHCNE_MKDIR|SHCNE_DRIVEREMOVED|
  288. SHCNE_DELETE|SHCNE_RMDIR|SHCNE_RENAMEITEM|SHCNE_RENAMEFOLDER|
  289. SHCNE_MEDIAINSERTED|SHCNE_MEDIAREMOVED|SHCNE_NETUNSHARE|SHCNE_NETSHARE|
  290. SHCNE_UPDATEITEM|SHCNE_UPDATEIMAGE|SHCNE_ASSOCCHANGED|
  291. SHCNE_UPDATEDIR | SHCNE_EXTENDED_EVENT,
  292. ((_mode & MODE_HISTORY) ? SHCNRF_ShellLevel : SHCNRF_ShellLevel | SHCNRF_InterruptLevel));
  293. }
  294. ASSERT(_hwndTree);
  295. _fSubClassed = SetWindowSubclass(_hwndTree, s_SubClassTreeWndProc,
  296. ID_NSCTREE, (DWORD_PTR)this);
  297. }
  298. if (pidlToFree) // Did we have to alloc our own pidl?
  299. ILFree(pidlToFree); // Yes.
  300. }
  301. /*****************************************************\
  302. DESCRIPTION:
  303. We want to unsubclass/subclass everytime we
  304. change roots so we get the correct notifications
  305. for everything in that subtree of the shell
  306. name space.
  307. \*****************************************************/
  308. void CNscTree::_UnSubClass(void)
  309. {
  310. if (_fSubClassed)
  311. {
  312. _fSubClassed = FALSE;
  313. RemoveWindowSubclass(_hwndTree, s_SubClassTreeWndProc, ID_NSCTREE);
  314. _UnregisterWindow(_hwndTree);
  315. _UnsubclassWindow(_hwndTree);
  316. }
  317. }
  318. void CNscTree::_ReleasePidls(void)
  319. {
  320. Pidl_Set(&_pidlRoot, NULL);
  321. Pidl_Set(&_pidlNavigatingTo, NULL);
  322. }
  323. HRESULT CNscTree::ShowWindow(BOOL fShow)
  324. {
  325. if (fShow)
  326. _TvOnShow();
  327. else
  328. _TvOnHide();
  329. return S_OK;
  330. }
  331. HRESULT CNscTree::SetSite(IUnknown *punkSite)
  332. {
  333. ATOMICRELEASE(_pnscProxy);
  334. if (!punkSite)
  335. {
  336. // We need to prepare to go away and squirel
  337. // away the currently selected pidl(s) because
  338. // the caller may call INSCTree::GetSelectedItem()
  339. // after the tree is gone.
  340. _OnWindowCleanup();
  341. }
  342. else
  343. {
  344. punkSite->QueryInterface(IID_PPV_ARG(INamespaceProxy, &_pnscProxy));
  345. }
  346. return CObjectWithSite::SetSite(punkSite);
  347. }
  348. DWORD BackgroundDestroyScheduler(void *pvData)
  349. {
  350. IShellTaskScheduler *pTaskScheduler = (IShellTaskScheduler *)pvData;
  351. pTaskScheduler->Release();
  352. return 0;
  353. }
  354. EXTERN_C const GUID TASKID_BackgroundEnum;
  355. HRESULT CNscTree::_OnWindowCleanup(void)
  356. {
  357. _fClosing = TRUE;
  358. if (_hwndTree)
  359. {
  360. ASSERT(::IsWindow(_hwndTree)); // make sure it has not been destroyed (it is a child)
  361. _TvOnHide();
  362. ::KillTimer(_hwndTree, IDT_SELECTION);
  363. ::SendMessage(_hwndTree, WM_SETREDRAW, FALSE, 0);
  364. TreeView_DeleteAllItemsQuickly(_hwndTree);
  365. _UnSubClass();
  366. _hwndTree = NULL;
  367. }
  368. // Squirel away the selected pidl in case the caller asks for it after the
  369. // treeview is gone.
  370. if (!_fIsSelectionCached)
  371. {
  372. _fIsSelectionCached = TRUE;
  373. Pidl_Set(&_pidlSelected, NULL);
  374. GetSelectedItem(&_pidlSelected, 0);
  375. }
  376. ATOMICRELEASE(_pFilter);
  377. if (_pTaskScheduler)
  378. {
  379. _pTaskScheduler->RemoveTasks(TOID_NULL, ITSAT_DEFAULT_LPARAM, FALSE);
  380. if (_pTaskScheduler->CountTasks(TASKID_BackgroundEnum) == 0)
  381. {
  382. _pTaskScheduler->Release();
  383. }
  384. // We need to keep Browseui loaded because we depend on the CShellTaskScheduler
  385. // to be still around when our background task executes. Browseui can be unloaded by COM when
  386. // we CoUninit from this thread.
  387. else if (!SHQueueUserWorkItem(BackgroundDestroyScheduler, (void *)_pTaskScheduler, 0, NULL, NULL, "browseui.dll", 0))
  388. {
  389. _pTaskScheduler->Release();
  390. }
  391. _pTaskScheduler = NULL;
  392. }
  393. _ReleasePidls();
  394. _ReleaseCachedShellFolder();
  395. return S_OK;
  396. }
  397. ITEMINFO *CNscTree::_GetTreeItemInfo(HTREEITEM hti)
  398. {
  399. TV_ITEM tvi;
  400. tvi.mask = TVIF_PARAM | TVIF_HANDLE;
  401. tvi.hItem = hti;
  402. if (!TreeView_GetItem(_hwndTree, &tvi))
  403. return NULL;
  404. return (ITEMINFO *)tvi.lParam;
  405. }
  406. PORDERITEM CNscTree::_GetTreeOrderItem(HTREEITEM hti)
  407. {
  408. ITEMINFO *pii = _GetTreeItemInfo(hti);
  409. return pii ? pii->poi : NULL;
  410. }
  411. // builds a fully qualified IDLIST from a given tree node by walking up the tree
  412. // be sure to free this when you are done!
  413. LPITEMIDLIST CNscTree::_GetFullIDList(HTREEITEM hti)
  414. {
  415. LPITEMIDLIST pidl, pidlT = NULL;
  416. if ((hti == TVI_ROOT) || (hti == NULL)) // evil root
  417. {
  418. pidlT = ILClone(_pidlRoot);
  419. return pidlT;
  420. }
  421. // now lets get the information about the item
  422. PORDERITEM poi = _GetTreeOrderItem(hti);
  423. if (!poi)
  424. {
  425. return NULL;
  426. }
  427. pidl = ILClone(poi->pidl);
  428. if (pidl && _pidlRoot)
  429. {
  430. while ((hti = TreeView_GetParent(_hwndTree, hti)))
  431. {
  432. poi = _GetTreeOrderItem(hti);
  433. if (!poi)
  434. return pidl; // will assume I messed up...
  435. if (poi->pidl)
  436. pidlT = ILCombine(poi->pidl, pidl);
  437. else
  438. pidlT = NULL;
  439. ILFree(pidl);
  440. pidl = pidlT;
  441. if (pidl == NULL)
  442. break; // outta memory
  443. }
  444. if (pidl)
  445. {
  446. // MODE_NORMAL has the pidl root in the tree
  447. if (_mode != MODE_NORMAL)
  448. {
  449. pidlT = ILCombine(_pidlRoot, pidl); // gotta get the silent root
  450. ILFree(pidl);
  451. }
  452. else
  453. pidlT = pidl;
  454. }
  455. }
  456. return pidlT;
  457. }
  458. BOOL _IsItemFileSystem(IShellFolder *psf, LPCITEMIDLIST pidl)
  459. {
  460. return (SHGetAttributes(psf, pidl, SFGAO_FOLDER | SFGAO_FILESYSTEM) == (SFGAO_FOLDER | SFGAO_FILESYSTEM));
  461. }
  462. HTREEITEM CNscTree::_AddItemToTree(HTREEITEM htiParent, LPITEMIDLIST pidl,
  463. int cChildren, int iPos, HTREEITEM htiAfter, /* = TVI_LAST*/
  464. BOOL fCheckForDups, /* = TRUE */ BOOL fMarked /*= FALSE */)
  465. {
  466. HTREEITEM htiRet = NULL;
  467. BOOL fCached;
  468. // So we need to cached the shell folder of the parent item. But, this is a little interesting:
  469. if (_mode == MODE_NORMAL && htiParent == TVI_ROOT)
  470. {
  471. // In "Normal" mode, or "Display root in NSC" mode, there is only 1 item that is parented to
  472. // TVI_ROOT. So when we do an _AddItemToTree, we need the shell folder that contains _pidlRoot or
  473. // the Parent of TVI_ROOT.
  474. fCached = (NULL != _CacheParentShellFolder(htiParent, NULL));
  475. }
  476. else
  477. {
  478. // But, in the "Favorites, Control or History" if htiParent is TVI_ROOT, then we are not adding _pidlRoot,
  479. // so we actually need the folder that IS TVI_ROOT.
  480. fCached = _CacheShellFolder(htiParent);
  481. }
  482. if (fCached)
  483. {
  484. LPITEMIDLIST pidlNew = ILClone(pidl);
  485. if (pidlNew)
  486. {
  487. PORDERITEM poi = OrderItem_Create(pidlNew, iPos);
  488. if (poi)
  489. {
  490. ITEMINFO *pii = (ITEMINFO *)LocalAlloc(LPTR, sizeof(*pii));
  491. if (pii)
  492. {
  493. pii->dwSig = _dwSignature++;
  494. pii->poi = poi;
  495. // For the normal case, we need a relative pidl for this add, but the lParam needs to have a full
  496. // pidl (This is so that arbitrary mounting works, as well as desktop case).
  497. pidl = pidlNew; //reuse variable
  498. if (_mode == MODE_NORMAL && htiParent == TVI_ROOT)
  499. {
  500. pidl = ILFindLastID(pidl);
  501. }
  502. if (!fCheckForDups || (NULL == (htiRet = _FindChild(_psfCache, htiParent, pidl))))
  503. {
  504. TV_INSERTSTRUCT tii;
  505. // Initialize item to add with callback for everything
  506. tii.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_CHILDREN | TVIF_STATE;
  507. tii.hParent = htiParent;
  508. tii.hInsertAfter = htiAfter;
  509. tii.item.iImage = I_IMAGECALLBACK;
  510. tii.item.iSelectedImage = I_IMAGECALLBACK;
  511. tii.item.pszText = LPSTR_TEXTCALLBACK;
  512. tii.item.cChildren = cChildren;
  513. tii.item.lParam = (LPARAM)pii;
  514. tii.item.stateMask = TVIS_STATEIMAGEMASK;
  515. tii.item.state = (fMarked ? NSC_TVIS_MARKED : 0);
  516. #ifdef DEBUG
  517. TracePIDL(pidl, TEXT("Inserting"));
  518. TraceMsg(TF_NSC, "_AddItemToTree(htiParent=%#08lx, htiAfter=%#08lx, fCheckForDups=%d, _psfCache=%#08lx)",
  519. htiParent, htiAfter, fCheckForDups, _psfCache);
  520. #endif // DEBUG
  521. pii->fNavigable = !_IsItemFileSystem(_psfCache, pidl);
  522. htiRet = TreeView_InsertItem(_hwndTree, &tii);
  523. if (htiRet)
  524. {
  525. pii = NULL; // don't free
  526. poi = NULL; // don't free
  527. pidlNew = NULL;
  528. }
  529. }
  530. if (pii)
  531. {
  532. LocalFree(pii);
  533. pii = NULL;
  534. }
  535. }
  536. if (poi)
  537. OrderItem_Free(poi, FALSE);
  538. }
  539. ILFree(pidlNew);
  540. }
  541. }
  542. return htiRet;
  543. }
  544. DWORD CNscTree::_SetExStyle(DWORD dwExStyle)
  545. {
  546. DWORD dwOldStyle = _dwExStyle;
  547. _dwExStyle = dwExStyle;
  548. return dwOldStyle;
  549. }
  550. DWORD CNscTree::_SetStyle(DWORD dwStyle)
  551. {
  552. dwStyle |= TVS_EDITLABELS | TVS_SHOWSELALWAYS | TVS_NONEVENHEIGHT;
  553. if (dwStyle & WS_HSCROLL)
  554. dwStyle &= ~WS_HSCROLL;
  555. else
  556. dwStyle |= TVS_NOHSCROLL;
  557. if (TVS_HASLINES & dwStyle)
  558. dwStyle &= ~TVS_FULLROWSELECT; // If it has TVS_HASLINES, it can't have TVS_FULLROWSELECT
  559. // If the parent window is mirrored then the treeview window will inheret the mirroring flag
  560. // And we need the reading order to be Left to right, which is the right to left in the mirrored mode.
  561. if (((_mode & MODE_HISTORY) || (MODE_NORMAL == _mode)) && IS_WINDOW_RTL_MIRRORED(_hwndParent))
  562. {
  563. // This means left to right reading order because this window will be mirrored.
  564. dwStyle |= TVS_RTLREADING;
  565. }
  566. // According to Bug#241601, Tooltips display too quickly. The problem is
  567. // the original designer of the InfoTips in the Treeview merged the "InfoTip" tooltip and
  568. // the "I'm too small to display correctly" tooltips. This is really unfortunate because you
  569. // cannot control the display of these tooltips independantly. Therefore we are turning off
  570. // infotips in normal mode. (lamadio) 4.7.99
  571. AssertMsg(_mode != MODE_NORMAL || !(dwStyle & TVS_INFOTIP), TEXT("can't have infotip with normal mode in nsc"));
  572. DWORD dwOldStyle = _style;
  573. _style = dwStyle | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VSCROLL | WS_TABSTOP;
  574. _fSingleExpand = BOOLIFY(_style & TVS_SINGLEEXPAND);
  575. return dwOldStyle;
  576. }
  577. HRESULT CNscTree::CreateTree(HWND hwndParent, DWORD dwStyles, HWND *phwnd)
  578. {
  579. return CreateTree2(hwndParent, dwStyles, 0, phwnd);
  580. }
  581. HRESULT CNscTree::CreateTree2(HWND hwndParent, DWORD dwStyle, DWORD dwExStyle, HWND *phwnd)
  582. {
  583. _fIsSelectionCached = FALSE;
  584. if (*phwnd)
  585. return S_OK;
  586. _hwndParent = hwndParent;
  587. _SetStyle(dwStyle);
  588. _SetExStyle(dwExStyle);
  589. *phwnd = _CreateTreeview();
  590. if (*phwnd == NULL)
  591. {
  592. return E_OUTOFMEMORY;
  593. }
  594. ::ShowWindow(_hwndTree, SW_SHOW);
  595. return S_OK;
  596. }
  597. HWND CNscTree::_CreateTreeview()
  598. {
  599. ASSERT(_hwndTree == NULL);
  600. LONG lTop = 0;
  601. RECT rcParent;
  602. ::GetClientRect(_hwndParent, &rcParent);
  603. #if 0
  604. if ((_dwFlags & NSS_HEADER) && _hwndHdr)
  605. {
  606. RECT rc;
  607. GetWindowRect(_hwndHdr, &rc);
  608. lTop = RECTHEIGHT(rc);
  609. }
  610. #endif
  611. TCHAR szTitle[40];
  612. if (_mode & (MODE_HISTORY | MODE_FAVORITES))
  613. {
  614. // create with a window title so that msaa can expose name
  615. int id = (_mode & MODE_HISTORY) ? IDS_BAND_HISTORY : IDS_BAND_FAVORITES;
  616. MLLoadString(id, szTitle, ARRAYSIZE(szTitle));
  617. }
  618. else
  619. {
  620. szTitle[0] = 0;
  621. }
  622. _hwndTree = CreateWindowEx(0, WC_TREEVIEW, szTitle, _style | WS_VISIBLE,
  623. 0, lTop, rcParent.right, rcParent.bottom, _hwndParent, (HMENU)ID_CONTROL, HINST_THISDLL, NULL);
  624. if (_hwndTree)
  625. {
  626. ::SendMessage(_hwndTree, TVM_SETSCROLLTIME, 100, 0);
  627. ::SendMessage(_hwndTree, CCM_SETUNICODEFORMAT, DLL_IS_UNICODE, 0);
  628. if (_dwExStyle)
  629. TreeView_SetExtendedStyle(_hwndTree, _dwExStyle, _dwExStyle);
  630. }
  631. else
  632. {
  633. TraceMsg(TF_ERROR, "_hwndTree failed");
  634. }
  635. return _hwndTree;
  636. }
  637. UINT GetControlCharWidth(HWND hwnd)
  638. {
  639. SIZE siz = {0};
  640. CClientDC dc(HWND_DESKTOP);
  641. if (dc.m_hDC)
  642. {
  643. HFONT hfOld = dc.SelectFont(FORWARD_WM_GETFONT(hwnd, SendMessage));
  644. if (hfOld)
  645. {
  646. GetTextExtentPoint(dc.m_hDC, TEXT("0"), 1, &siz);
  647. dc.SelectFont(hfOld);
  648. }
  649. }
  650. return siz.cx;
  651. }
  652. HWND CNscTree::_CreateHeader()
  653. {
  654. if (!_hwndHdr)
  655. {
  656. _hwndHdr = CreateWindowEx(0, WC_HEADER, NULL, HDS_HORZ | WS_CHILD, 0, 0, 0, 0,
  657. _hwndParent, (HMENU)ID_HEADER, HINST_THISDLL, NULL);
  658. if (_hwndHdr)
  659. {
  660. HD_LAYOUT layout;
  661. WINDOWPOS wpos;
  662. RECT rcClient;
  663. int cxChar = GetControlCharWidth(_hwndTree);
  664. layout.pwpos = &wpos;
  665. ::GetClientRect(_hwndParent, &rcClient);
  666. layout.prc = &rcClient;
  667. if (Header_Layout(_hwndHdr, &layout))
  668. {
  669. ::MoveWindow(_hwndTree, 0, wpos.cy, RECTWIDTH(rcClient), RECTHEIGHT(rcClient)-wpos.cy, TRUE);
  670. for (int i = 0; i < DPA_GetPtrCount(_hdpaColumns);)
  671. {
  672. HEADERINFO *phinfo = (HEADERINFO *)DPA_GetPtr(_hdpaColumns, i);
  673. if (EVAL(phinfo))
  674. {
  675. HD_ITEM item;
  676. item.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
  677. item.pszText = phinfo->szName;
  678. item.fmt = phinfo->fmt;
  679. item.cxy = cxChar * phinfo->cxChar;
  680. if (Header_InsertItem(_hwndHdr, i, &item) == -1)
  681. {
  682. DPA_DeletePtr(_hdpaColumns, i);
  683. LocalFree(phinfo);
  684. phinfo = NULL;
  685. }
  686. else
  687. {
  688. i++;
  689. }
  690. }
  691. }
  692. if (_hwndTree)
  693. {
  694. HFONT hfont = (HFONT)::SendMessage(_hwndTree, WM_GETFONT, 0, 0);
  695. if (hfont)
  696. ::SendMessage(_hwndHdr, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
  697. }
  698. ::SetWindowPos(_hwndHdr, wpos.hwndInsertAfter, wpos.x, wpos.y,
  699. wpos.cx, wpos.cy, wpos.flags | SWP_SHOWWINDOW);
  700. }
  701. }
  702. }
  703. return _hwndHdr;
  704. }
  705. void CNscTree::_TvOnHide()
  706. {
  707. _DtRevoke();
  708. ::SetWindowPos(_hwndTree, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW);
  709. }
  710. void CNscTree::_TvOnShow()
  711. {
  712. ::SetWindowPos(_hwndTree, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
  713. _DtRegister();
  714. }
  715. HRESULT IUnknown_GetAmbientProperty(IUnknown *punk, DISPID dispid, VARTYPE vt, void *pData)
  716. {
  717. HRESULT hr = E_FAIL;
  718. if (punk)
  719. {
  720. IDispatch *pdisp;
  721. hr = punk->QueryInterface(IID_PPV_ARG(IDispatch, &pdisp));
  722. if (SUCCEEDED(hr))
  723. {
  724. DISPPARAMS dp = {0};
  725. VARIANT v;
  726. VariantInit(&v);
  727. hr = pdisp->Invoke(dispid, IID_NULL, 0, DISPATCH_PROPERTYGET, &dp, &v, NULL, NULL);
  728. if (SUCCEEDED(hr))
  729. {
  730. VARIANT vDest;
  731. VariantInit(&vDest);
  732. // we've got the variant, so now go an coerce it to the type
  733. // that the user wants.
  734. //
  735. hr = VariantChangeType(&vDest, &v, 0, vt);
  736. if (SUCCEEDED(hr))
  737. {
  738. *((DWORD *)pData) = *((DWORD *)&vDest.lVal);
  739. VariantClear(&vDest);
  740. }
  741. VariantClear(&v);
  742. }
  743. pdisp->Release();
  744. }
  745. }
  746. return hr;
  747. }
  748. HRESULT CNscTree::_HandleWinIniChange()
  749. {
  750. COLORREF clrBk;
  751. if (FAILED(IUnknown_GetAmbientProperty(_punkSite, DISPID_AMBIENT_BACKCOLOR, VT_I4, &clrBk)))
  752. clrBk = GetSysColor(COLOR_WINDOW);
  753. TreeView_SetBkColor(_hwndTree, clrBk);
  754. if (!(_dwFlags & NSS_NORMALTREEVIEW))
  755. {
  756. // make things a bit more spaced out
  757. int cyItem = TreeView_GetItemHeight(_hwndTree);
  758. cyItem += LOGOGAP + 1;
  759. TreeView_SetItemHeight(_hwndTree, cyItem);
  760. }
  761. // Show compressed files in different color...
  762. SHELLSTATE ss;
  763. SHGetSetSettings(&ss, SSF_SHOWCOMPCOLOR, FALSE);
  764. _fShowCompColor = ss.fShowCompColor;
  765. return S_OK;
  766. }
  767. HRESULT CNscTree::Initialize(LPCITEMIDLIST pidlRoot, DWORD grfEnumFlags, DWORD dwFlags)
  768. {
  769. HRESULT hr;
  770. _grfFlags = grfEnumFlags; // IShellFolder::EnumObjects() flags.
  771. if (!(_mode & MODE_CUSTOM))
  772. {
  773. if (_mode != MODE_NORMAL)
  774. {
  775. dwFlags |= NSS_BORDER;
  776. }
  777. else
  778. {
  779. dwFlags |= NSS_NORMALTREEVIEW;
  780. }
  781. }
  782. _dwFlags = dwFlags; // Behavior Flags
  783. if (_dwFlags & NSS_NORMALTREEVIEW)
  784. _dwFlags &= ~NSS_HEADER;// multi-select requires owner draw
  785. if (!_fInitialized)
  786. {
  787. ::SendMessage(_hwndTree, WM_SETREDRAW, FALSE, 0);
  788. _fInitialized = TRUE;
  789. SHFILEINFO sfi;
  790. HIMAGELIST himl = (HIMAGELIST)SHGetFileInfo(TEXT(DEFAULT_PATHSTR), 0, &sfi,
  791. sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
  792. TreeView_SetImageList(_hwndTree, himl, TVSIL_NORMAL);
  793. _DtRegister();
  794. //failure ignored intentionally
  795. THR(CoCreateInstance(CLSID_ShellTaskScheduler, NULL, CLSCTX_INPROC,
  796. IID_PPV_ARG(IShellTaskScheduler, &_pTaskScheduler)));
  797. if (_pTaskScheduler)
  798. _pTaskScheduler->Status(ITSSFLAG_KILL_ON_DESTROY, ITSS_THREAD_TIMEOUT_NO_CHANGE);
  799. hr = Init(); // init lock and scroll handles for CDelegateDropTarget
  800. ASSERT(SUCCEEDED(hr));
  801. if (_dwFlags & NSS_BORDER)
  802. {
  803. // set borders and space out for all, much cleaner.
  804. TreeView_SetBorder(_hwndTree, TVSBF_XBORDER, 2 * LOGOGAP, 0);
  805. }
  806. // init some settings
  807. _HandleWinIniChange();
  808. // pidlRoot may equal NULL because that is equal to CSIDL_DESKTOP.
  809. if ((LPITEMIDLIST)INVALID_HANDLE_VALUE != pidlRoot)
  810. {
  811. _UnSubClass();
  812. _SetRoot(pidlRoot, 1, NULL, NSSR_CREATEPIDL);
  813. _SubClass(pidlRoot);
  814. }
  815. // need top level frame available for D&D if possible.
  816. _hwndDD = ::GetParent(_hwndTree);
  817. IOleWindow *pOleWindow;
  818. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IOleWindow, &pOleWindow))))
  819. {
  820. pOleWindow->GetWindow(&_hwndDD);
  821. pOleWindow->Release();
  822. }
  823. //this is a non-ML resource
  824. _hicoPinned = (HICON)LoadImage(HINST_THISDLL, MAKEINTRESOURCE(IDI_PINNED), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
  825. ASSERT(_hicoPinned);
  826. ::SendMessage(_hwndTree, WM_SETREDRAW, TRUE, 0);
  827. }
  828. else
  829. hr = _ChangePidlRoot(pidlRoot);
  830. return hr;
  831. }
  832. // set the root of the name space control.
  833. //
  834. // in:
  835. // pidlRoot NULL means the desktop
  836. // HIWORD 0 -> LOWORD == ID of special folder (CSIDL_* values)
  837. //
  838. // flags,
  839. // pidlRoot, PIDL, NULL for desktop, or CSIDL for shell special folder
  840. // iExpandDepth, how many levels to expand the tree
  841. // pidlExpandTo NULL, or PIDL to expand to
  842. //
  843. BOOL CNscTree::_SetRoot(LPCITEMIDLIST pidlRoot, int iExpandDepth, LPCITEMIDLIST pidlExpandTo, NSSR_FLAGS flags)
  844. {
  845. _ReleasePidls();
  846. // review chrisny: clean up this psr stuff.
  847. // HIWORD/LOWORD stuff is to support pidl IDs instead of full pidl here
  848. if (HIWORD(pidlRoot))
  849. {
  850. _pidlRoot = ILClone(pidlRoot);
  851. }
  852. else
  853. {
  854. SHGetSpecialFolderLocation(NULL, LOWORD(pidlRoot) ? LOWORD(pidlRoot) : CSIDL_DESKTOP, &_pidlRoot);
  855. }
  856. if (_pidlRoot)
  857. {
  858. HTREEITEM htiRoot = TVI_ROOT;
  859. if (_mode == MODE_NORMAL)
  860. {
  861. // Since we'll be adding this into the tree, we need
  862. // to clone it: We have a copy for the class, and we
  863. // have one for the tree itself (Makes life easier so
  864. // we don't have to special case TVI_ROOT).
  865. htiRoot = _AddItemToTree(TVI_ROOT, _pidlRoot, 1, 0);
  866. if (htiRoot)
  867. {
  868. TreeView_SelectItem(_hwndTree, htiRoot);
  869. TraceMsg(TF_NSC, "NSCBand: Setting Root to \"Desktop\"");
  870. }
  871. else
  872. {
  873. htiRoot = TVI_ROOT;
  874. }
  875. }
  876. BOOL fOrdered = _fOrdered;
  877. _LoadSF(htiRoot, _pidlRoot, &fOrdered); // load the roots (actual children of _pidlRoot.
  878. // this is probably redundant since _LoadSF->_LoadOrder sets this
  879. _fOrdered = BOOLIFY(fOrdered);
  880. #ifdef DEBUG
  881. TracePIDLAbs(_pidlRoot, TEXT("Setting Root to"));
  882. #endif // DEBUG
  883. return TRUE;
  884. }
  885. TraceMsg(DM_ERROR, "set root failed");
  886. _ReleasePidls();
  887. return FALSE;
  888. }
  889. // cache the shell folder for a given tree item
  890. // in:
  891. // hti tree node to cache shell folder for. this my be
  892. // NULL indicating the root item.
  893. //
  894. BOOL CNscTree::_CacheShellFolder(HTREEITEM hti)
  895. {
  896. // in the cache?
  897. if ((hti != _htiCache) || (_psfCache == NULL))
  898. {
  899. // cache miss, do the work
  900. LPITEMIDLIST pidl;
  901. BOOL fRet = FALSE;
  902. _fpsfCacheIsTopLevel = FALSE;
  903. _ReleaseCachedShellFolder();
  904. if ((hti == NULL) || (hti == TVI_ROOT))
  905. {
  906. pidl = ILClone(_pidlRoot);
  907. }
  908. else
  909. {
  910. pidl = _GetFullIDList(hti);
  911. }
  912. if (pidl)
  913. {
  914. if (SUCCEEDED(IEBindToObject(pidl, &_psfCache)))
  915. {
  916. if (_pnscProxy)
  917. _pnscProxy->CacheItem(pidl);
  918. ASSERT(_psfCache);
  919. _htiCache = hti; // this is for the cache match
  920. _fpsfCacheIsTopLevel = (hti == TVI_ROOT || hti == NULL);
  921. _psfCache->QueryInterface(IID_PPV_ARG(IShellFolder2, &_psf2Cache));
  922. fRet = TRUE;
  923. }
  924. ILFree(pidl);
  925. }
  926. return fRet;
  927. }
  928. return TRUE;
  929. }
  930. #define TVI_ROOTPARENT ((HTREEITEM)(ULONG_PTR)-0xF000)
  931. // pidlItem is typically a relative pidl, except in the case of the root where
  932. // it can be a fully qualified pidl
  933. LPITEMIDLIST CNscTree::_CacheParentShellFolder(HTREEITEM hti, LPITEMIDLIST pidl)
  934. {
  935. // need parent shell folder of TVI_ROOT, special case for drop insert into root level of tree.
  936. if (hti == TVI_ROOT ||
  937. hti == NULL ||
  938. (_mode == MODE_NORMAL &&
  939. TreeView_GetParent(_hwndTree, hti) == NULL)) // If we have a null parent and we're a normal,
  940. // than that's the same as root.
  941. {
  942. if (_htiCache != TVI_ROOTPARENT)
  943. {
  944. _ReleaseCachedShellFolder();
  945. IEBindToParentFolder(_pidlRoot, &_psfCache, NULL);
  946. if (!ILIsEmpty(_pidlRoot))
  947. _htiCache = TVI_ROOTPARENT;
  948. }
  949. return ILFindLastID(_pidlRoot);
  950. }
  951. if (_CacheShellFolder(TreeView_GetParent(_hwndTree, hti)))
  952. {
  953. if (pidl == NULL)
  954. {
  955. PORDERITEM poi = _GetTreeOrderItem(hti);
  956. if (!poi)
  957. return NULL;
  958. pidl = poi->pidl;
  959. }
  960. return ILFindLastID(pidl);
  961. }
  962. return NULL;
  963. }
  964. typedef struct _SORTPARAMS
  965. {
  966. CNscTree *pnsc;
  967. IShellFolder *psf;
  968. } SORTPARAMS;
  969. int CALLBACK CNscTree::_TreeCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  970. {
  971. SORTPARAMS *pSortParams = (SORTPARAMS *)lParamSort;
  972. PORDERITEM poi1 = GetPoi(lParam1), poi2 = GetPoi(lParam2);
  973. HRESULT hr = pSortParams->pnsc->_CompareIDs(pSortParams->psf, poi1->pidl, poi2->pidl);
  974. return (short)SCODE_CODE(hr);
  975. }
  976. int CALLBACK CNscTree::_TreeOrder(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  977. {
  978. HRESULT hr;
  979. PORDERITEM poi1 = GetPoi(lParam1), poi2 = GetPoi(lParam2);
  980. ASSERT((poi1 != NULL) && (poi1 != NULL));
  981. if (poi1->nOrder == poi2->nOrder)
  982. hr = 0;
  983. else
  984. // do unsigned compare so -1 goes to end of list
  985. hr = (poi1->nOrder < poi2->nOrder ? -1 : 1);
  986. return (short)SCODE_CODE(hr);
  987. }
  988. // review chrisny: instead of sort, insert items on the fly.
  989. void CNscTree::_Sort(HTREEITEM hti, IShellFolder *psf)
  990. {
  991. TV_SORTCB scb;
  992. SORTPARAMS SortParams = {this, psf};
  993. BOOL fOrdering = _IsOrdered(hti);
  994. #ifdef DEBUG
  995. TraceHTREE(hti, TEXT("Sorting"));
  996. #endif
  997. scb.hParent = hti;
  998. scb.lpfnCompare = !fOrdering ? _TreeCompare : _TreeOrder;
  999. scb.lParam = (LPARAM)&SortParams;
  1000. TreeView_SortChildrenCB(_hwndTree, &scb, FALSE);
  1001. }
  1002. BOOL CNscTree::_IsOrdered(HTREEITEM htiRoot)
  1003. {
  1004. if ((htiRoot == TVI_ROOT) || (htiRoot == NULL))
  1005. return _fOrdered;
  1006. else
  1007. {
  1008. PORDERITEM poi = _GetTreeOrderItem(htiRoot);
  1009. if (poi)
  1010. {
  1011. // LParam Is a Boolean:
  1012. // TRUE: It has an order.
  1013. // FALSE: It does not have an order.
  1014. // Question: Where is that order stored? _hdpaOrder?
  1015. return poi->lParam;
  1016. }
  1017. }
  1018. return FALSE;
  1019. }
  1020. //helper function to init _hdpaOrd
  1021. //MUST be followed by a call to _FreeOrderList
  1022. HRESULT CNscTree::_PopulateOrderList(HTREEITEM htiRoot)
  1023. {
  1024. int i = 0;
  1025. HTREEITEM hti = NULL;
  1026. #ifdef DEBUG
  1027. TraceHTREE(htiRoot, TEXT("Populating Order List from tree node"));
  1028. #endif
  1029. if (_hdpaOrd)
  1030. DPA_Destroy(_hdpaOrd);
  1031. _hdpaOrd = DPA_Create(4);
  1032. if (_hdpaOrd == NULL)
  1033. return E_FAIL;
  1034. for (hti = TreeView_GetChild(_hwndTree, htiRoot); hti; hti = TreeView_GetNextSibling(_hwndTree, hti))
  1035. {
  1036. PORDERITEM poi = _GetTreeOrderItem(hti);
  1037. if (poi)
  1038. {
  1039. poi->nOrder = i; // reset the positions of the nodes.
  1040. DPA_SetPtr(_hdpaOrd, i++, (void *)poi);
  1041. }
  1042. }
  1043. //set the root's ordered flag
  1044. if (htiRoot == TVI_ROOT)
  1045. {
  1046. _fOrdered = TRUE;
  1047. }
  1048. else
  1049. {
  1050. PORDERITEM poi = _GetTreeOrderItem(htiRoot);
  1051. if (poi)
  1052. {
  1053. poi->lParam = TRUE;
  1054. }
  1055. }
  1056. return S_OK;
  1057. }
  1058. //helper function to free _hdpaOrd
  1059. //MUST be preceded by a call to _PopulateOrderList
  1060. void CNscTree::_FreeOrderList(HTREEITEM htiRoot)
  1061. {
  1062. ASSERT(_hdpaOrd);
  1063. #ifdef DEBUG
  1064. TraceHTREE(htiRoot, TEXT("Freeing OrderList"));
  1065. #endif
  1066. _ReleaseCachedShellFolder();
  1067. // Persist the new order out to the registry
  1068. LPITEMIDLIST pidl = _GetFullIDList(htiRoot);
  1069. if (pidl)
  1070. {
  1071. IStream* pstm = GetOrderStream(pidl, STGM_WRITE | STGM_CREATE);
  1072. if (pstm)
  1073. {
  1074. if (_CacheShellFolder(htiRoot))
  1075. {
  1076. #ifdef DEBUG
  1077. for (int i=0; i<DPA_GetPtrCount(_hdpaOrd); i++)
  1078. {
  1079. PORDERITEM poi = (PORDERITEM)DPA_GetPtr(_hdpaOrd, i);
  1080. if (poi)
  1081. {
  1082. ASSERTMSG(poi->nOrder >= 0, "nsc saving bogus order list nOrder (%d), get reljai", poi->nOrder);
  1083. }
  1084. }
  1085. #endif
  1086. OrderList_SaveToStream(pstm, _hdpaOrd, _psfCache);
  1087. pstm->Release();
  1088. // Notify everyone that the order changed
  1089. SHSendChangeMenuNotify(this, SHCNEE_ORDERCHANGED, SHCNF_FLUSH, _pidlRoot);
  1090. _dwOrderSig++;
  1091. TraceMsg(TF_NSC, "NSCBand: Sent SHCNE_EXTENDED_EVENT : SHCNEE_ORDERCHANGED");
  1092. // Remove this notify message immediately (so _fDropping is set
  1093. // and we'll ignore this event in above OnChange method)
  1094. //
  1095. // _FlushNotifyMessages(_hwndTree);
  1096. }
  1097. else
  1098. pstm->Release();
  1099. }
  1100. ILFree(pidl);
  1101. }
  1102. DPA_Destroy(_hdpaOrd);
  1103. _hdpaOrd = NULL;
  1104. }
  1105. //removes any order the user has set and goes back to alphabetical sort
  1106. HRESULT CNscTree::ResetSort(void)
  1107. {
  1108. HRESULT hr = S_OK;
  1109. #ifdef UNUSED
  1110. ASSERT(_psfCache);
  1111. ASSERT(_pidlRoot);
  1112. int cAdded = 0;
  1113. IStream* pstm = NULL;
  1114. _fWeChangedOrder = TRUE;
  1115. if (FAILED(hr = _PopulateOrderList(TVI_ROOT)))
  1116. return hr;
  1117. pstm = OpenPidlOrderStream((LPCITEMIDLIST)CSIDL_FAVORITES, _pidlRoot, REG_SUBKEY_FAVORITESA, STGM_CREATE | STGM_WRITE);
  1118. _CacheShellFolder(TVI_ROOT);
  1119. if (pstm == NULL || _psfCache == NULL)
  1120. {
  1121. ATOMICRELEASE(pstm);
  1122. _FreeOrderList(TVI_ROOT);
  1123. return S_OK;
  1124. }
  1125. _fOrdered = FALSE;
  1126. ORDERINFO oinfo;
  1127. oinfo.psf = _psfCache;
  1128. (oinfo.psf)->AddRef();
  1129. oinfo.dwSortBy = OI_SORTBYNAME;
  1130. DPA_Sort(_hdpaOrd, OrderItem_Compare,(LPARAM)&oinfo);
  1131. ATOMICRELEASE(oinfo.psf);
  1132. OrderList_Reorder(_hdpaOrd);
  1133. OrderList_SaveToStream(pstm, _hdpaOrd, _psfCache);
  1134. ATOMICRELEASE(pstm);
  1135. _FreeOrderList(TVI_ROOT);
  1136. Refresh();
  1137. _fWeChangedOrder = FALSE;
  1138. #endif
  1139. return hr;
  1140. }
  1141. void CNscTree::MoveItemUpOrDown(BOOL fUp)
  1142. {
  1143. HTREEITEM htiSelected = TreeView_GetSelection(_hwndTree);
  1144. HTREEITEM htiToSwap = (fUp) ? TreeView_GetPrevSibling(_hwndTree, htiSelected) :
  1145. TreeView_GetNextSibling(_hwndTree, htiSelected);
  1146. HTREEITEM htiParent = TreeView_GetParent(_hwndTree, htiSelected);
  1147. if (htiParent == NULL)
  1148. htiParent = TVI_ROOT;
  1149. ASSERT(htiSelected);
  1150. _fWeChangedOrder = TRUE;
  1151. if (FAILED(_PopulateOrderList(htiParent)))
  1152. return;
  1153. if (htiSelected && htiToSwap)
  1154. {
  1155. PORDERITEM poiSelected = _GetTreeOrderItem(htiSelected);
  1156. PORDERITEM poiToSwap = _GetTreeOrderItem(htiToSwap);
  1157. if (poiSelected && poiToSwap)
  1158. {
  1159. int iOrder = poiSelected->nOrder;
  1160. poiSelected->nOrder = poiToSwap->nOrder;
  1161. poiToSwap->nOrder = iOrder;
  1162. }
  1163. _CacheShellFolder(htiParent);
  1164. if (_psfCache)
  1165. _Sort(htiParent, _psfCache);
  1166. }
  1167. TreeView_SelectItem(_hwndTree, htiSelected);
  1168. _FreeOrderList(htiParent);
  1169. _fWeChangedOrder = FALSE;
  1170. }
  1171. // filter function... let clients filter what gets added here
  1172. BOOL CNscTree::_ShouldAdd(LPCITEMIDLIST pidl)
  1173. {
  1174. // send notify up to parent to let them filter
  1175. return TRUE;
  1176. }
  1177. BOOL CNscTree::_OnItemExpandingMsg(NM_TREEVIEW *pnm)
  1178. {
  1179. HCURSOR hCursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
  1180. BOOL bRet = _OnItemExpanding(pnm->itemNew.hItem, pnm->action,
  1181. (pnm->itemNew.state & TVIS_EXPANDEDONCE), (pnm->itemNew.state & TVIS_EXPANDPARTIAL));
  1182. SetCursor(hCursorOld);
  1183. return bRet;
  1184. }
  1185. //
  1186. // The NSC item is expandable if it is a regular folder and it's not one
  1187. // of those funky non-expandable channel folders.
  1188. //
  1189. BOOL CNscTree::_IsExpandable(HTREEITEM hti)
  1190. {
  1191. BOOL fExpandable = FALSE;
  1192. LPCITEMIDLIST pidlItem = _CacheParentShellFolder(hti, NULL);
  1193. if (pidlItem)
  1194. {
  1195. // make sure item is actually a folder and not a non-expandable channel folder
  1196. // except: in org favs, never expand channel folders
  1197. ULONG ulAttr = SFGAO_FOLDER;
  1198. LPITEMIDLIST pidlTarget = NULL;
  1199. if (SUCCEEDED(_psfCache->GetAttributesOf(1, &pidlItem, &ulAttr)) &&
  1200. (ulAttr & SFGAO_FOLDER) &&
  1201. !(SUCCEEDED(SHGetNavigateTarget(_psfCache, pidlItem, &pidlTarget, &ulAttr)) &&
  1202. ((_mode & MODE_CONTROL) ?
  1203. TRUE :
  1204. !IsExpandableChannelFolder(_psfCache, pidlItem))))
  1205. {
  1206. fExpandable = TRUE;
  1207. }
  1208. ILFree(pidlTarget);
  1209. }
  1210. return fExpandable;
  1211. }
  1212. BOOL CNscTree::_OnItemExpanding(HTREEITEM htiToActivate, UINT action, BOOL fExpandedOnce, BOOL fIsExpandPartial)
  1213. {
  1214. BOOL fReturn = FALSE; // false means let treeview proceed
  1215. if (action != TVE_EXPAND)
  1216. {
  1217. htiToActivate = TreeView_GetParent(_hwndTree, htiToActivate);
  1218. }
  1219. else if (fExpandedOnce && !fIsExpandPartial)
  1220. {
  1221. // Do nothing
  1222. }
  1223. else
  1224. {
  1225. if (_IsExpandable(htiToActivate))
  1226. {
  1227. LPITEMIDLIST pidlParent = _GetFullIDList(htiToActivate);
  1228. if (pidlParent)
  1229. {
  1230. BOOL fOrdered;
  1231. // If we were previously partially expanded, then we need to do a full expand
  1232. _LoadSF(htiToActivate, pidlParent, &fOrdered);
  1233. ILFree(pidlParent);
  1234. }
  1235. }
  1236. // do not remove + on downlevel because inserting items would not expand htiToActivate
  1237. // instead we will remove the plus if nothing gets added
  1238. if (!fIsExpandPartial && MODE_NORMAL == _mode && IsOS(OS_WHISTLERORGREATER))
  1239. {
  1240. // If we did not add anything we should update this item to let
  1241. // the user know something happened.
  1242. TreeView_SetChildren(_hwndTree, htiToActivate, NSC_CHILDREN_REMOVE);
  1243. }
  1244. // keep the old behavior for favorites/history/...
  1245. if (MODE_NORMAL == _mode)
  1246. {
  1247. // cannot let treeview proceed with expansion, nothing will be added
  1248. // until background thread is done enumerating
  1249. fReturn = TRUE;
  1250. }
  1251. }
  1252. _UpdateActiveBorder(htiToActivate);
  1253. return fReturn;
  1254. }
  1255. HTREEITEM CNscTree::_FindFromRoot(HTREEITEM htiRoot, LPCITEMIDLIST pidl)
  1256. {
  1257. HTREEITEM htiRet = NULL;
  1258. LPITEMIDLIST pidlParent, pidlChild;
  1259. BOOL fFreePidlParent = FALSE;
  1260. #ifdef DEBUG
  1261. TracePIDLAbs(pidl, TEXT("Finding this pidl"));
  1262. TraceHTREE(htiRoot, TEXT("from this root"));
  1263. #endif
  1264. if (!htiRoot)
  1265. {
  1266. // When in "Normal" mode, we need to use the first child, not the root
  1267. // in order to calculate, because there is no "Invisible" root. On the
  1268. // other hand, History and Favorites have an invisible root: Their
  1269. // parent folder, so they need this fudge.
  1270. htiRoot = (MODE_NORMAL == _mode) ? TreeView_GetChild(_hwndTree, 0) : TVI_ROOT;
  1271. pidlParent = _pidlRoot; // the invisible root.
  1272. }
  1273. else
  1274. {
  1275. pidlParent = _GetFullIDList(htiRoot);
  1276. fFreePidlParent = TRUE;
  1277. }
  1278. if (pidlParent == NULL)
  1279. return NULL;
  1280. if (ILIsEqual(pidlParent, pidl))
  1281. {
  1282. if (fFreePidlParent)
  1283. ILFree(pidlParent);
  1284. return htiRoot;
  1285. }
  1286. pidlChild = ILFindChild(pidlParent, pidl);
  1287. if (pidlChild == NULL)
  1288. {
  1289. if (fFreePidlParent)
  1290. ILFree(pidlParent);
  1291. return NULL; // not root match, no hti
  1292. }
  1293. // root match, carry on . . .
  1294. // Are we rooted under the Desktop (i.e. Empty pidl or ILIsEmpty(_pidlRoot))
  1295. IShellFolder *psf = NULL;
  1296. HRESULT hr = IEBindToObject(pidlParent, &psf);
  1297. if (FAILED(hr))
  1298. {
  1299. if (fFreePidlParent)
  1300. ILFree(pidlParent);
  1301. return htiRet;
  1302. }
  1303. while (htiRoot && psf)
  1304. {
  1305. LPITEMIDLIST pidlItem = ILCloneFirst(pidlChild);
  1306. if (!pidlItem)
  1307. break;
  1308. htiRoot = _FindChild(psf, htiRoot, pidlItem);
  1309. IShellFolder *psfNext = NULL;
  1310. hr = psf->BindToObject(pidlItem, NULL, IID_PPV_ARG(IShellFolder, &psfNext));
  1311. ILFree(pidlItem);
  1312. if (!htiRoot)
  1313. {
  1314. ATOMICRELEASE(psfNext);
  1315. break;
  1316. }
  1317. psf->Release();
  1318. psf = psfNext;
  1319. pidlChild = _ILNext(pidlChild);
  1320. // if we're down to an empty pidl, we've found it!
  1321. if (ILIsEmpty(pidlChild))
  1322. {
  1323. htiRet = htiRoot;
  1324. break;
  1325. }
  1326. if (FAILED(hr))
  1327. {
  1328. ASSERT(psfNext == NULL);
  1329. break;
  1330. }
  1331. }
  1332. if (psf)
  1333. psf->Release();
  1334. if (fFreePidlParent)
  1335. ILFree(pidlParent);
  1336. #ifdef DEBUG
  1337. TraceHTREE(htiRet, TEXT("Found at"));
  1338. #endif
  1339. return htiRet;
  1340. }
  1341. BOOL CNscTree::_FIsItem(IShellFolder *psf, LPCITEMIDLIST pidl, HTREEITEM hti)
  1342. {
  1343. PORDERITEM poi = _GetTreeOrderItem(hti);
  1344. return poi && poi->pidl && psf->CompareIDs(0, poi->pidl, pidl) == 0;
  1345. }
  1346. HRESULT CNscTree::_OnSHNotifyDelete(LPCITEMIDLIST pidl, int *piPosDeleted, HTREEITEM *phtiParent)
  1347. {
  1348. HRESULT hr = S_FALSE;
  1349. HTREEITEM hti = _FindFromRoot(NULL, pidl);
  1350. if (hti == TVI_ROOT)
  1351. return E_INVALIDARG; // invalid arg, DELETION OF TVI_ROOT
  1352. // need to clear _pidlDrag if the one being deleted is _pidlDrag.
  1353. // handles case where dragging into another folder from within or dragging out.
  1354. if (_pidlDrag)
  1355. {
  1356. LPCITEMIDLIST pidltst = _CacheParentShellFolder(hti, NULL);
  1357. if (pidltst)
  1358. {
  1359. if (!_psfCache->CompareIDs(0, pidltst, _pidlDrag))
  1360. _pidlDrag = NULL;
  1361. }
  1362. }
  1363. if (pidl && (hti != NULL))
  1364. {
  1365. _fIgnoreNextItemExpanding = TRUE;
  1366. HTREEITEM htiParent = TreeView_GetParent(_hwndTree, hti);
  1367. if (phtiParent)
  1368. *phtiParent = htiParent;
  1369. //if caller wants the position of the deleted item, don't reorder the other items
  1370. if (piPosDeleted)
  1371. {
  1372. PORDERITEM poi = _GetTreeOrderItem(hti);
  1373. if (poi)
  1374. {
  1375. *piPosDeleted = poi->nOrder;
  1376. hr = S_OK;
  1377. }
  1378. TreeView_DeleteItem(_hwndTree, hti);
  1379. }
  1380. else
  1381. {
  1382. if (htiParent == NULL)
  1383. htiParent = TVI_ROOT;
  1384. if (TreeView_DeleteItem(_hwndTree, hti))
  1385. {
  1386. _ReorderChildren(htiParent);
  1387. hr = S_OK;
  1388. }
  1389. }
  1390. // Update the + next to the parent folder. Note that History and Favorites
  1391. // set ALL of their items to be Folder items, so this is not needed for
  1392. // favorites.
  1393. // do this only on downlevel as comctl v6 takes care of pluses
  1394. if (_mode == MODE_NORMAL && !IsOS(OS_WHISTLERORGREATER))
  1395. {
  1396. LPCITEMIDLIST pidl = _CacheParentShellFolder(htiParent, NULL);
  1397. if (pidl && !ILIsEmpty(pidl))
  1398. {
  1399. DWORD dwAttrib = SFGAO_HASSUBFOLDER;
  1400. if (SUCCEEDED(_psfCache->GetAttributesOf(1, &pidl, &dwAttrib)) &&
  1401. !(dwAttrib & SFGAO_HASSUBFOLDER))
  1402. {
  1403. TV_ITEM tvi;
  1404. tvi.mask = TVIF_CHILDREN | TVIF_HANDLE;
  1405. tvi.hItem = htiParent;
  1406. tvi.cChildren = 0;
  1407. TreeView_SetItem(_hwndTree, &tvi);
  1408. }
  1409. }
  1410. }
  1411. _fIgnoreNextItemExpanding = FALSE;
  1412. if (hti == _htiCut)
  1413. {
  1414. _htiCut = NULL;
  1415. _TreeNukeCutState();
  1416. }
  1417. }
  1418. return hr;
  1419. }
  1420. BOOL CNscTree::_IsItemNameInTree(LPCITEMIDLIST pidl)
  1421. {
  1422. BOOL fReturn = FALSE;
  1423. HTREEITEM hti = _FindFromRoot(NULL, pidl);
  1424. if (hti)
  1425. {
  1426. WCHAR szTree[MAX_PATH];
  1427. TV_ITEM tvi;
  1428. tvi.mask = TVIF_TEXT;
  1429. tvi.hItem = hti;
  1430. tvi.pszText = szTree;
  1431. tvi.cchTextMax = ARRAYSIZE(szTree);
  1432. if (TreeView_GetItem(_hwndTree, &tvi))
  1433. {
  1434. IShellFolder* psf;
  1435. LPCITEMIDLIST pidlChild;
  1436. if (SUCCEEDED(_ParentFromItem(pidl, &psf, &pidlChild)))
  1437. {
  1438. WCHAR szName[MAX_PATH];
  1439. if (SUCCEEDED(DisplayNameOf(psf, pidlChild, SHGDN_INFOLDER, szName, ARRAYSIZE(szName))))
  1440. {
  1441. fReturn = (StrCmp(szName, szTree) == 0);
  1442. }
  1443. psf->Release();
  1444. }
  1445. }
  1446. }
  1447. return fReturn;
  1448. }
  1449. //
  1450. // Attempt to perform a rename-in-place. Returns
  1451. //
  1452. // S_OK - rename succeeded
  1453. // S_FALSE - original object not found
  1454. // error - rename failed
  1455. //
  1456. HRESULT CNscTree::_OnSHNotifyRename(LPCITEMIDLIST pidl, LPCITEMIDLIST pidlNew)
  1457. {
  1458. HTREEITEM hti, htiParent = NULL;
  1459. HRESULT hr = S_FALSE;
  1460. //
  1461. // If the source and destination belong to the same folder, then
  1462. // it's an in-folder rename.
  1463. //
  1464. LPITEMIDLIST pidlParent = ILCloneParent(pidl);
  1465. LPITEMIDLIST pidlNewParent = ILCloneParent(pidlNew);
  1466. if (pidlParent && pidlNewParent && IEILIsEqual(pidlParent, pidlNewParent, TRUE) && (hti = _FindFromRoot(NULL, pidl)))
  1467. {
  1468. // to avoid reentering problems
  1469. if (!_IsItemNameInTree(pidlNew))
  1470. {
  1471. HTREEITEM htiSelected = TreeView_GetSelection(_hwndTree);
  1472. ::SendMessage(_hwndTree, WM_SETREDRAW, FALSE, 0);
  1473. if ((_OnSHNotifyDelete(pidl, NULL, &htiParent) != E_INVALIDARG) // invalid arg indication of bogus rename, do not continue.
  1474. && (_OnSHNotifyCreate(pidlNew, DEFAULTORDERPOSITION, htiParent) == S_OK))
  1475. {
  1476. if (hti == htiSelected)
  1477. {
  1478. hti = _FindFromRoot(NULL, pidlNew);
  1479. _SelectNoExpand(_hwndTree, hti); // do not expand this guy
  1480. }
  1481. // NTRAID 89444: If we renamed the item the user is sitting on,
  1482. // SHBrowseForFolder doesn't realize it and doesn't update the
  1483. // edit control.
  1484. hr = S_OK;
  1485. }
  1486. ::SendMessage(_hwndTree, WM_SETREDRAW, TRUE, 0);
  1487. }
  1488. }
  1489. // rename can be a move, so do not depend on the delete happening successfully.
  1490. else if ((_OnSHNotifyDelete(pidl, NULL, &htiParent) != E_INVALIDARG) // invalid arg indication of bogus rename, do not continue.
  1491. && (_OnSHNotifyCreate(pidlNew, DEFAULTORDERPOSITION, htiParent) == S_OK))
  1492. {
  1493. hr = S_OK;
  1494. }
  1495. ILFree(pidlParent);
  1496. ILFree(pidlNewParent);
  1497. // if user created a new folder and changed the default name but is still in edit mode in defview
  1498. // and then clicked on the + of the parent folder we start enumerating the folder (or stealing items
  1499. // from defview) before defview had time to change the name of the new folder. The result is
  1500. // we enumerate the old name and before we transfer it to the foreground thread shell change notify rename
  1501. // kicks in and we change the item already in the tree. We then merge the items from the enumeration
  1502. // which results in extra folder with the old name.
  1503. // to avoid this we force the reenumeration...
  1504. _dwOrderSig++;
  1505. return hr;
  1506. }
  1507. //
  1508. // To update an item, just find it and invalidate it.
  1509. //
  1510. void CNscTree::_OnSHNotifyUpdateItem(LPCITEMIDLIST pidl, LPITEMIDLIST pidlReal)
  1511. {
  1512. HTREEITEM hti = _FindFromRoot(NULL, pidl);
  1513. if (hti)
  1514. {
  1515. _TreeInvalidateItemInfo(hti, TVIF_TEXT);
  1516. if (pidlReal && hti != TVI_ROOT)
  1517. {
  1518. PORDERITEM poi = _GetTreeOrderItem(hti);
  1519. _AssignPidl(poi, pidlReal);
  1520. }
  1521. }
  1522. }
  1523. LPITEMIDLIST CNscTree::_FindHighestDeadItem(LPCITEMIDLIST pidl)
  1524. {
  1525. LPITEMIDLIST pidlRet = NULL;
  1526. LPITEMIDLIST pidlParent = ILCloneParent(pidl);
  1527. if (pidlParent)
  1528. {
  1529. IShellFolder* psf;
  1530. LPCITEMIDLIST pidlChild;
  1531. if (SUCCEEDED(_ParentFromItem(pidlParent, &psf, &pidlChild)))
  1532. {
  1533. DWORD dwAttrib = SFGAO_VALIDATE;
  1534. if (FAILED(psf->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlChild, &dwAttrib)))
  1535. {
  1536. pidlRet = _FindHighestDeadItem(pidlParent);
  1537. }
  1538. psf->Release();
  1539. }
  1540. ILFree(pidlParent);
  1541. }
  1542. return pidlRet ? pidlRet : ILClone(pidl);
  1543. }
  1544. void CNscTree::_RemoveDeadBranch(LPCITEMIDLIST pidl)
  1545. {
  1546. LPITEMIDLIST pidlTop = _FindHighestDeadItem(pidl);
  1547. if (pidlTop)
  1548. {
  1549. HTREEITEM hti = _FindFromRoot(NULL, pidlTop);
  1550. if (hti)
  1551. {
  1552. if (!TreeView_DeleteItem(_hwndTree, hti))
  1553. {
  1554. ASSERTMSG(FALSE, "CNscTree::_OnSHNotifyUpdateDir: DeleteItem failed in tree control"); // somethings hosed in the tree.
  1555. }
  1556. }
  1557. ILFree(pidlTop);
  1558. }
  1559. }
  1560. HRESULT CNscTree::_OnSHNotifyUpdateDir(LPCITEMIDLIST pidl)
  1561. {
  1562. HRESULT hr = S_FALSE;
  1563. HTREEITEM hti = _FindFromRoot(NULL, pidl);
  1564. if (hti)
  1565. { // folder exists in tree refresh folder now if had been loaded by expansion.
  1566. IShellFolder* psf = NULL;
  1567. LPCITEMIDLIST pidlChild;
  1568. if (SUCCEEDED(_ParentFromItem(pidl, &psf, &pidlChild)))
  1569. {
  1570. LPITEMIDLIST pidlReal;
  1571. DWORD dwAttrib = SFGAO_VALIDATE;
  1572. // pidlChild is read-only, so we start
  1573. // off our double validation with getting the "real"
  1574. // pidl which will fall back to a clone
  1575. if (SUCCEEDED(_IdlRealFromIdlSimple(psf, pidlChild, &pidlReal))
  1576. && SUCCEEDED(psf->GetAttributesOf(1, (LPCITEMIDLIST *)&pidlReal, &dwAttrib)))
  1577. {
  1578. TV_ITEM tvi;
  1579. tvi.mask = TVIF_STATE;
  1580. tvi.stateMask = (TVIS_EXPANDEDONCE | TVIS_EXPANDED | TVIS_EXPANDPARTIAL);
  1581. tvi.hItem = (HTREEITEM)hti;
  1582. if (hti != TVI_ROOT)
  1583. {
  1584. if (!TreeView_GetItem(_hwndTree, &tvi))
  1585. tvi.state = 0;
  1586. }
  1587. if (hti == TVI_ROOT || tvi.state & TVIS_EXPANDEDONCE)
  1588. {
  1589. hr = _UpdateDir(hti, TRUE);
  1590. }
  1591. else if (!(tvi.state & TVIS_EXPANDEDONCE))
  1592. {
  1593. TreeView_SetChildren(_hwndTree, hti, NSC_CHILDREN_CALLBACK);
  1594. }
  1595. if (hti != TVI_ROOT)
  1596. {
  1597. PORDERITEM poi = _GetTreeOrderItem(hti);
  1598. _AssignPidl(poi, pidlReal);
  1599. }
  1600. ILFree(pidlReal);
  1601. }
  1602. else
  1603. {
  1604. _RemoveDeadBranch(pidl);
  1605. }
  1606. psf->Release();
  1607. }
  1608. }
  1609. return hr;
  1610. }
  1611. HRESULT CNscTree::_GetEnumFlags(IShellFolder *psf, LPCITEMIDLIST pidlFolder, DWORD *pgrfFlags, HWND *phwnd)
  1612. {
  1613. HWND hwnd = NULL;
  1614. DWORD grfFlags = _grfFlags;
  1615. if (_pFilter)
  1616. {
  1617. LPITEMIDLIST pidlFree = NULL;
  1618. if (pidlFolder == NULL)
  1619. {
  1620. SHGetIDListFromUnk(psf, &pidlFree);
  1621. pidlFolder = pidlFree;
  1622. }
  1623. _pFilter->GetEnumFlags(psf, pidlFolder, &hwnd, &grfFlags);
  1624. ILFree(pidlFree);
  1625. }
  1626. *pgrfFlags = grfFlags;
  1627. if (phwnd)
  1628. *phwnd = hwnd;
  1629. return S_OK;
  1630. }
  1631. HRESULT CNscTree::_GetEnum(IShellFolder *psf, LPCITEMIDLIST pidlFolder, IEnumIDList **ppenum)
  1632. {
  1633. HWND hwnd = NULL;
  1634. DWORD grfFlags;
  1635. _GetEnumFlags(psf, pidlFolder, &grfFlags, &hwnd);
  1636. // get the enumerator and add the child items for any given pidl
  1637. // REARCHITECT: right now, we don't detect if we actually are dealing with a folder (shell32.dll
  1638. // allows you to create an IShellfolder to a non folder object, so we get bad
  1639. // dialogs, by not passing the hwnd, we don't get the dialogs. we should fix this better. by caching
  1640. // in the tree whether it is a folder or not.
  1641. return psf->EnumObjects(/* _fAutoExpanding ?*/ hwnd, grfFlags, ppenum);
  1642. }
  1643. BOOL CNscTree::_ShouldShow(IShellFolder* psf, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem)
  1644. {
  1645. BOOL bRet = TRUE;
  1646. if (_pFilter)
  1647. {
  1648. LPITEMIDLIST pidlFree = NULL;
  1649. if (pidlFolder == NULL)
  1650. {
  1651. SHGetIDListFromUnk(psf, &pidlFree);
  1652. pidlFolder = pidlFree;
  1653. }
  1654. bRet = (S_OK == _pFilter->ShouldShow(psf, pidlFolder, pidlItem));
  1655. if (pidlFree)
  1656. ILFree(pidlFree);
  1657. }
  1658. return bRet;
  1659. }
  1660. // updates existing dir only. Not new load.
  1661. HRESULT CNscTree::_UpdateDir(HTREEITEM hti, BOOL fUpdatePidls)
  1662. {
  1663. HRESULT hr = S_FALSE;
  1664. LPITEMIDLIST pidlParent = _GetFullIDList(hti);
  1665. if (pidlParent)
  1666. {
  1667. BOOL fOrdered;
  1668. _fUpdate = TRUE;
  1669. hr = _StartBackgroundEnum(hti, pidlParent, &fOrdered, fUpdatePidls);
  1670. _fUpdate = FALSE;
  1671. ILFree(pidlParent);
  1672. }
  1673. return hr;
  1674. }
  1675. int CNscTree::_TreeItemIndexInHDPA(HDPA hdpa, IShellFolder *psfParent, HTREEITEM hti, int iReverseStart)
  1676. {
  1677. int iIndex = -1;
  1678. ASSERT(hti);
  1679. PORDERITEM poi = _GetTreeOrderItem(hti);
  1680. if (poi)
  1681. {
  1682. int celt = DPA_GetPtrCount(hdpa);
  1683. ASSERT(iReverseStart <= celt && iReverseStart >= 0);
  1684. for (int i = iReverseStart-1; i >= 0; i--)
  1685. {
  1686. PORDERITEM poi2 = (PORDERITEM)DPA_GetPtr(hdpa, i);
  1687. if (poi2)
  1688. {
  1689. if (_psfCache->CompareIDs(0, poi->pidl, poi2->pidl) == 0)
  1690. {
  1691. iIndex = i;
  1692. break;
  1693. }
  1694. }
  1695. }
  1696. }
  1697. return iIndex;
  1698. }
  1699. HRESULT CNscTree::_Expand(LPCITEMIDLIST pidl, int iDepth)
  1700. {
  1701. HRESULT hr = E_FAIL;
  1702. HTREEITEM hti = _ExpandToItem(pidl);
  1703. if (hti)
  1704. {
  1705. hr = _ExpandNode(hti, TVE_EXPAND, iDepth);
  1706. // tvi_root is not a pointer and treeview doesn't check for special
  1707. // values so don't select root to prevent fault
  1708. if (hti != TVI_ROOT)
  1709. _SelectNoExpand(_hwndTree, hti);
  1710. }
  1711. return hr;
  1712. }
  1713. HRESULT CNscTree::_ExpandNode(HTREEITEM htiParent, int iCode, int iDepth)
  1714. {
  1715. // nothing to expand
  1716. if (!iDepth)
  1717. return S_OK;
  1718. _fInExpand = TRUE;
  1719. _uDepth = (UINT)iDepth-1;
  1720. HRESULT hr = TreeView_Expand(_hwndTree, htiParent, iCode) ? S_OK : E_FAIL;
  1721. _uDepth = 0;
  1722. _fInExpand = FALSE;
  1723. return hr;
  1724. }
  1725. HTREEITEM CNscTree::_FindChild(IShellFolder *psf, HTREEITEM htiParent, LPCITEMIDLIST pidlChild)
  1726. {
  1727. HTREEITEM hti;
  1728. for (hti = TreeView_GetChild(_hwndTree, htiParent); hti; hti = TreeView_GetNextSibling(_hwndTree, hti))
  1729. {
  1730. if (_FIsItem(psf, pidlChild, hti))
  1731. break;
  1732. }
  1733. return hti;
  1734. }
  1735. void CNscTree::_ReorderChildren(HTREEITEM htiParent)
  1736. {
  1737. int i = 0;
  1738. HTREEITEM hti;
  1739. for (hti = TreeView_GetChild(_hwndTree, htiParent); hti; hti = TreeView_GetNextSibling(_hwndTree, hti))
  1740. {
  1741. PORDERITEM poi = _GetTreeOrderItem(hti);
  1742. if (poi)
  1743. {
  1744. poi->nOrder = i++; // reset the positions of the nodes.
  1745. }
  1746. }
  1747. }
  1748. HRESULT CNscTree::_InsertChild(HTREEITEM htiParent, IShellFolder *psfParent, LPCITEMIDLIST pidlChild,
  1749. BOOL fExpand, BOOL fSimpleToRealIDL, int iPosition, HTREEITEM *phti)
  1750. {
  1751. LPITEMIDLIST pidlReal;
  1752. HRESULT hr;
  1753. HTREEITEM htiNew = NULL;
  1754. if (fSimpleToRealIDL)
  1755. {
  1756. hr = _IdlRealFromIdlSimple(psfParent, pidlChild, &pidlReal);
  1757. }
  1758. else
  1759. {
  1760. hr = SHILClone(pidlChild, &pidlReal);
  1761. }
  1762. // review chrisny: no sort here, use compareitems to insert item instead.
  1763. if (SUCCEEDED(hr))
  1764. {
  1765. HTREEITEM htiAfter = TVI_LAST;
  1766. BOOL fOrdered = _IsOrdered(htiParent);
  1767. if (iPosition != DEFAULTORDERPOSITION || !fOrdered)
  1768. {
  1769. if (iPosition == 0)
  1770. htiAfter = TVI_FIRST;
  1771. else
  1772. {
  1773. if (!fOrdered)
  1774. htiAfter = TVI_FIRST;
  1775. for (HTREEITEM hti = TreeView_GetChild(_hwndTree, htiParent); hti; hti = TreeView_GetNextSibling(_hwndTree, hti))
  1776. {
  1777. PORDERITEM poi = _GetTreeOrderItem(hti);
  1778. if (poi)
  1779. {
  1780. if (fOrdered)
  1781. {
  1782. if (poi->nOrder == iPosition-1)
  1783. {
  1784. htiAfter = hti;
  1785. #ifdef DEBUG
  1786. TraceHTREE(htiAfter, TEXT("Inserting After"));
  1787. #endif
  1788. break;
  1789. }
  1790. }
  1791. else
  1792. {
  1793. if ((short)SCODE_CODE(_CompareIDs(psfParent, pidlReal, poi->pidl)) > 0)
  1794. htiAfter = hti;
  1795. else
  1796. break;
  1797. }
  1798. }
  1799. }
  1800. }
  1801. }
  1802. if ((_FindChild(psfParent, htiParent, pidlReal) == NULL))
  1803. {
  1804. int cChildren = 1;
  1805. if (MODE_NORMAL == _mode)
  1806. {
  1807. DWORD dwAttrib = SFGAO_FOLDER | SFGAO_STREAM;
  1808. hr = psfParent->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlReal, &dwAttrib);
  1809. if (SUCCEEDED(hr))
  1810. cChildren = _GetChildren(psfParent, pidlReal, dwAttrib);
  1811. }
  1812. if (SUCCEEDED(hr))
  1813. {
  1814. htiNew = _AddItemToTree(htiParent, pidlReal, cChildren, iPosition, htiAfter, TRUE, _IsMarked(htiParent));
  1815. if (htiNew)
  1816. {
  1817. _ReorderChildren(htiParent);
  1818. if (fExpand)
  1819. _ExpandNode(htiParent, TVE_EXPAND, 1); // force expansion to show new item.
  1820. //ensure the item is visible after a rename (or external drop, but that should always be a noop)
  1821. if (iPosition != DEFAULTORDERPOSITION)
  1822. TreeView_EnsureVisible(_hwndTree, htiNew);
  1823. hr = S_OK;
  1824. }
  1825. else
  1826. {
  1827. hr = S_FALSE;
  1828. }
  1829. }
  1830. }
  1831. ILFree(pidlReal);
  1832. }
  1833. if (phti)
  1834. *phti = htiNew;
  1835. return hr;
  1836. }
  1837. HRESULT CheckForExpandOnce(HWND hwndTree, HTREEITEM hti)
  1838. {
  1839. // Root node always expanded.
  1840. if (hti == TVI_ROOT)
  1841. return S_OK;
  1842. TV_ITEM tvi;
  1843. tvi.mask = TVIF_STATE | TVIF_CHILDREN;
  1844. tvi.stateMask = (TVIS_EXPANDEDONCE | TVIS_EXPANDED | TVIS_EXPANDPARTIAL);
  1845. tvi.hItem = (HTREEITEM)hti;
  1846. if (TreeView_GetItem(hwndTree, &tvi))
  1847. {
  1848. if (!(tvi.state & TVIS_EXPANDEDONCE) && (tvi.cChildren == 0))
  1849. {
  1850. TreeView_SetChildren(hwndTree, hti, NSC_CHILDREN_FORCE);
  1851. }
  1852. }
  1853. return S_OK;
  1854. }
  1855. HRESULT _InvokeCommandThunk(IContextMenu * pcm, HWND hwndParent)
  1856. {
  1857. HRESULT hr;
  1858. if (g_fRunningOnNT)
  1859. {
  1860. CMINVOKECOMMANDINFOEX ici = {0};
  1861. ici.cbSize = sizeof(ici);
  1862. ici.hwnd = hwndParent;
  1863. ici.nShow = SW_NORMAL;
  1864. ici.lpVerb = CMDSTR_NEWFOLDERA;
  1865. ici.fMask = CMIC_MASK_UNICODE | CMIC_MASK_FLAG_NO_UI;
  1866. ici.lpVerbW = CMDSTR_NEWFOLDERW;
  1867. hr = pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)(&ici));
  1868. }
  1869. else
  1870. {
  1871. CMINVOKECOMMANDINFO ici = {0};
  1872. ici.cbSize = sizeof(ici);
  1873. ici.hwnd = hwndParent;
  1874. ici.nShow = SW_NORMAL;
  1875. ici.lpVerb = CMDSTR_NEWFOLDERA;
  1876. ici.fMask = CMIC_MASK_FLAG_NO_UI;
  1877. // Win95 doesn't work with CMIC_MASK_UNICODE & CMDSTR_NEWFOLDERW
  1878. hr = pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)(&ici));
  1879. }
  1880. return hr;
  1881. }
  1882. BOOL CNscTree::_IsItemExpanded(HTREEITEM hti)
  1883. {
  1884. // if it's not open, then use it's parent
  1885. TV_ITEM tvi;
  1886. tvi.mask = TVIF_STATE;
  1887. tvi.stateMask = TVIS_EXPANDED;
  1888. tvi.hItem = (HTREEITEM)hti;
  1889. return (TreeView_GetItem(_hwndTree, &tvi) && (tvi.state & TVIS_EXPANDED));
  1890. }
  1891. HRESULT CNscTree::CreateNewFolder(HTREEITEM hti)
  1892. {
  1893. HRESULT hr = E_FAIL;
  1894. if (hti)
  1895. {
  1896. // If the user selected a folder item (file), we need
  1897. // to bind set the cache to the parent folder.
  1898. LPITEMIDLIST pidl = _GetFullIDList(hti);
  1899. if (pidl)
  1900. {
  1901. ULONG ulAttr = SFGAO_FOLDER; // make sure item is actually a folder
  1902. if (SUCCEEDED(IEGetAttributesOf(pidl, &ulAttr)))
  1903. {
  1904. HTREEITEM htiTarget; // tree item in which new folder is created
  1905. // Is it a folder?
  1906. if (ulAttr & SFGAO_FOLDER)
  1907. {
  1908. // non-Normal modes (!MODE_NORMAL) wants the new folder to be created as
  1909. // a sibling instead of as a child of the selected folder if it's
  1910. // closed. I assume their reasoning is that closed folders are often
  1911. // selected by accident/default because these views are mostly 1 level.
  1912. // We don't want this functionality for the normal mode.
  1913. if ((MODE_NORMAL != _mode) && !_IsItemExpanded(hti))
  1914. {
  1915. htiTarget = TreeView_GetParent(_hwndTree, hti); // yes, so fine.
  1916. }
  1917. else
  1918. {
  1919. htiTarget = hti;
  1920. }
  1921. }
  1922. else
  1923. {
  1924. htiTarget = TreeView_GetParent(_hwndTree, hti); // No, so bind to the parent.
  1925. }
  1926. if (NULL == htiTarget)
  1927. {
  1928. htiTarget = TVI_ROOT; // should be synonymous
  1929. }
  1930. // ensure that this pidl has MenuOrder information (see IE55 #94868)
  1931. if (!_IsOrdered(htiTarget) && _mode != MODE_NORMAL)
  1932. {
  1933. // its not "ordered" (doesn't have reg key persisting order of folder)
  1934. // then create make it ordered
  1935. if (SUCCEEDED(_PopulateOrderList(htiTarget)))
  1936. {
  1937. ASSERT(_hdpaOrd);
  1938. _FreeOrderList(htiTarget);
  1939. }
  1940. }
  1941. _CacheShellFolder(htiTarget);
  1942. }
  1943. ILFree(pidl);
  1944. }
  1945. }
  1946. // If no item is selected, we should still create a folder in whatever
  1947. // the user most recently dinked with. This is important if the
  1948. // Favorites folder is completely empty.
  1949. if (_psfCache)
  1950. {
  1951. IContextMenu *pcm;
  1952. if (GetUIVersion() < 5)
  1953. {
  1954. IShellView *psv;
  1955. hr = _psfCache->CreateViewObject(_hwndTree, IID_PPV_ARG(IShellView, &psv));
  1956. if (SUCCEEDED(hr))
  1957. {
  1958. hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARG(IContextMenu, &pcm));
  1959. if (SUCCEEDED(hr))
  1960. {
  1961. IUnknown_SetSite(pcm, psv);
  1962. }
  1963. psv->Release();
  1964. }
  1965. }
  1966. else
  1967. {
  1968. hr = CoCreateInstance(CLSID_NewMenu, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IContextMenu, &pcm));
  1969. }
  1970. if (SUCCEEDED(hr))
  1971. {
  1972. HMENU hmContext = CreatePopupMenu();
  1973. hr = pcm->QueryContextMenu(hmContext, 0, 1, 256, 0);
  1974. if (SUCCEEDED(hr))
  1975. {
  1976. _pidlNewFolderParent = _GetFullIDList(_htiCache);
  1977. IShellExtInit *psei;
  1978. if (SUCCEEDED(pcm->QueryInterface(IID_PPV_ARG(IShellExtInit, &psei))))
  1979. {
  1980. psei->Initialize(_pidlNewFolderParent, NULL, NULL);
  1981. psei->Release();
  1982. }
  1983. hr = _InvokeCommandThunk(pcm, _hwndParent);
  1984. SHChangeNotifyHandleEvents(); // Flush the events to it doesn't take forever to shift into edit mode
  1985. Pidl_Set(&_pidlNewFolderParent, NULL);
  1986. }
  1987. IUnknown_SetSite(pcm, NULL);
  1988. DestroyMenu(hmContext);
  1989. pcm->Release();
  1990. }
  1991. }
  1992. return hr;
  1993. }
  1994. HRESULT CNscTree::_EnterNewFolderEditMode(LPCITEMIDLIST pidlNewFolder)
  1995. {
  1996. HTREEITEM htiNewFolder = _FindFromRoot(NULL, pidlNewFolder);
  1997. LPITEMIDLIST pidlParent = NULL;
  1998. // 1. Flush all the notifications.
  1999. // 2. Find the new dir in the tree.
  2000. // Expand the parent if needed.
  2001. // 3. Put it into the rename mode.
  2002. EVAL(SUCCEEDED(SetSelectedItem(pidlNewFolder, FALSE, FALSE, 0)));
  2003. if (htiNewFolder == NULL)
  2004. {
  2005. pidlParent = ILClone(pidlNewFolder);
  2006. ILRemoveLastID(pidlParent);
  2007. HTREEITEM htiParent = _FindFromRoot(NULL, pidlParent);
  2008. // We are looking for the parent folder. If this is NOT
  2009. // the root, then we need to expand it to show it.
  2010. // NOTE: If it is root, Tree view will
  2011. // try and deref TVI_ROOT and faults.
  2012. if (htiParent != TVI_ROOT)
  2013. {
  2014. // Try expanding the parent and finding again.
  2015. CheckForExpandOnce(_hwndTree, htiParent);
  2016. TreeView_SelectItem(_hwndTree, htiParent);
  2017. _ExpandNode(htiParent, TVE_EXPAND, 1);
  2018. }
  2019. htiNewFolder = _FindFromRoot(NULL, pidlNewFolder);
  2020. }
  2021. if (htiNewFolder == NULL)
  2022. {
  2023. // Something went very wrong here. We are not able to find newly added node.
  2024. // One last try after refreshing the entire tree. (slow)
  2025. // May be we didn't get notification.
  2026. Refresh();
  2027. htiNewFolder = _FindFromRoot(NULL, pidlNewFolder);
  2028. if (htiNewFolder && (htiNewFolder != TVI_ROOT))
  2029. {
  2030. HTREEITEM htiParent = _FindFromRoot(NULL, pidlParent);
  2031. // We are looking for the parent folder. If this is NOT
  2032. // the root, then we need to expand it to show it.
  2033. // NOTE: If it is root, Tree view will
  2034. // try and deref TVI_ROOT and faults.
  2035. if (htiParent != TVI_ROOT)
  2036. {
  2037. CheckForExpandOnce(_hwndTree, htiParent);
  2038. TreeView_SelectItem(_hwndTree, htiParent);
  2039. _ExpandNode(htiParent, TVE_EXPAND, 1);
  2040. }
  2041. }
  2042. htiNewFolder = _FindFromRoot(NULL, pidlNewFolder);
  2043. }
  2044. // Put Edit label on the item for possible renaming by user.
  2045. if (htiNewFolder)
  2046. {
  2047. _fOkToRename = TRUE; //otherwise label editing is canceled
  2048. TreeView_EditLabel(_hwndTree, htiNewFolder);
  2049. _fOkToRename = FALSE;
  2050. }
  2051. if (pidlParent)
  2052. ILFree(pidlParent);
  2053. return S_OK;
  2054. }
  2055. HRESULT CNscTree::_OnSHNotifyCreate(LPCITEMIDLIST pidl, int iPosition, HTREEITEM htiParent)
  2056. {
  2057. HRESULT hr = S_OK;
  2058. HTREEITEM hti = NULL;
  2059. if (ILIsParent(_pidlRoot, pidl, FALSE))
  2060. {
  2061. LPITEMIDLIST pidlParent = ILCloneParent(pidl);
  2062. if (pidlParent)
  2063. {
  2064. hti = _FindFromRoot(NULL, pidlParent);
  2065. ILFree(pidlParent);
  2066. }
  2067. if (hti)
  2068. {
  2069. // folder exists in tree, if item expanded, load the node, else bag out.
  2070. if (_mode != MODE_NORMAL)
  2071. {
  2072. TV_ITEM tvi;
  2073. if (hti != TVI_ROOT)
  2074. {
  2075. tvi.mask = TVIF_STATE | TVIF_CHILDREN;
  2076. tvi.stateMask = (TVIS_EXPANDEDONCE | TVIS_EXPANDED | TVIS_EXPANDPARTIAL);
  2077. tvi.hItem = (HTREEITEM)hti;
  2078. if (!TreeView_GetItem(_hwndTree, &tvi))
  2079. return hr;
  2080. // If we drag and item over to a node which has never beem expanded
  2081. // before we will always fail to add the new node.
  2082. if (!(tvi.state & TVIS_EXPANDEDONCE))
  2083. {
  2084. CheckForExpandOnce(_hwndTree, hti);
  2085. tvi.mask = TVIF_STATE;
  2086. tvi.stateMask = (TVIS_EXPANDEDONCE | TVIS_EXPANDED | TVIS_EXPANDPARTIAL);
  2087. tvi.hItem = (HTREEITEM)hti;
  2088. // We need to reset this. This is causing some weird behaviour during drag and drop.
  2089. _fAsyncDrop = FALSE;
  2090. if (!TreeView_GetItem(_hwndTree, &tvi))
  2091. return hr;
  2092. }
  2093. }
  2094. else
  2095. tvi.state = (TVIS_EXPANDEDONCE); // evil root is always expanded.
  2096. if (tvi.state & TVIS_EXPANDEDONCE)
  2097. {
  2098. LPCITEMIDLIST pidlChild;
  2099. IShellFolder *psf;
  2100. hr = _ParentFromItem(pidl, &psf, &pidlChild);
  2101. if (SUCCEEDED(hr))
  2102. {
  2103. if (_fAsyncDrop) // inserted via drag/drop
  2104. {
  2105. int iNewPos = _fInsertBefore ? (_iDragDest - 1) : _iDragDest;
  2106. LPITEMIDLIST pidlReal;
  2107. if (SUCCEEDED(_IdlRealFromIdlSimple(psf, pidlChild, &pidlReal)))
  2108. {
  2109. if (_MoveNode(_iDragSrc, iNewPos, pidlReal))
  2110. {
  2111. TraceMsg(TF_NSC, "NSCBand: Reordering Item");
  2112. _fDropping = TRUE;
  2113. _Dropped();
  2114. _fAsyncDrop = FALSE;
  2115. _fDropping = FALSE;
  2116. }
  2117. ILFree(pidlReal);
  2118. }
  2119. _htiCur = NULL;
  2120. _fDragging = _fInserting = _fDropping = FALSE;
  2121. _iDragDest = _iDragSrc = -1;
  2122. }
  2123. else // standard shell notify create or drop with no insert, rename.
  2124. {
  2125. if (SUCCEEDED(hr))
  2126. {
  2127. if (_iDragDest >= 0)
  2128. iPosition = _iDragDest;
  2129. hr = _InsertChild(hti, psf, pidlChild, BOOLIFY(tvi.state & TVIS_SELECTED), TRUE, iPosition, NULL);
  2130. if (_iDragDest >= 0 &&
  2131. SUCCEEDED(_PopulateOrderList(hti)))
  2132. {
  2133. _fDropping = TRUE;
  2134. _Dropped();
  2135. _fDropping = FALSE;
  2136. }
  2137. }
  2138. }
  2139. psf->Release();
  2140. }
  2141. }
  2142. }
  2143. else // MODE_NORMAL
  2144. {
  2145. // no need to do anything, this item hasn't been expanded yet
  2146. if (TreeView_GetItemState(_hwndTree, hti, TVIS_EXPANDEDONCE) & TVIS_EXPANDEDONCE)
  2147. {
  2148. LPCITEMIDLIST pidlChild;
  2149. IShellFolder *psf;
  2150. if (SUCCEEDED(_ParentFromItem(pidl, &psf, &pidlChild)))
  2151. {
  2152. LPITEMIDLIST pidlReal;
  2153. if (SUCCEEDED(_IdlRealFromIdlSimple(psf, pidlChild, &pidlReal)))
  2154. {
  2155. do // scope
  2156. {
  2157. DWORD dwEnumFlags;
  2158. _GetEnumFlags(psf, pidlChild, &dwEnumFlags, NULL);
  2159. DWORD dwAttributes = SHGetAttributes(psf, pidlReal, SFGAO_FOLDER | SFGAO_HIDDEN | SFGAO_STREAM);
  2160. // filter out zip files (they are both folders and files but we treat them as files)
  2161. // on downlevel SFGAO_STREAM is the same as SFGAO_HASSTORAGE so we'll let zip files slide through (oh well)
  2162. // better than not adding filesystem folders (that have storage)
  2163. DWORD dwFlags = SFGAO_FOLDER;
  2164. if (IsOS(OS_WHISTLERORGREATER))
  2165. dwFlags |= SFGAO_STREAM;
  2166. if ((dwAttributes & dwFlags) == SFGAO_FOLDER)
  2167. {
  2168. if (!(dwEnumFlags & SHCONTF_FOLDERS))
  2169. break; // item is folder but client does not want folders
  2170. }
  2171. else if (!(dwEnumFlags & SHCONTF_NONFOLDERS))
  2172. break; // item is file, but client only wants folders
  2173. if (!(dwEnumFlags & SHCONTF_INCLUDEHIDDEN) &&
  2174. (dwAttributes & SFGAO_HIDDEN))
  2175. break;
  2176. hr = _InsertChild(hti, psf, pidlReal, FALSE, TRUE, iPosition, NULL);
  2177. if (S_OK == hr)
  2178. {
  2179. TreeView_SetChildren(_hwndTree, hti, NSC_CHILDREN_ADD);
  2180. }
  2181. } while (0); // Execute the block only once
  2182. ILFree(pidlReal);
  2183. }
  2184. psf->Release();
  2185. }
  2186. }
  2187. else
  2188. {
  2189. TreeView_SetChildren(_hwndTree, hti, NSC_CHILDREN_CALLBACK);
  2190. }
  2191. }
  2192. }
  2193. }
  2194. //if the item is being moved from a folder and we have it's position, we need to fix up the order in the old folder
  2195. if (_mode != MODE_NORMAL && iPosition >= 0) //htiParent && (htiParent != hti) &&
  2196. {
  2197. //item was deleted, need to fixup order info
  2198. _ReorderChildren(htiParent);
  2199. }
  2200. _UpdateActiveBorder(_htiActiveBorder);
  2201. return hr;
  2202. }
  2203. //FEATURE: make this void
  2204. HRESULT CNscTree::_OnDeleteItem(NM_TREEVIEW *pnm)
  2205. {
  2206. if (_htiActiveBorder == pnm->itemOld.hItem)
  2207. _htiActiveBorder = NULL;
  2208. ITEMINFO * pii = (ITEMINFO *) pnm->itemOld.lParam;
  2209. pnm->itemOld.lParam = NULL;
  2210. OrderItem_Free(pii->poi, TRUE);
  2211. LocalFree(pii);
  2212. pii = NULL;
  2213. return S_OK;
  2214. }
  2215. void CNscTree::_GetDefaultIconIndex(LPCITEMIDLIST pidl, ULONG ulAttrs, TVITEM *pitem, BOOL fFolder)
  2216. {
  2217. if (_iDefaultFavoriteIcon == 0)
  2218. {
  2219. WCHAR psz[MAX_PATH];
  2220. int iTemp = 0;
  2221. DWORD cbSize = ARRAYSIZE(psz);
  2222. if (SUCCEEDED(AssocQueryString(0, ASSOCSTR_DEFAULTICON, TEXT("InternetShortcut"), NULL, psz, &cbSize)))
  2223. iTemp = PathParseIconLocation(psz);
  2224. _iDefaultFavoriteIcon = Shell_GetCachedImageIndex(psz, iTemp, 0);
  2225. cbSize = ARRAYSIZE(psz);
  2226. if (SUCCEEDED(AssocQueryString(0, ASSOCSTR_DEFAULTICON, TEXT("Folder"), NULL, psz, &cbSize)))
  2227. iTemp = PathParseIconLocation(psz);
  2228. _iDefaultFolderIcon = Shell_GetCachedImageIndex(psz, iTemp, 0);
  2229. }
  2230. pitem->iImage = pitem->iSelectedImage = (fFolder) ? _iDefaultFolderIcon : _iDefaultFavoriteIcon;
  2231. }
  2232. BOOL CNscTree::_LoadOrder(HTREEITEM hti, LPCITEMIDLIST pidl, IShellFolder* psf, HDPA* phdpa)
  2233. {
  2234. BOOL fOrdered = FALSE;
  2235. HDPA hdpaOrder = NULL;
  2236. IStream *pstm = GetOrderStream(pidl, STGM_READ);
  2237. if (pstm)
  2238. {
  2239. OrderList_LoadFromStream(pstm, &hdpaOrder, psf);
  2240. pstm->Release();
  2241. }
  2242. fOrdered = !((hdpaOrder == NULL) || (DPA_GetPtrCount(hdpaOrder) == 0));
  2243. //set the tree item's ordered flag
  2244. PORDERITEM poi;
  2245. if (hti == TVI_ROOT)
  2246. {
  2247. _fOrdered = fOrdered;
  2248. }
  2249. else if ((poi = _GetTreeOrderItem(hti)) != NULL)
  2250. {
  2251. poi->lParam = fOrdered;
  2252. }
  2253. *phdpa = hdpaOrder;
  2254. return fOrdered;
  2255. }
  2256. // load shell folder and deal with persisted ordering.
  2257. HRESULT CNscTree::_LoadSF(HTREEITEM htiRoot, LPCITEMIDLIST pidl, BOOL *pfOrdered)
  2258. {
  2259. ASSERT(pfOrdered);
  2260. #ifdef DEBUG
  2261. TraceHTREE(htiRoot, TEXT("Loading the Shell Folder for"));
  2262. #endif
  2263. HRESULT hr = S_OK;
  2264. IDVGetEnum *pdvge;
  2265. if (_pidlNavigatingTo && ILIsEqual(pidl, _pidlNavigatingTo) && SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IDVGetEnum, &pdvge))))
  2266. {
  2267. pdvge->Release(); // we don't need this, just checking if view supports enumeration stealing
  2268. // If we want to expand the item that we are navigating to,
  2269. // then let's wait for the CDefView to populate so that we
  2270. // can go steal its contents
  2271. _fExpandNavigateTo = TRUE;
  2272. if (_fNavigationFinished)
  2273. {
  2274. _CacheShellFolder(htiRoot); // make sure we cache folder in case it is misbehaving shell extension
  2275. LPITEMIDLIST pidlClone;
  2276. hr = SHILClone(pidl, &pidlClone);
  2277. if (SUCCEEDED(hr))
  2278. hr = RightPaneNavigationFinished(pidlClone); // function takes ownership of pidl
  2279. }
  2280. }
  2281. else
  2282. {
  2283. hr = _StartBackgroundEnum(htiRoot, pidl, pfOrdered, FALSE);
  2284. }
  2285. return hr;
  2286. }
  2287. HRESULT CNscTree::_StartBackgroundEnum(HTREEITEM htiRoot, LPCITEMIDLIST pidl,
  2288. BOOL *pfOrdered, BOOL fUpdatePidls)
  2289. {
  2290. HRESULT hr = E_OUTOFMEMORY;
  2291. if (_CacheShellFolder(htiRoot))
  2292. {
  2293. HDPA hdpaOrder = NULL;
  2294. IShellFolder *psfItem = _psfCache;
  2295. psfItem->AddRef(); // hang on as adding items may change the cached psfCache
  2296. *pfOrdered = _LoadOrder(htiRoot, pidl, psfItem, &hdpaOrder);
  2297. DWORD grfFlags;
  2298. DWORD dwSig = 0;
  2299. _GetEnumFlags(psfItem, pidl, &grfFlags, NULL);
  2300. if (htiRoot && htiRoot != TVI_ROOT)
  2301. {
  2302. ITEMINFO *pii = _GetTreeItemInfo(htiRoot);
  2303. if (pii)
  2304. dwSig = pii->dwSig;
  2305. }
  2306. else
  2307. {
  2308. htiRoot = TVI_ROOT;
  2309. }
  2310. if (_pTaskScheduler)
  2311. {
  2312. // AddNscEnumTask takes ownership of hdpaOrder, but not the pidls
  2313. hr = AddNscEnumTask(_pTaskScheduler, pidl, s_NscEnumCallback, this,
  2314. (UINT_PTR)htiRoot, dwSig, grfFlags, hdpaOrder,
  2315. _pidlExpandingTo, _dwOrderSig, !_fInExpand,
  2316. _uDepth, _fUpdate, fUpdatePidls);
  2317. if (SUCCEEDED(hr) && !_fInExpand)
  2318. {
  2319. _fShouldShowAppStartCursor = TRUE;
  2320. }
  2321. }
  2322. psfItem->Release();
  2323. }
  2324. return hr;
  2325. }
  2326. // s_NscEnumCallback : Callback function for the background enumration.
  2327. // This function takes ownership of the hdpa and the pidls.
  2328. void CNscTree::s_NscEnumCallback(CNscTree *pns, LPITEMIDLIST pidl, UINT_PTR uId, DWORD dwSig, HDPA hdpa,
  2329. LPITEMIDLIST pidlExpandingTo, DWORD dwOrderSig, UINT uDepth,
  2330. BOOL fUpdate, BOOL fUpdatePidls)
  2331. {
  2332. NSC_BKGDENUMDONEDATA * pbedd = new NSC_BKGDENUMDONEDATA;
  2333. if (pbedd)
  2334. {
  2335. pbedd->pidl = pidl;
  2336. pbedd->hitem = (HTREEITEM)uId;
  2337. pbedd->dwSig = dwSig;
  2338. pbedd->hdpa = hdpa;
  2339. pbedd->pidlExpandingTo = pidlExpandingTo;
  2340. pbedd->dwOrderSig = dwOrderSig;
  2341. pbedd->uDepth = uDepth;
  2342. pbedd->fUpdate = fUpdate;
  2343. pbedd->fUpdatePidls = fUpdatePidls;
  2344. // get the lock so that we can add the data to the end of the list
  2345. NSC_BKGDENUMDONEDATA **ppbeddWalk = NULL;
  2346. EnterCriticalSection(&pns->_csBackgroundData);
  2347. // Start at the head. We use a pointer to pointer here to eliminate special cases
  2348. ppbeddWalk = &pns->_pbeddList;
  2349. // First walk to the end of the list
  2350. while (*ppbeddWalk)
  2351. ppbeddWalk = &(*ppbeddWalk)->pNext;
  2352. *ppbeddWalk = pbedd;
  2353. LeaveCriticalSection(&pns->_csBackgroundData);
  2354. // It's ok to ignore the return value here. The data will be cleaned up when the
  2355. // CNscTree object gets destroyed
  2356. if (::IsWindow(pns->_hwndTree))
  2357. ::PostMessage(pns->_hwndTree, WM_NSCBACKGROUNDENUMDONE, (WPARAM)NULL, (LPARAM)NULL);
  2358. }
  2359. else
  2360. {
  2361. ILFree(pidl);
  2362. ILFree(pidlExpandingTo);
  2363. OrderList_Destroy(&hdpa, TRUE);
  2364. }
  2365. }
  2366. BOOL OrderList_Insert(HDPA hdpa, int iIndex, LPITEMIDLIST pidl, int nOrder)
  2367. {
  2368. PORDERITEM poi = OrderItem_Create(pidl, nOrder);
  2369. if (poi)
  2370. {
  2371. if (-1 != DPA_InsertPtr(hdpa, iIndex, poi))
  2372. return TRUE;
  2373. OrderItem_Free(poi, TRUE); // free pid
  2374. }
  2375. return FALSE;
  2376. }
  2377. void CNscTree::_EnumBackgroundDone(NSC_BKGDENUMDONEDATA *pbedd)
  2378. {
  2379. HCURSOR hCursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
  2380. HTREEITEM hti = pbedd->hitem;
  2381. TVITEM tvi;
  2382. tvi.mask = TVIF_PARAM;
  2383. tvi.hItem = hti;
  2384. // This can fail if the item was moved before the async icon
  2385. // extraction finished for that item.
  2386. ITEMINFO* pii = NULL;
  2387. if (hti != TVI_ROOT && TreeView_GetItem(_hwndTree, &tvi))
  2388. {
  2389. pii = GetPii(tvi.lParam);
  2390. // Check if we have the right guy
  2391. if (pii->dwSig != pbedd->dwSig)
  2392. {
  2393. // Try to find it using the pidl
  2394. hti = _FindFromRoot(NULL, pbedd->pidl);
  2395. if (hti)
  2396. pii = _GetTreeItemInfo(hti);
  2397. }
  2398. }
  2399. if ((hti == TVI_ROOT || (pii && pii->dwSig == pbedd->dwSig)) && _CacheShellFolder(hti))
  2400. {
  2401. // Check if the ordering has changed while we were doing the background enumeration
  2402. if (pbedd->dwOrderSig == _dwOrderSig)
  2403. {
  2404. IShellFolder *psfItem = _psfCache;
  2405. psfItem->AddRef(); // hang on as adding items may change the cached psfCache
  2406. BOOL fInRename = _fInLabelEdit;
  2407. HTREEITEM htiWasRenaming = fInRename ? _htiRenaming : NULL;
  2408. HTREEITEM htiExpandTo = NULL;
  2409. if (pbedd->pidlExpandingTo)
  2410. htiExpandTo = _FindChild(psfItem, hti, pbedd->pidlExpandingTo);
  2411. BOOL fParentMarked = _IsMarked(hti);
  2412. BOOL fItemWasAdded = FALSE;
  2413. BOOL fItemAlreadyIn = FALSE;
  2414. ::SendMessage(_hwndTree, WM_SETREDRAW, FALSE, 0);
  2415. HTREEITEM htiTemp;
  2416. HTREEITEM htiLast = NULL;
  2417. // find last child
  2418. for (htiTemp = TreeView_GetChild(_hwndTree, hti); htiTemp;)
  2419. {
  2420. htiLast = htiTemp;
  2421. htiTemp = TreeView_GetNextSibling(_hwndTree, htiTemp);
  2422. }
  2423. HTREEITEM htiCur = htiLast;
  2424. BOOL bReorder = FALSE;
  2425. int iCur = DPA_GetPtrCount(pbedd->hdpa);
  2426. for (htiTemp = htiLast; htiTemp;)
  2427. {
  2428. HTREEITEM htiNextChild = TreeView_GetPrevSibling(_hwndTree, htiTemp);
  2429. // must delete in this way or break the linkage of tree.
  2430. int iIndex = _TreeItemIndexInHDPA(pbedd->hdpa, psfItem, htiTemp, iCur);
  2431. if (-1 == iIndex)
  2432. {
  2433. PORDERITEM poi = _GetTreeOrderItem(htiTemp);
  2434. if (poi)
  2435. {
  2436. DWORD dwAttrib = SFGAO_VALIDATE;
  2437. if (FAILED(psfItem->GetAttributesOf(1, (LPCITEMIDLIST*)&poi->pidl, &dwAttrib)))
  2438. {
  2439. TreeView_DeleteItem(_hwndTree, htiTemp);
  2440. if (htiCur == htiTemp)
  2441. {
  2442. htiCur = htiNextChild;
  2443. }
  2444. }
  2445. else
  2446. {
  2447. // the item is valid but it didn't get enumerated (possible in partial network enumeration)
  2448. // we need to add it to our list of new items
  2449. LPITEMIDLIST pidl = ILClone(poi->pidl);
  2450. if (pidl)
  2451. {
  2452. if (!OrderList_Insert(pbedd->hdpa, iCur, pidl, -1)) //frees the pidl
  2453. {
  2454. // must delete item or our insertion below will be out of whack
  2455. TreeView_DeleteItem(_hwndTree, htiTemp);
  2456. if (htiCur == htiTemp)
  2457. {
  2458. htiCur = htiNextChild;
  2459. }
  2460. }
  2461. else
  2462. {
  2463. bReorder = TRUE; // we reinserted the item into the order list, must reorder
  2464. }
  2465. }
  2466. }
  2467. }
  2468. }
  2469. else
  2470. {
  2471. iCur = iIndex; // our next orderlist insertion point
  2472. }
  2473. htiTemp = htiNextChild;
  2474. }
  2475. if (!_fOrdered)
  2476. {
  2477. int cAdded = DPA_GetPtrCount(pbedd->hdpa);
  2478. // htiCur contains the last sibling in that branch
  2479. HTREEITEM htiInsertPosition = htiCur ? htiCur : TVI_FIRST;
  2480. // Now adding all the new elements starting from the last, since adding at the end of the tree
  2481. // is very slow
  2482. for (int i = cAdded-1; i >= 0; i--)
  2483. {
  2484. PORDERITEM pitoi = (PORDERITEM)DPA_FastGetPtr(pbedd->hdpa, i);
  2485. if (pitoi == NULL)
  2486. break;
  2487. if (htiCur)
  2488. {
  2489. PORDERITEM poi = _GetTreeOrderItem(htiCur);
  2490. if (poi)
  2491. {
  2492. HRESULT hr = psfItem->CompareIDs(0, pitoi->pidl, poi->pidl);
  2493. // If the item is already there, let's not add it again
  2494. if (HRESULT_CODE(hr) == 0)
  2495. {
  2496. fItemAlreadyIn = TRUE;
  2497. if (pbedd->fUpdatePidls)
  2498. {
  2499. _AssignPidl(poi, pitoi->pidl);
  2500. }
  2501. // Get to the next item
  2502. htiCur = TreeView_GetPrevSibling(_hwndTree, htiCur);
  2503. htiInsertPosition = htiCur;
  2504. if (!htiCur)
  2505. htiInsertPosition = TVI_FIRST;
  2506. continue;
  2507. }
  2508. }
  2509. }
  2510. if (_ShouldShow(psfItem, pbedd->pidl, pitoi->pidl))
  2511. {
  2512. int cChildren = 1;
  2513. if (MODE_NORMAL == _mode)
  2514. {
  2515. DWORD dwAttrib = SHGetAttributes(psfItem, pitoi->pidl, SFGAO_FOLDER | SFGAO_STREAM);
  2516. cChildren = _GetChildren(psfItem, pitoi->pidl, dwAttrib);
  2517. }
  2518. // If this is a normal NSC, we need to display the plus sign correctly.
  2519. if (_AddItemToTree(hti, pitoi->pidl, cChildren, pitoi->nOrder, htiInsertPosition, FALSE, fParentMarked))
  2520. {
  2521. fItemWasAdded = TRUE;
  2522. }
  2523. else
  2524. {
  2525. break;
  2526. }
  2527. }
  2528. }
  2529. }
  2530. else // _fOrdered
  2531. {
  2532. if (bReorder)
  2533. {
  2534. OrderList_Reorder(pbedd->hdpa);
  2535. }
  2536. LPITEMIDLIST pidlParent = _GetFullIDList(hti);
  2537. if (pidlParent)
  2538. {
  2539. int celt = DPA_GetPtrCount(pbedd->hdpa);
  2540. for (int i = 0; i < celt; i++)
  2541. {
  2542. PORDERITEM pitoi = (PORDERITEM)DPA_FastGetPtr(pbedd->hdpa, i);
  2543. if (pitoi == NULL)
  2544. break;
  2545. LPITEMIDLIST pidlFull = ILCombine(pidlParent, pitoi->pidl);
  2546. if (pidlFull)
  2547. {
  2548. htiTemp = _FindFromRoot(hti, pidlFull);
  2549. // if we DON'T FIND IT add it to the tree . . .
  2550. if (!htiTemp)
  2551. {
  2552. if (_AddItemToTree(hti, pitoi->pidl, 1, pitoi->nOrder, TVI_LAST, FALSE, fParentMarked))
  2553. {
  2554. fItemWasAdded = TRUE;
  2555. }
  2556. else
  2557. {
  2558. break;
  2559. }
  2560. }
  2561. else
  2562. {
  2563. PORDERITEM poiItem = _GetTreeOrderItem(htiTemp);
  2564. if (poiItem)
  2565. {
  2566. poiItem->nOrder = pitoi->nOrder;
  2567. }
  2568. fItemAlreadyIn = TRUE;
  2569. }
  2570. ILFree(pidlFull);
  2571. }
  2572. }
  2573. ILFree(pidlParent);
  2574. }
  2575. _Sort(hti, _psfCache);
  2576. }
  2577. if (fItemWasAdded || fItemAlreadyIn)
  2578. {
  2579. //make sure something is selected, otherwise first click selects instead of expanding/collapsing/navigating
  2580. HTREEITEM htiSelected = TreeView_GetSelection(_hwndTree);
  2581. if (!htiSelected)
  2582. {
  2583. htiSelected = TreeView_GetFirstVisible(_hwndTree);
  2584. _SelectNoExpand(_hwndTree, htiSelected); // do not expand this guy
  2585. }
  2586. if (hti != TVI_ROOT)
  2587. {
  2588. // if this is updatedir, don't expand the node
  2589. if (!pbedd->fUpdate)
  2590. {
  2591. // Check to see if it's expanded.
  2592. tvi.mask = TVIF_STATE;
  2593. tvi.stateMask = (TVIS_EXPANDEDONCE | TVIS_EXPANDED | TVIS_EXPANDPARTIAL);
  2594. tvi.hItem = hti;
  2595. if (TreeView_GetItem(_hwndTree, &tvi))
  2596. {
  2597. if (!(tvi.state & TVIS_EXPANDED) || (tvi.state & TVIS_EXPANDPARTIAL))
  2598. {
  2599. _fIgnoreNextItemExpanding = TRUE;
  2600. _ExpandNode(hti, TVE_EXPAND, 1);
  2601. _fIgnoreNextItemExpanding = FALSE;
  2602. }
  2603. }
  2604. }
  2605. // Handle full recursive expansion case.
  2606. if (pbedd->uDepth)
  2607. {
  2608. for (htiTemp = TreeView_GetChild(_hwndTree, hti); htiTemp;)
  2609. {
  2610. HTREEITEM htiNextChild = TreeView_GetNextSibling(_hwndTree, htiTemp);
  2611. _ExpandNode(htiTemp, TVE_EXPAND, pbedd->uDepth);
  2612. htiTemp = htiNextChild;
  2613. }
  2614. if (TVI_ROOT != htiSelected)
  2615. TreeView_EnsureVisible(_hwndTree, htiSelected);
  2616. }
  2617. }
  2618. }
  2619. else if (MODE_NORMAL == _mode && !IsOS(OS_WHISTLERORGREATER)) //see comment in OnItemExpanding
  2620. {
  2621. // we didn't add anything, we better remove +
  2622. TreeView_SetChildren(_hwndTree, hti, NSC_CHILDREN_REMOVE);
  2623. }
  2624. // we're doing refresh/update dir, we don't care if items were added or not
  2625. if (pbedd->fUpdate)
  2626. {
  2627. for (htiTemp = TreeView_GetChild(_hwndTree, hti); htiTemp; htiTemp = TreeView_GetNextSibling(_hwndTree, htiTemp))
  2628. {
  2629. PORDERITEM pitoi = _GetTreeOrderItem(htiTemp);
  2630. if (!pitoi)
  2631. break;
  2632. if (SHGetAttributes(psfItem, pitoi->pidl, SFGAO_FOLDER | SFGAO_STREAM) == SFGAO_FOLDER)
  2633. {
  2634. UINT uState = TVIS_EXPANDED;
  2635. if (TVI_ROOT != htiTemp)
  2636. uState = TreeView_GetItemState(_hwndTree, htiTemp, TVIS_EXPANDEDONCE | TVIS_EXPANDED | TVIS_EXPANDPARTIAL);
  2637. if (uState & TVIS_EXPANDED)
  2638. {
  2639. LPITEMIDLIST pidlFull = ILCombine(pbedd->pidl, pitoi->pidl);
  2640. if (pidlFull)
  2641. {
  2642. BOOL fOrdered;
  2643. _fUpdate = TRUE;
  2644. _fInExpand = BOOLIFY(uState & TVIS_EXPANDPARTIAL);
  2645. _StartBackgroundEnum(htiTemp, pidlFull, &fOrdered, pbedd->fUpdatePidls);
  2646. _fInExpand = FALSE;
  2647. _fUpdate = FALSE;
  2648. ILFree(pidlFull);
  2649. }
  2650. }
  2651. else if (uState & TVIS_EXPANDEDONCE)
  2652. {
  2653. TreeView_DeleteChildren(_hwndTree, htiTemp);
  2654. TreeView_SetChildren(_hwndTree, htiTemp, NSC_CHILDREN_CALLBACK);
  2655. }
  2656. }
  2657. }
  2658. }
  2659. ::SendMessage(_hwndTree, WM_SETREDRAW, TRUE, 0);
  2660. if (htiExpandTo)
  2661. TreeView_EnsureVisible(_hwndTree, htiExpandTo);
  2662. if (fItemWasAdded && fInRename)
  2663. {
  2664. _fOkToRename = TRUE; //otherwise label editing is canceled
  2665. TreeView_EditLabel(_hwndTree, htiWasRenaming);
  2666. _fOkToRename = FALSE;
  2667. }
  2668. psfItem->Release();
  2669. }
  2670. else
  2671. {
  2672. BOOL fOrdered;
  2673. // The order has changed, we need start over again using the new order
  2674. _StartBackgroundEnum(hti, pbedd->pidl, &fOrdered, pbedd->fUpdatePidls);
  2675. }
  2676. }
  2677. delete pbedd;
  2678. SetCursor(hCursorOld);
  2679. }
  2680. // review chrisny: get rid of this function.
  2681. int CNscTree::_GetChildren(IShellFolder *psf, LPCITEMIDLIST pidl, ULONG ulAttrs)
  2682. {
  2683. int cChildren = 0; // assume none
  2684. // treat zip folders as files (they are both folders and files but we treat them as files)
  2685. // on downlevel SFGAO_STREAM is the same as SFGAO_HASSTORAGE so we'll let zip files slide through (oh well)
  2686. // better than not adding filesystem folders (that have storage)
  2687. if ((ulAttrs & SFGAO_FOLDER))
  2688. {
  2689. if (IsOS(OS_WHISTLERORGREATER))
  2690. cChildren = I_CHILDRENAUTO; // let treeview handle +'s
  2691. if (_grfFlags & SHCONTF_FOLDERS)
  2692. {
  2693. // if just folders we can peek at the attributes
  2694. if (SHGetAttributes(psf, pidl, SFGAO_HASSUBFOLDER))
  2695. cChildren = 1;
  2696. }
  2697. if (cChildren != 1 && (_grfFlags & SHCONTF_NONFOLDERS))
  2698. {
  2699. // there is no SFGAO_ bit that includes non folders so we need to enum
  2700. IShellFolder *psfItem;
  2701. if (SUCCEEDED(psf->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfItem))))
  2702. {
  2703. // if we are showing non folders we have to do an enum to peek down at items below
  2704. IEnumIDList *penum;
  2705. if (S_OK == _GetEnum(psfItem, NULL, &penum))
  2706. {
  2707. ULONG celt;
  2708. LPITEMIDLIST pidlTemp;
  2709. if (penum->Next(1, &pidlTemp, &celt) == S_OK && celt == 1)
  2710. {
  2711. //do not call ShouldShow here because we will end up without + if the item is filtered out
  2712. //it's better to have an extra + that is going to go away when user clicks on it
  2713. //than to not be able to expand item with valid children
  2714. cChildren = 1;
  2715. ILFree(pidlTemp);
  2716. }
  2717. penum->Release();
  2718. }
  2719. psfItem->Release();
  2720. }
  2721. }
  2722. }
  2723. return cChildren;
  2724. }
  2725. void CNscTree::_OnGetDisplayInfo(TV_DISPINFO *pnm)
  2726. {
  2727. PORDERITEM poi = GetPoi(pnm->item.lParam);
  2728. LPCITEMIDLIST pidl = _CacheParentShellFolder(pnm->item.hItem, poi->pidl);
  2729. ASSERT(pidl);
  2730. if (pidl == NULL)
  2731. return;
  2732. ASSERT(_psfCache);
  2733. ASSERT(pnm->item.mask & (TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_CHILDREN));
  2734. if (pnm->item.mask & TVIF_TEXT)
  2735. {
  2736. SHELLDETAILS details;
  2737. if (SUCCEEDED(_GetDisplayNameOf(pidl, SHGDN_INFOLDER, &details)))
  2738. StrRetToBuf(&details.str, pidl, pnm->item.pszText, pnm->item.cchTextMax);
  2739. }
  2740. // make sure we set the attributes for those flags that need them
  2741. if (pnm->item.mask & (TVIF_CHILDREN | TVIF_IMAGE | TVIF_SELECTEDIMAGE))
  2742. {
  2743. ULONG ulAttrs = SHGetAttributes(_psfCache, pidl, SFGAO_FOLDER | SFGAO_STREAM | SFGAO_NEWCONTENT);
  2744. // review chrisny: still need to handle notify of changes from
  2745. // other navs.
  2746. // HACKHACK!!! we're using the TVIS_FOCUSED bit to stored whether there's
  2747. // new content or not.
  2748. if (ulAttrs & SFGAO_NEWCONTENT)
  2749. {
  2750. pnm->item.mask |= TVIF_STATE;
  2751. pnm->item.stateMask = TVIS_FOCUSED; // init state mask to bold
  2752. pnm->item.state = TVIS_FOCUSED; // init state mask to bold
  2753. }
  2754. // Also see if this guy has any child folders
  2755. if (pnm->item.mask & TVIF_CHILDREN)
  2756. pnm->item.cChildren = _GetChildren(_psfCache, pidl, ulAttrs);
  2757. if (pnm->item.mask & (TVIF_IMAGE | TVIF_SELECTEDIMAGE))
  2758. // We now need to map the item into the right image index.
  2759. _GetDefaultIconIndex(pidl, ulAttrs, &pnm->item, (ulAttrs & SFGAO_FOLDER));
  2760. _UpdateItemDisplayInfo(pnm->item.hItem);
  2761. }
  2762. // force the treeview to store this so we don't get called back again
  2763. pnm->item.mask |= TVIF_DI_SETITEM;
  2764. }
  2765. #define SZ_CUTA "cut"
  2766. #define SZ_CUT TEXT(SZ_CUTA)
  2767. #define SZ_RENAMEA "rename"
  2768. #define SZ_RENAME TEXT(SZ_RENAMEA)
  2769. void CNscTree::_ApplyCmd(HTREEITEM hti, IContextMenu *pcm, UINT idCmd)
  2770. {
  2771. TCHAR szCommandString[40];
  2772. BOOL fHandled = FALSE;
  2773. BOOL fCutting = FALSE;
  2774. // We need to special case the rename command
  2775. if (SUCCEEDED(ContextMenu_GetCommandStringVerb(pcm, idCmd, szCommandString, ARRAYSIZE(szCommandString))))
  2776. {
  2777. if (StrCmpI(szCommandString, SZ_RENAME)==0)
  2778. {
  2779. TreeView_EditLabel(_hwndTree, hti);
  2780. fHandled = TRUE;
  2781. }
  2782. else if (!StrCmpI(szCommandString, SZ_CUT))
  2783. {
  2784. fCutting = TRUE;
  2785. }
  2786. }
  2787. if (!fHandled)
  2788. {
  2789. CMINVOKECOMMANDINFO ici = {
  2790. sizeof(CMINVOKECOMMANDINFO),
  2791. 0,
  2792. _hwndTree,
  2793. MAKEINTRESOURCEA(idCmd),
  2794. NULL, NULL,
  2795. SW_NORMAL,
  2796. };
  2797. HRESULT hr = pcm->InvokeCommand(&ici);
  2798. if (fCutting && SUCCEEDED(hr))
  2799. {
  2800. TV_ITEM tvi;
  2801. tvi.mask = TVIF_STATE;
  2802. tvi.stateMask = TVIS_CUT;
  2803. tvi.state = TVIS_CUT;
  2804. tvi.hItem = hti;
  2805. TreeView_SetItem(_hwndTree, &tvi);
  2806. // _hwndNextViewer = SetClipboardViewer(_hwndTree);
  2807. // _htiCut = hti;
  2808. }
  2809. //hack to force a selection update, so oc can update it's status text
  2810. if (_mode & MODE_CONTROL)
  2811. {
  2812. HTREEITEM hti = TreeView_GetSelection(_hwndTree);
  2813. ::SendMessage(_hwndTree, WM_SETREDRAW, FALSE, 0);
  2814. TreeView_SelectItem(_hwndTree, NULL);
  2815. //only select the item if the handle is still valid
  2816. if (hti)
  2817. TreeView_SelectItem(_hwndTree, hti);
  2818. ::SendMessage(_hwndTree, WM_SETREDRAW, TRUE, 0);
  2819. }
  2820. }
  2821. }
  2822. // perform actions like they were chosen from the context menu, but without showing the menu
  2823. HRESULT CNscTree::_InvokeContextMenuCommand(BSTR strCommand)
  2824. {
  2825. ASSERT(strCommand);
  2826. HTREEITEM htiSelected = TreeView_GetSelection(_hwndTree);
  2827. if (htiSelected)
  2828. {
  2829. if (StrCmpIW(strCommand, L"rename") == 0)
  2830. {
  2831. _fOkToRename = TRUE; //otherwise label editing is canceled
  2832. TreeView_EditLabel(_hwndTree, htiSelected);
  2833. _fOkToRename = FALSE;
  2834. }
  2835. else
  2836. {
  2837. LPCITEMIDLIST pidl = _CacheParentShellFolder(htiSelected, NULL);
  2838. if (pidl)
  2839. {
  2840. IContextMenu *pcm;
  2841. if (SUCCEEDED(_psfCache->GetUIObjectOf(_hwndTree, 1, &pidl, IID_PPV_ARG_NULL(IContextMenu, &pcm))))
  2842. {
  2843. CHAR szCommand[MAX_PATH];
  2844. SHUnicodeToAnsi(strCommand, szCommand, ARRAYSIZE(szCommand));
  2845. // QueryContextMenu, even though unused, initializes the folder properly (fixes delete subscription problems)
  2846. HMENU hmenu = CreatePopupMenu();
  2847. if (hmenu)
  2848. pcm->QueryContextMenu(hmenu, 0, 0, 0x7fff, CMF_NORMAL);
  2849. /* Need to try twice, in case callee is ANSI-only */
  2850. CMINVOKECOMMANDINFOEX ici =
  2851. {
  2852. CMICEXSIZE_NT4, /* Be NT4-compat */
  2853. CMIC_MASK_UNICODE,
  2854. _hwndTree,
  2855. szCommand,
  2856. NULL, NULL,
  2857. SW_NORMAL,
  2858. 0, NULL,
  2859. NULL,
  2860. strCommand,
  2861. NULL, NULL,
  2862. NULL,
  2863. };
  2864. HRESULT hr = pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
  2865. if (hr == E_INVALIDARG)
  2866. {
  2867. // Recipient didn't like the unicode command; send an ANSI one
  2868. ici.cbSize = sizeof(CMINVOKECOMMANDINFO);
  2869. ici.fMask &= ~CMIC_MASK_UNICODE;
  2870. pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
  2871. }
  2872. // do any visuals for cut state
  2873. if (SUCCEEDED(hr) && StrCmpIW(strCommand, L"cut") == 0)
  2874. {
  2875. HTREEITEM hti = TreeView_GetSelection(_hwndTree);
  2876. if (hti)
  2877. {
  2878. _TreeSetItemState(hti, TVIS_CUT, TVIS_CUT);
  2879. ASSERT(!_hwndNextViewer);
  2880. _hwndNextViewer = ::SetClipboardViewer(_hwndTree);
  2881. _htiCut = hti;
  2882. }
  2883. }
  2884. if (hmenu)
  2885. DestroyMenu(hmenu);
  2886. pcm->Release();
  2887. }
  2888. }
  2889. }
  2890. //if properties was invoked, who knows what might have changed, so force a reselect
  2891. if (StrCmpNW(strCommand, L"properties", 10) == 0)
  2892. {
  2893. TreeView_SelectItem(_hwndTree, htiSelected);
  2894. }
  2895. }
  2896. return S_OK;
  2897. }
  2898. //
  2899. // pcm = IContextMenu for the item the user selected
  2900. // hti = the item the user selected
  2901. //
  2902. // Okay, this menu thing is kind of funky.
  2903. //
  2904. // If "Favorites", then everybody gets "Create new folder".
  2905. //
  2906. // If expandable:
  2907. // Show "Expand" or "Collapse"
  2908. // (accordingly) and set it as the default.
  2909. //
  2910. // If not expandable:
  2911. // The default menu of the underlying context menu is
  2912. // used as the default; or use the first item if nobody
  2913. // picked a default.
  2914. //
  2915. // We replace the existing "Open" command with our own.
  2916. //
  2917. HMENU CNscTree::_CreateContextMenu(IContextMenu *pcm, HTREEITEM hti)
  2918. {
  2919. BOOL fExpandable = _IsExpandable(hti);
  2920. HMENU hmenu = CreatePopupMenu();
  2921. if (hmenu)
  2922. {
  2923. pcm->QueryContextMenu(hmenu, 0, RSVIDM_CONTEXT_START, 0x7fff, CMF_EXPLORE | CMF_CANRENAME);
  2924. // Always delete "Create shortcut" from the context menu.
  2925. ContextMenu_DeleteCommandByName(pcm, hmenu, RSVIDM_CONTEXT_START, L"link");
  2926. // Sometimes we need to delete "Open":
  2927. //
  2928. // History mode always. The context menu for history mode folders
  2929. // has "Open" but it doesn't work, so we need to replace it with
  2930. // Expand/Collapse. And the context menu for history mode items
  2931. // has "Open" but it opens in a new window. We want to navigate.
  2932. //
  2933. // Favorites mode, expandable: Leave "Open" alone -- it will open
  2934. // the expandable thing in a new window.
  2935. //
  2936. // Favorites mode, non-expandable: Delete the original "Open" and
  2937. // replace it with ours that does a navigate.
  2938. //
  2939. BOOL fReplaceOpen = (_mode & MODE_HISTORY) || (!fExpandable && (_mode & MODE_FAVORITES));
  2940. if (fReplaceOpen)
  2941. ContextMenu_DeleteCommandByName(pcm, hmenu, RSVIDM_CONTEXT_START, L"open");
  2942. // Load the NSC part of the context menu and party on it separately.
  2943. // By doing this, we save the trouble of having to do a SHPrettyMenu
  2944. // after we dork it -- Shell_MergeMenus does all the prettying
  2945. // automatically. NOTE: this is totally bogus reasoning - cleaner code the other way around...
  2946. HMENU hmenuctx = LoadMenuPopup_PrivateNoMungeW(POPUP_CONTEXT_NSC);
  2947. if (hmenuctx)
  2948. {
  2949. // create new folder doesn't make sense outside of favorites
  2950. // (actually, it does, but there's no interface to it)
  2951. if (!(_mode & MODE_FAVORITES))
  2952. DeleteMenu(hmenuctx, RSVIDM_NEWFOLDER, MF_BYCOMMAND);
  2953. // Of "Expand", "Collapse", or "Open", we will keep at most one of
  2954. // them. idmKeep is the one we choose to keep.
  2955. //
  2956. UINT idmKeep;
  2957. if (fExpandable)
  2958. {
  2959. // Even if the item has no children, we still show Expand.
  2960. // The reason is that an item that has never been expanded
  2961. // is marked as "children: unknown" so we show an Expand
  2962. // and then the user picks it and nothing expands. And then
  2963. // the user clicks it again and the Expand option is gone!
  2964. // (Because the second time, we know that the item isn't
  2965. // expandable.)
  2966. //
  2967. // Better to be consistently wrong than randomly wrong.
  2968. //
  2969. if (_IsItemExpanded(hti))
  2970. idmKeep = RSVIDM_COLLAPSE;
  2971. else
  2972. idmKeep = RSVIDM_EXPAND;
  2973. }
  2974. else if (!(_mode & MODE_CONTROL))
  2975. {
  2976. idmKeep = RSVIDM_OPEN;
  2977. }
  2978. else
  2979. {
  2980. idmKeep = 0;
  2981. }
  2982. // Now go decide which of RSVIDM_COLLAPSE, RSVIDM_EXPAND, or
  2983. // RSVIDM_OPEN we want to keep.
  2984. //
  2985. if (idmKeep != RSVIDM_EXPAND)
  2986. DeleteMenu(hmenuctx, RSVIDM_EXPAND, MF_BYCOMMAND);
  2987. if (idmKeep != RSVIDM_COLLAPSE)
  2988. DeleteMenu(hmenuctx, RSVIDM_COLLAPSE, MF_BYCOMMAND);
  2989. if (idmKeep != RSVIDM_OPEN)
  2990. DeleteMenu(hmenuctx, RSVIDM_OPEN, MF_BYCOMMAND);
  2991. // in normal mode we want to gray out expand if folder cannot be expanded
  2992. if (idmKeep == RSVIDM_EXPAND && _mode == MODE_NORMAL)
  2993. {
  2994. TV_ITEM tvi;
  2995. tvi.mask = TVIF_CHILDREN;
  2996. tvi.hItem = hti;
  2997. if (TreeView_GetItem(_hwndTree, &tvi) && !tvi.cChildren)
  2998. {
  2999. EnableMenuItem(hmenuctx, RSVIDM_EXPAND, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
  3000. }
  3001. }
  3002. Shell_MergeMenus(hmenu, hmenuctx, 0, 0, 0xFFFF, fReplaceOpen ? 0 : MM_ADDSEPARATOR);
  3003. DestroyMenu(hmenuctx);
  3004. if (idmKeep)
  3005. SetMenuDefaultItem(hmenu, idmKeep, MF_BYCOMMAND);
  3006. }
  3007. // Menu item "Open in New Window" needs to be disabled if the restriction is set
  3008. if( SHRestricted2W(REST_NoOpeninNewWnd, NULL, 0))
  3009. {
  3010. EnableMenuItem(hmenu, RSVIDM_CONTEXT_START + RSVIDM_OPEN_NEWWINDOW,
  3011. MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
  3012. }
  3013. _SHPrettyMenu(hmenu);
  3014. }
  3015. return hmenu;
  3016. }
  3017. LRESULT CNscTree::_OnContextMenu(short x, short y)
  3018. {
  3019. HTREEITEM hti;
  3020. POINT ptPopup; // in screen coordinate
  3021. //assert that the SetFocus() below won't be ripping focus away from anyone
  3022. ASSERT((_mode & MODE_CONTROL) ? (GetFocus() == _hwndTree) : TRUE);
  3023. if (x == -1 && y == -1)
  3024. {
  3025. // Keyboard-driven: Get the popup position from the selected item.
  3026. hti = TreeView_GetSelection(_hwndTree);
  3027. if (hti)
  3028. {
  3029. RECT rc;
  3030. //
  3031. // Note that TV_GetItemRect returns it in client coordinate!
  3032. //
  3033. TreeView_GetItemRect(_hwndTree, hti, &rc, TRUE);
  3034. //cannot point to middle of item rect because if item name cannot fit into control rect
  3035. //treeview puts tooltip on top and rect returned above is from tooltip whose middle
  3036. //may not be in Treeview which causes problems later in the function
  3037. ptPopup.x = rc.left + 1;
  3038. ptPopup.y = (rc.top + rc.bottom) / 2;
  3039. ::MapWindowPoints(_hwndTree, HWND_DESKTOP, &ptPopup, 1);
  3040. }
  3041. //so we can go into rename mode
  3042. _fOkToRename = TRUE;
  3043. }
  3044. else
  3045. {
  3046. TV_HITTESTINFO tvht;
  3047. // Mouse-driven: Pick the treeitem from the position.
  3048. ptPopup.x = x;
  3049. ptPopup.y = y;
  3050. tvht.pt = ptPopup;
  3051. ::ScreenToClient(_hwndTree, &tvht.pt);
  3052. hti = TreeView_HitTest(_hwndTree, &tvht);
  3053. }
  3054. if (hti)
  3055. {
  3056. LPCITEMIDLIST pidl = _CacheParentShellFolder(hti, NULL);
  3057. if (pidl)
  3058. {
  3059. IContextMenu *pcm;
  3060. TreeView_SelectDropTarget(_hwndTree, hti);
  3061. if (SUCCEEDED(_psfCache->GetUIObjectOf(_hwndTree, 1, &pidl, IID_PPV_ARG_NULL(IContextMenu, &pcm))))
  3062. {
  3063. pcm->QueryInterface(IID_PPV_ARG(IContextMenu2, &_pcmSendTo));
  3064. HMENU hmenu = _CreateContextMenu(pcm, hti);
  3065. if (hmenu)
  3066. {
  3067. UINT idCmd;
  3068. _pcm = pcm; // for IContextMenu2 code
  3069. // use _hwnd so menu msgs go there and I can forward them
  3070. // using IContextMenu2 so "Sent To" works
  3071. // review chrisny: useTrackPopupMenuEx for clipping etc.
  3072. idCmd = TrackPopupMenu(hmenu,
  3073. TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
  3074. ptPopup.x, ptPopup.y, 0, _hwndTree, NULL);
  3075. // Note: must requery selected item to verify that the hti is good. This
  3076. // solves the problem where the hti was deleted, hence pointed to something
  3077. // bogus, then we write to it causing heap corruption, while the menu was up.
  3078. TV_HITTESTINFO tvht;
  3079. tvht.pt = ptPopup;
  3080. ::ScreenToClient(_hwndTree, &tvht.pt);
  3081. hti = TreeView_HitTest(_hwndTree, &tvht);
  3082. if (hti && idCmd)
  3083. {
  3084. switch (idCmd)
  3085. {
  3086. case RSVIDM_OPEN:
  3087. case RSVIDM_EXPAND:
  3088. case RSVIDM_COLLAPSE:
  3089. TreeView_SelectItem(_hwndTree, hti);
  3090. // turn off flag, so select will have an effect.
  3091. _fOkToRename = FALSE;
  3092. _OnSelChange(FALSE); // selection has changed, force the navigation.
  3093. // SelectItem may not expand (if was closed and selected)
  3094. TreeView_Expand(_hwndTree, hti, idCmd == RSVIDM_COLLAPSE ? TVE_COLLAPSE : TVE_EXPAND);
  3095. break;
  3096. // This WAS unix only, now win32 does it too
  3097. // IEUNIX : We allow new folder creation from context menu. since
  3098. // this control was used to organize favorites in IEUNIX4.0
  3099. case RSVIDM_NEWFOLDER:
  3100. CreateNewFolder(hti);
  3101. break;
  3102. default:
  3103. _ApplyCmd(hti, pcm, idCmd-RSVIDM_CONTEXT_START);
  3104. break;
  3105. }
  3106. //we must have had focus before (asserted above), but we might have lost it after a delete.
  3107. //get it back.
  3108. //this is only a problem in the nsc oc.
  3109. if ((_mode & MODE_CONTROL) && !_fInLabelEdit)
  3110. ::SetFocus(_hwndTree);
  3111. }
  3112. ATOMICRELEASE(_pcmSendTo);
  3113. DestroyMenu(hmenu);
  3114. _pcm = NULL;
  3115. }
  3116. pcm->Release();
  3117. }
  3118. TreeView_SelectDropTarget(_hwndTree, NULL);
  3119. }
  3120. }
  3121. if (x == -1 && y == -1)
  3122. _fOkToRename = FALSE;
  3123. return S_FALSE; // So WM_CONTEXTMENU message will not come.
  3124. }
  3125. HRESULT CNscTree::_QuerySelection(IContextMenu **ppcm, HTREEITEM *phti)
  3126. {
  3127. HRESULT hr = E_FAIL;
  3128. HTREEITEM hti = TreeView_GetSelection(_hwndTree);
  3129. if (hti)
  3130. {
  3131. LPCITEMIDLIST pidl = _CacheParentShellFolder(hti, NULL);
  3132. if (pidl)
  3133. {
  3134. if (ppcm)
  3135. {
  3136. hr = _psfCache->GetUIObjectOf(_hwndTree, 1, &pidl,
  3137. IID_PPV_ARG_NULL(IContextMenu, ppcm));
  3138. }
  3139. else
  3140. {
  3141. hr = S_OK;
  3142. }
  3143. }
  3144. }
  3145. if (phti)
  3146. *phti = hti;
  3147. return hr;
  3148. }
  3149. LRESULT NSCEditBoxSubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
  3150. UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
  3151. {
  3152. if (uIdSubclass == ID_NSC_SUBCLASS && uMsg == WM_GETDLGCODE)
  3153. {
  3154. return DLGC_WANTMESSAGE;
  3155. }
  3156. return DefSubclassProc(hwnd, uMsg, wParam, lParam);
  3157. }
  3158. LRESULT CNscTree::_OnBeginLabelEdit(TV_DISPINFO *ptvdi)
  3159. {
  3160. BOOL fCantRename = TRUE;
  3161. LPCITEMIDLIST pidl = _CacheParentShellFolder(ptvdi->item.hItem, NULL);
  3162. if (pidl)
  3163. {
  3164. if (SHGetAttributes(_psfCache, pidl, SFGAO_CANRENAME))
  3165. fCantRename = FALSE;
  3166. }
  3167. HWND hwndEdit = (HWND)::SendMessage(_hwndTree, TVM_GETEDITCONTROL, 0, 0);
  3168. if (hwndEdit)
  3169. {
  3170. WCHAR szName[MAX_PATH];
  3171. if (SUCCEEDED(DisplayNameOf(_psfCache, pidl, SHGDN_INFOLDER|SHGDN_FOREDITING, szName, ARRAYSIZE(szName))))
  3172. {
  3173. SHLimitInputEdit(hwndEdit, _psfCache);
  3174. ::SetWindowText(hwndEdit, szName);
  3175. }
  3176. SetWindowSubclass(hwndEdit, NSCEditBoxSubclassWndProc, ID_NSC_SUBCLASS, NULL);
  3177. }
  3178. _fInLabelEdit = !fCantRename;
  3179. if (_fInLabelEdit)
  3180. _htiRenaming = ptvdi->item.hItem;
  3181. return fCantRename;
  3182. }
  3183. //
  3184. // Utility function for CNSCTree::_OnEndLabelEdit
  3185. // Does not set the new value in the tree view if the old
  3186. // value is the same.
  3187. //
  3188. BOOL CNscTree::_LabelEditIsNewValueValid(TV_DISPINFO *ptvdi)
  3189. {
  3190. ASSERT(ptvdi && ptvdi->item.hItem);
  3191. TCHAR szOldValue[MAX_PATH];
  3192. szOldValue[0] = '\0';
  3193. TV_ITEM tvi;
  3194. tvi.mask = TVIF_TEXT;
  3195. tvi.hItem = (HTREEITEM)ptvdi->item.hItem;
  3196. tvi.pszText = szOldValue;
  3197. tvi.cchTextMax = ARRAYSIZE(szOldValue);
  3198. TreeView_GetItem(_hwndTree, &tvi);
  3199. //
  3200. // is the old value in the control unequal to the new one?
  3201. //
  3202. return (0 != StrCmp(tvi.pszText, ptvdi->item.pszText));
  3203. }
  3204. LRESULT CNscTree::_OnEndLabelEdit(TV_DISPINFO *ptvdi)
  3205. {
  3206. HWND hwndEdit = (HWND)::SendMessage(_hwndTree, TVM_GETEDITCONTROL, 0, 0);
  3207. if (hwndEdit)
  3208. {
  3209. RemoveWindowSubclass(hwndEdit, NSCEditBoxSubclassWndProc, ID_NSC_SUBCLASS);
  3210. }
  3211. #ifdef UNIX
  3212. // IEUNIX (APPCOMPAT): If we lose activation in the middle of rename operation
  3213. // and we have invalid name in the edit box, rename operation will popup
  3214. // a message box which causes IE on unix to go into infinite focus changing
  3215. // loop. To workaround this problem, we are considering the operation as
  3216. // cancelled and we copy the original value into the buffer.
  3217. BOOL fHasActivation = FALSE;
  3218. if (GetActiveWindow() && IsChild(GetActiveWindow(), _hwndTree))
  3219. fHasActivation = TRUE;
  3220. if (!fHasActivation)
  3221. {
  3222. TV_ITEM tvi;
  3223. tvi.mask = TVIF_TEXT;
  3224. tvi.hItem = (HTREEITEM)ptvdi->item.hItem;
  3225. tvi.pszText = ptvdi->item.pszText;
  3226. tvi.cchTextMax = ptvdi->item.cchTextMax;
  3227. TreeView_GetItem(_hwndTree, &tvi);
  3228. }
  3229. #endif
  3230. if ((ptvdi->item.pszText != NULL) && _LabelEditIsNewValueValid(ptvdi))
  3231. {
  3232. ASSERT(ptvdi->item.hItem);
  3233. LPCITEMIDLIST pidl = _CacheParentShellFolder(ptvdi->item.hItem, NULL);
  3234. if (pidl)
  3235. {
  3236. WCHAR wszName[MAX_PATH - 5]; //-5 to work around nt4 shell32 bug
  3237. SHTCharToUnicode(ptvdi->item.pszText, wszName, ARRAYSIZE(wszName));
  3238. if (SUCCEEDED(_psfCache->SetNameOf(_hwndTree, pidl, wszName, 0, NULL)))
  3239. {
  3240. // NOTES: pidl is no longer valid here.
  3241. // Set the handle to NULL in the notification to let
  3242. // the system know that the pointer is probably not
  3243. // valid anymore.
  3244. ptvdi->item.hItem = NULL;
  3245. _FlushNotifyMessages(_hwndTree); // do this last, else we get bad results
  3246. _fInLabelEdit = FALSE;
  3247. #ifdef UNIX
  3248. SHChangeNotifyHandleEvents();
  3249. #endif
  3250. }
  3251. else
  3252. {
  3253. // not leaving label edit mode here, so do not set _fInLabelEdit to FALSE or we
  3254. // will not get ::TranslateAcceleratorIO() and backspace, etc, will not work.
  3255. _fOkToRename = TRUE; //otherwise label editing is canceled
  3256. ::SendMessage(_hwndTree, TVM_EDITLABEL, (WPARAM)ptvdi->item.pszText, (LPARAM)ptvdi->item.hItem);
  3257. _fOkToRename = FALSE;
  3258. }
  3259. }
  3260. }
  3261. else
  3262. _fInLabelEdit = FALSE;
  3263. if (!_fInLabelEdit)
  3264. _htiRenaming = NULL;
  3265. //else user cancelled, nothing to do here.
  3266. return 0; // We always return 0, "we handled it".
  3267. }
  3268. BOOL _DidDropOnRecycleBin(IDataObject *pdtobj)
  3269. {
  3270. CLSID clsid;
  3271. return SUCCEEDED(DataObj_GetBlob(pdtobj, g_cfTargetCLSID, &clsid, sizeof(clsid))) &&
  3272. IsEqualCLSID(clsid, CLSID_RecycleBin);
  3273. }
  3274. void CNscTree::_OnBeginDrag(NM_TREEVIEW *pnmhdr)
  3275. {
  3276. LPCITEMIDLIST pidl = _CacheParentShellFolder(pnmhdr->itemNew.hItem, NULL);
  3277. _htiDragging = pnmhdr->itemNew.hItem; // item we are dragging.
  3278. if (pidl)
  3279. {
  3280. if (_pidlDrag)
  3281. {
  3282. ILFree(_pidlDrag);
  3283. _pidlDrag = NULL;
  3284. }
  3285. DWORD dwEffect = SHGetAttributes(_psfCache, pidl, DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK);
  3286. if (dwEffect)
  3287. {
  3288. IDataObject *pdtobj;
  3289. HRESULT hr = _psfCache->GetUIObjectOf(_hwndTree, 1, &pidl, IID_PPV_ARG_NULL(IDataObject, &pdtobj));
  3290. if (SUCCEEDED(hr))
  3291. {
  3292. HWND hwndTT;
  3293. _fDragging = TRUE;
  3294. if (hwndTT = TreeView_GetToolTips(_hwndTree))
  3295. ::SendMessage(hwndTT, TTM_POP, (WPARAM) 0, (LPARAM) 0);
  3296. PORDERITEM poi = _GetTreeOrderItem(pnmhdr->itemNew.hItem);
  3297. if (poi)
  3298. {
  3299. _iDragSrc = poi->nOrder;
  3300. TraceMsg(TF_NSC, "NSCBand: Starting Drag");
  3301. _pidlDrag = ILClone(poi->pidl);
  3302. _htiFolderStart = TreeView_GetParent(_hwndTree, pnmhdr->itemNew.hItem);
  3303. if (_htiFolderStart == NULL)
  3304. _htiFolderStart = TVI_ROOT;
  3305. }
  3306. else
  3307. {
  3308. _iDragSrc = -1;
  3309. _pidlDrag = NULL;
  3310. _htiFolderStart = NULL;
  3311. }
  3312. //
  3313. // Don't allow drag and drop of channels if
  3314. // REST_NoRemovingChannels is set.
  3315. //
  3316. if (!SHRestricted2(REST_NoRemovingChannels, NULL, 0) ||
  3317. !_IsChannelFolder(_htiDragging))
  3318. {
  3319. HIMAGELIST himlDrag;
  3320. SHLoadOLE(SHELLNOTIFY_OLELOADED); // Browser Only - our shell32 doesn't know ole has been loaded
  3321. _fStartingDrag = TRUE;
  3322. IDragSourceHelper* pdsh = NULL;
  3323. if (SUCCEEDED(CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER,
  3324. IID_PPV_ARG(IDragSourceHelper, &pdsh))))
  3325. {
  3326. pdsh->InitializeFromWindow(_hwndTree, &pnmhdr->ptDrag, pdtobj);
  3327. _fStartingDrag = FALSE;
  3328. }
  3329. else
  3330. {
  3331. himlDrag = TreeView_CreateDragImage(_hwndTree, pnmhdr->itemNew.hItem);
  3332. _fStartingDrag = FALSE;
  3333. if (himlDrag)
  3334. {
  3335. DAD_SetDragImage(himlDrag, NULL);
  3336. }
  3337. }
  3338. hr = SHDoDragDrop(_hwndTree, pdtobj, NULL, dwEffect, &dwEffect);
  3339. // the below follows the logic in defview for non-filesystem deletes.
  3340. InitClipboardFormats();
  3341. if ((DRAGDROP_S_DROP == hr) &&
  3342. (DROPEFFECT_MOVE == dwEffect) &&
  3343. (DROPEFFECT_MOVE == DataObj_GetDWORD(pdtobj, g_cfPerformedEffect, DROPEFFECT_NONE)))
  3344. {
  3345. // enable UI for the recycle bin case (the data will be lost
  3346. // as the recycle bin really can't recycle stuff that is not files)
  3347. UINT uFlags = _DidDropOnRecycleBin(pdtobj) ? 0 : CMIC_MASK_FLAG_NO_UI;
  3348. SHInvokeCommandOnDataObject(_hwndTree, NULL, pdtobj, uFlags, "delete");
  3349. }
  3350. else if (dwEffect == DROPEFFECT_NONE)
  3351. {
  3352. // nothing happened when the d&d terminated, so clean up you fool.
  3353. ILFree(_pidlDrag);
  3354. _pidlDrag = NULL;
  3355. }
  3356. if (pdsh)
  3357. {
  3358. pdsh->Release();
  3359. }
  3360. else
  3361. {
  3362. DAD_SetDragImage((HIMAGELIST)-1, NULL);
  3363. ImageList_Destroy(himlDrag);
  3364. }
  3365. }
  3366. _iDragSrc = -1;
  3367. pdtobj->Release();
  3368. }
  3369. }
  3370. }
  3371. _htiDragging = NULL;
  3372. }
  3373. BOOL IsExpandableChannelFolder(IShellFolder *psf, LPCITEMIDLIST pidl)
  3374. {
  3375. if (WhichPlatform() == PLATFORM_INTEGRATED)
  3376. return SHIsExpandableFolder(psf, pidl);
  3377. ASSERT(pidl);
  3378. ASSERT(psf);
  3379. BOOL fExpand = FALSE;
  3380. IShellFolder* psfChannelFolder;
  3381. if (pidl && psf && SUCCEEDED(SHBindToObject(psf, IID_X_PPV_ARG(IShellFolder, pidl, &psfChannelFolder))))
  3382. {
  3383. IEnumIDList *penum;
  3384. if (S_OK == psfChannelFolder->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &penum))
  3385. {
  3386. ULONG celt;
  3387. LPITEMIDLIST pidlTemp;
  3388. if (penum->Next(1, &pidlTemp, &celt) == S_OK && celt == 1)
  3389. {
  3390. ILFree(pidlTemp);
  3391. fExpand = FALSE;
  3392. }
  3393. if (penum->Next(1, &pidlTemp, &celt) == S_OK && celt == 1)
  3394. {
  3395. ILFree(pidlTemp);
  3396. fExpand = TRUE;
  3397. }
  3398. penum->Release();
  3399. }
  3400. psfChannelFolder->Release();
  3401. }
  3402. return fExpand;
  3403. }
  3404. BOOL CNscTree::_OnSelChange(BOOL fMark)
  3405. {
  3406. BOOL fExpand = TRUE;
  3407. HTREEITEM hti = TreeView_GetSelection(_hwndTree);
  3408. BOOL fMultiSelect = _dwFlags & NSS_MULTISELECT;
  3409. //if we're in control mode (where pnscProxy always null), never navigate
  3410. if (hti)
  3411. {
  3412. LPCITEMIDLIST pidlItem = _CacheParentShellFolder(hti, NULL);
  3413. if (pidlItem && !fMultiSelect)
  3414. {
  3415. if (_pnscProxy && !_fInSelectPidl)
  3416. {
  3417. ULONG ulAttrs = SFGAO_FOLDER | SFGAO_NEWCONTENT;
  3418. LPITEMIDLIST pidlTarget;
  3419. LPITEMIDLIST pidlFull = _GetFullIDList(hti);
  3420. HRESULT hr = _pnscProxy->GetNavigateTarget(pidlFull, &pidlTarget, &ulAttrs);
  3421. if (SUCCEEDED(hr))
  3422. {
  3423. if (hr == S_OK)
  3424. {
  3425. _pnscProxy->Invoke(pidlTarget);
  3426. ILFree(pidlTarget);
  3427. }
  3428. // review chrisny: still need to handle notify of changes from
  3429. // other navs.
  3430. if (ulAttrs & SFGAO_NEWCONTENT)
  3431. {
  3432. TV_ITEM tvi;
  3433. tvi.hItem = hti;
  3434. tvi.mask = TVIF_STATE | TVIF_HANDLE;
  3435. tvi.stateMask = TVIS_FOCUSED; // the BOLD bit is to be
  3436. tvi.state = 0; // cleared
  3437. TreeView_SetItem(_hwndTree, &tvi);
  3438. }
  3439. }
  3440. else
  3441. {
  3442. if (!(SHGetAttributes(_psfCache, pidlItem, SFGAO_FOLDER)))
  3443. SHInvokeDefaultCommand(_hwndTree, _psfCache, pidlItem);
  3444. }
  3445. ILFree(pidlFull);
  3446. fExpand = hr != S_OK && (ulAttrs & SFGAO_FOLDER);
  3447. }
  3448. }
  3449. }
  3450. if (fMultiSelect)
  3451. {
  3452. if (fMark)
  3453. {
  3454. UINT uState = TreeView_GetItemState(_hwndTree, hti, NSC_TVIS_MARKED) & NSC_TVIS_MARKED;
  3455. uState ^= NSC_TVIS_MARKED;
  3456. _MarkChildren(hti, uState == NSC_TVIS_MARKED);
  3457. _htiActiveBorder = NULL;
  3458. }
  3459. }
  3460. else if (!_fSingleExpand && fExpand && (_mode != MODE_NORMAL))
  3461. {
  3462. TreeView_Expand(_hwndTree, hti, TVE_TOGGLE);
  3463. }
  3464. if (!fMultiSelect)
  3465. _UpdateActiveBorder(hti);
  3466. return TRUE;
  3467. }
  3468. void CNscTree::_OnSetSelection()
  3469. {
  3470. HTREEITEM hti = TreeView_GetSelection(_hwndTree);
  3471. LPITEMIDLIST pidlItem = _GetFullIDList(hti);
  3472. if (_pnscProxy && !_fInSelectPidl)
  3473. {
  3474. _pnscProxy->OnSelectionChanged(pidlItem);
  3475. }
  3476. ILFree(pidlItem);
  3477. }
  3478. void CNscTree::_OnGetInfoTip(NMTVGETINFOTIP* pnm)
  3479. {
  3480. // No info tip operation on drag/drop
  3481. if (_fDragging || _fDropping || _fClosing || _fHandlingShellNotification || _fInSelectPidl)
  3482. return;
  3483. PORDERITEM poi = GetPoi(pnm->lParam);
  3484. if (poi)
  3485. {
  3486. LPITEMIDLIST pidl = _CacheParentShellFolder(pnm->hItem, poi->pidl);
  3487. if (pidl)
  3488. {
  3489. // Use the imported Browseui function because the one in shell\lib does
  3490. // not work on browser-only platforms
  3491. GetInfoTip(_psfCache, pidl, pnm->pszText, pnm->cchTextMax);
  3492. }
  3493. }
  3494. }
  3495. LRESULT CNscTree::_OnSetCursor(NMMOUSE* pnm)
  3496. {
  3497. if (_mode == MODE_NORMAL && _fShouldShowAppStartCursor)
  3498. {
  3499. SetCursor(LoadCursor(NULL, IDC_APPSTARTING));
  3500. return 1;
  3501. }
  3502. if (!pnm->dwItemData)
  3503. {
  3504. SetCursor(LoadCursor(NULL, IDC_ARROW));
  3505. return 1;
  3506. }
  3507. if (!(_mode & MODE_CONTROL) && (_mode != MODE_NORMAL))
  3508. {
  3509. ITEMINFO* pii = GetPii(pnm->dwItemData);
  3510. if (pii)
  3511. {
  3512. if (!pii->fNavigable)
  3513. {
  3514. //folders always get the arrow
  3515. SetCursor(LoadCursor(NULL, IDC_ARROW));
  3516. }
  3517. else
  3518. {
  3519. //favorites always get some form of the hand
  3520. HCURSOR hCursor = pii->fGreyed ? (HCURSOR)LoadCursor(HINST_THISDLL, MAKEINTRESOURCE(IDC_OFFLINE_HAND)) :
  3521. LoadHandCursor(0);
  3522. if (hCursor)
  3523. SetCursor(hCursor);
  3524. }
  3525. }
  3526. }
  3527. else
  3528. {
  3529. //always show the arrow in org favs
  3530. SetCursor(LoadCursor(NULL, IDC_ARROW));
  3531. }
  3532. return 1; // 1 if We handled it, 0 otherwise
  3533. }
  3534. BOOL CNscTree::_IsTopParentItem(HTREEITEM hti)
  3535. {
  3536. return (hti && (!TreeView_GetParent(_hwndTree, hti)));
  3537. }
  3538. LRESULT CNscTree::_OnNotify(LPNMHDR pnm)
  3539. {
  3540. LRESULT lres = 0;
  3541. switch (pnm->idFrom)
  3542. {
  3543. case ID_CONTROL:
  3544. {
  3545. switch (pnm->code)
  3546. {
  3547. case NM_CUSTOMDRAW:
  3548. return _OnCDNotify((LPNMCUSTOMDRAW)pnm);
  3549. case TVN_GETINFOTIP:
  3550. // no info tips on drag/drop ops
  3551. // According to Bug#241601, Tooltips display too quickly. The problem is
  3552. // the original designer of the InfoTips in the Treeview merged the "InfoTip" tooltip and
  3553. // the "I'm too small to display correctly" tooltips. This is really unfortunate because you
  3554. // cannot control the display of these tooltips independantly. Therefore we are turning off
  3555. // infotips in normal mode.
  3556. if (!_fInLabelEdit && _mode != MODE_NORMAL)
  3557. _OnGetInfoTip((NMTVGETINFOTIP*)pnm);
  3558. else
  3559. return FALSE;
  3560. break;
  3561. case NM_SETCURSOR:
  3562. lres = _OnSetCursor((NMMOUSE*)pnm);
  3563. break;
  3564. case NM_SETFOCUS:
  3565. case NM_KILLFOCUS:
  3566. if (pnm->code == NM_KILLFOCUS)
  3567. {
  3568. _fHasFocus = FALSE;
  3569. //invalidate the item because tabbing away doesn't
  3570. RECT rc;
  3571. // Tree can focus and not have any items.
  3572. HTREEITEM hti = TreeView_GetSelection(_hwndTree);
  3573. if (hti)
  3574. {
  3575. TreeView_GetItemRect(_hwndTree, hti, &rc, FALSE);
  3576. //does this need to be UpdateWindow? only if focus rect gets left behind.
  3577. ::InvalidateRect(_hwndTree, &rc, FALSE);
  3578. }
  3579. }
  3580. else
  3581. {
  3582. _fHasFocus = TRUE;
  3583. }
  3584. // do this for both set and kill focus...
  3585. if (_dwFlags & NSS_MULTISELECT)
  3586. {
  3587. HTREEITEM hti = TreeView_GetNextItem(_hwndTree, NULL, TVGN_FIRSTVISIBLE);
  3588. while (hti)
  3589. {
  3590. UINT uState = TreeView_GetItemState(_hwndTree, hti, NSC_TVIS_MARKED);
  3591. if (uState & NSC_TVIS_MARKED)
  3592. {
  3593. RECT rc;
  3594. TreeView_GetItemRect(_hwndTree, hti, &rc, FALSE);
  3595. //does this need to be UpdateWindow? only if focus rect gets left behind.
  3596. ::InvalidateRect(_hwndTree, &rc, FALSE);
  3597. }
  3598. hti = TreeView_GetNextItem(_hwndTree, hti, TVGN_NEXTVISIBLE);
  3599. }
  3600. }
  3601. break;
  3602. case TVN_KEYDOWN:
  3603. {
  3604. TV_KEYDOWN *ptvkd = (TV_KEYDOWN *) pnm;
  3605. switch (ptvkd->wVKey)
  3606. {
  3607. case VK_RETURN:
  3608. case VK_SPACE:
  3609. _OnSelChange(TRUE);
  3610. lres = TRUE;
  3611. break;
  3612. case VK_DELETE:
  3613. if (!((_mode & MODE_HISTORY) && IsInetcplRestricted(L"History")))
  3614. {
  3615. // in explorer band we never come here
  3616. // and in browse for folder we cannot ignore the selection
  3617. // because we will end up with nothing selected
  3618. if (_mode != MODE_NORMAL)
  3619. _fIgnoreNextSelChange = TRUE;
  3620. InvokeContextMenuCommand(L"delete");
  3621. }
  3622. break;
  3623. case VK_UP:
  3624. case VK_DOWN:
  3625. //VK_MENU == VK_ALT
  3626. if ((_mode != MODE_HISTORY) && (_mode != MODE_NORMAL) && (GetKeyState(VK_MENU) < 0))
  3627. {
  3628. MoveItemUpOrDown(ptvkd->wVKey == VK_UP);
  3629. lres = 0;
  3630. _fIgnoreNextSelChange = TRUE;
  3631. }
  3632. break;
  3633. case VK_F2:
  3634. //only do this in org favs, because the band accel handler usually processes this
  3635. //SHBrowseForFolder doesn't have band to process it so do it in normal mode as well
  3636. if ((_mode & MODE_CONTROL) || _mode == MODE_NORMAL)
  3637. InvokeContextMenuCommand(L"rename");
  3638. break;
  3639. default:
  3640. break;
  3641. }
  3642. if (!_fSingleExpand && !(_dwFlags & NSS_MULTISELECT))
  3643. _UpdateActiveBorder(TreeView_GetSelection(_hwndTree));
  3644. }
  3645. break;
  3646. case TVN_SELCHANGINGA:
  3647. case TVN_SELCHANGING:
  3648. {
  3649. //hack because treeview keydown ALWAYS does it's default processing
  3650. if (_fIgnoreNextSelChange)
  3651. {
  3652. _fIgnoreNextSelChange = FALSE;
  3653. return TRUE;
  3654. }
  3655. NM_TREEVIEW * pnmtv = (NM_TREEVIEW *) pnm;
  3656. //if it's coming from somewhere weird (like a WM_SETFOCUS), don't let it select
  3657. return (pnmtv->action != TVC_BYKEYBOARD) && (pnmtv->action != TVC_BYMOUSE) && (pnmtv->action != TVC_UNKNOWN);
  3658. }
  3659. break;
  3660. case TVN_SELCHANGEDA:
  3661. case TVN_SELCHANGED:
  3662. if (_fSelectFromMouseClick)
  3663. {
  3664. _OnSetSelection();
  3665. }
  3666. else
  3667. {
  3668. ::KillTimer(_hwndTree, IDT_SELECTION);
  3669. ::SetTimer(_hwndTree, IDT_SELECTION, GetDoubleClickTime(), NULL);
  3670. }
  3671. #ifdef DEBUG
  3672. {
  3673. HTREEITEM hti = TreeView_GetSelection(_hwndTree);
  3674. LPITEMIDLIST pidl = _GetFullIDList(hti);
  3675. if (pidl)
  3676. {
  3677. TCHAR sz[MAX_PATH];
  3678. SHGetNameAndFlags(pidl, SHGDN_NORMAL, sz, SIZECHARS(sz), NULL);
  3679. TraceMsg(TF_NSC, "NSCBand: Selecting %s", sz);
  3680. //
  3681. // On NT4 and W95 shell this call will miss the history
  3682. // shell extension junction point. It will then deref into
  3683. // history pidls as if they were shell pidls. On short
  3684. // history pidls this would fault on a debug version of the OS.
  3685. //
  3686. // SHGetNameAndFlags(pidl, SHGDN_FORPARSING | SHGDN_FORADDRESSBAR, sz, SIZECHARS(sz), NULL);
  3687. //TraceMsg(TF_NSC, "displayname = %s", sz);
  3688. ILFree(pidl);
  3689. }
  3690. }
  3691. #endif // DEBUG
  3692. break;
  3693. case TVN_GETDISPINFO:
  3694. _OnGetDisplayInfo((TV_DISPINFO *)pnm);
  3695. break;
  3696. case TVN_ITEMEXPANDING:
  3697. TraceMsg(TF_NSC, "NSCBand: Expanding");
  3698. if (!_fIgnoreNextItemExpanding)
  3699. {
  3700. lres = _OnItemExpandingMsg((LPNM_TREEVIEW)pnm);
  3701. }
  3702. else if (!_fInExpand) // pretend we processed it if we are expanding to avoid recursion
  3703. {
  3704. lres = TRUE;
  3705. }
  3706. break;
  3707. case TVN_DELETEITEM:
  3708. _OnDeleteItem((LPNM_TREEVIEW)pnm);
  3709. break;
  3710. case TVN_BEGINDRAG:
  3711. case TVN_BEGINRDRAG:
  3712. _OnBeginDrag((NM_TREEVIEW *)pnm);
  3713. break;
  3714. case TVN_BEGINLABELEDIT:
  3715. //this is to prevent slow double-click rename in favorites and history
  3716. if (_mode != MODE_NORMAL && !_fOkToRename)
  3717. return 1;
  3718. lres = _OnBeginLabelEdit((TV_DISPINFO *)pnm);
  3719. if (_punkSite)
  3720. IUnknown_UIActivateIO(_punkSite, TRUE, NULL);
  3721. break;
  3722. case TVN_ENDLABELEDIT:
  3723. lres = _OnEndLabelEdit((TV_DISPINFO *)pnm);
  3724. break;
  3725. case TVN_SINGLEEXPAND:
  3726. case NM_DBLCLK:
  3727. break;
  3728. case NM_CLICK:
  3729. {
  3730. //if someone clicks on the selected item, force a selection change (to force a navigate)
  3731. DWORD dwPos = GetMessagePos();
  3732. TV_HITTESTINFO tvht;
  3733. HTREEITEM hti;
  3734. tvht.pt.x = GET_X_LPARAM(dwPos);
  3735. tvht.pt.y = GET_Y_LPARAM(dwPos);
  3736. ::ScreenToClient(_hwndTree, &tvht.pt);
  3737. hti = TreeView_HitTest(_hwndTree, &tvht);
  3738. // But not if they click on the button, since that means that they
  3739. // are merely expanding/contracting and not selecting
  3740. if (hti && !(tvht.flags & TVHT_ONITEMBUTTON))
  3741. {
  3742. _fSelectFromMouseClick = TRUE;
  3743. TreeView_SelectItem(_hwndTree, hti);
  3744. _OnSelChange(TRUE);
  3745. _fSelectFromMouseClick = FALSE;
  3746. }
  3747. break;
  3748. }
  3749. case NM_RCLICK:
  3750. {
  3751. DWORD dwPos = GetMessagePos();
  3752. _fOkToRename = TRUE;
  3753. lres = _OnContextMenu(GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos));
  3754. _fOkToRename = FALSE;
  3755. break;
  3756. }
  3757. default:
  3758. break;
  3759. }
  3760. } // case ID_CONTROL
  3761. case ID_HEADER:
  3762. {
  3763. switch (pnm->code)
  3764. {
  3765. case HDN_TRACK:
  3766. break;
  3767. case HDN_ENDTRACK:
  3768. ::InvalidateRect(_hwndTree, NULL, TRUE);
  3769. break;
  3770. default:
  3771. break;
  3772. }
  3773. }
  3774. default:
  3775. break;
  3776. }
  3777. return lres;
  3778. }
  3779. HRESULT CNscTree::OnChange(LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  3780. {
  3781. // review chrisny: better error return here.
  3782. _fHandlingShellNotification = TRUE;
  3783. _OnChangeNotify(lEvent, pidl1, pidl2);
  3784. _fHandlingShellNotification = FALSE;
  3785. return S_OK;
  3786. }
  3787. // in comctl32 v5 there is no way to programmatically select an item (in single expand mode)
  3788. // without expanding it, so we fake it here by setting _fIgnoreNextItemExpanding to true and then
  3789. // rejecting expansion when it is set
  3790. void CNscTree::_SelectNoExpand(HWND hwnd, HTREEITEM hti)
  3791. {
  3792. UINT uFlags = TVGN_CARET;
  3793. if (IsOS(OS_WHISTLERORGREATER))
  3794. uFlags |= TVSI_NOSINGLEEXPAND; // new for v6
  3795. else if (_fSingleExpand)
  3796. _fIgnoreNextItemExpanding = TRUE;
  3797. _fInExpand = TRUE; // Treeview will force expand the parents, make sure we know it's not the user clicking on items
  3798. TreeView_Select(hwnd, hti, uFlags);
  3799. _fInExpand = FALSE;
  3800. _fIgnoreNextItemExpanding = FALSE;
  3801. }
  3802. void CNscTree::_SelectPidl(LPCITEMIDLIST pidl, BOOL fCreate, BOOL fReinsert)
  3803. {
  3804. HTREEITEM hti;
  3805. // _ExpandToItem doesn't play well with empty pidl (i.e. desktop)
  3806. if (_mode == MODE_NORMAL && ILIsEqual(pidl, _pidlRoot))
  3807. hti = _FindFromRoot(NULL, pidl);
  3808. else
  3809. hti = _ExpandToItem(pidl, fCreate, fReinsert);
  3810. if (hti != NULL)
  3811. {
  3812. _SelectNoExpand(_hwndTree, hti);
  3813. #ifdef DEBUG
  3814. TraceHTREE(hti, TEXT("Found"));
  3815. #endif
  3816. }
  3817. }
  3818. HTREEITEM CNscTree::_ExpandToItem(LPCITEMIDLIST pidl, BOOL fCreate /*= TRUE*/, BOOL fReinsert /*= FALSE*/)
  3819. {
  3820. HTREEITEM hti = NULL;
  3821. LPITEMIDLIST pidlItem = NULL;
  3822. LPCITEMIDLIST pidlTemp = NULL;
  3823. LPITEMIDLIST pidlParent;
  3824. TV_ITEM tvi;
  3825. IShellFolder *psf = NULL;
  3826. IShellFolder *psfNext = NULL;
  3827. HRESULT hr = S_OK;
  3828. #ifdef DEBUG
  3829. TracePIDLAbs(pidl, TEXT("Attempting to select"));
  3830. #endif
  3831. // We need to do this so items that are rooted at the Desktop, are found
  3832. // correctly.
  3833. HTREEITEM htiParent = (_mode == MODE_NORMAL) ? TreeView_GetRoot(_hwndTree) : TVI_ROOT;
  3834. ASSERT((_hwndTree != NULL) && (pidl != NULL));
  3835. if (_hwndTree == NULL)
  3836. goto LGone;
  3837. // We should unify the "FindFromRoot" code path and this one.
  3838. pidlParent = _pidlRoot;
  3839. if (ILIsEmpty(pidlParent))
  3840. {
  3841. pidlTemp = pidl;
  3842. SHGetDesktopFolder(&psf);
  3843. }
  3844. else
  3845. {
  3846. if ((pidlTemp = ILFindChild(pidlParent, pidl)) == NULL)
  3847. {
  3848. goto LGone; // not root match, no hti
  3849. }
  3850. // root match, carry on . . .
  3851. hr = IEBindToObject(pidlParent, &psf);
  3852. }
  3853. if (FAILED(hr))
  3854. {
  3855. goto LGone;
  3856. }
  3857. while (!ILIsEmpty(pidlTemp))
  3858. {
  3859. if ((pidlItem = ILCloneFirst(pidlTemp)) == NULL)
  3860. goto LGone;
  3861. pidlTemp = _ILNext(pidlTemp);
  3862. // Since we are selecting a pidl, we need to make sure it's parent is visible.
  3863. // We do it this before the insert, so that we don't have to check for duplicates.
  3864. // when enumerating NTDev it goes from about 10min to about 8 seconds.
  3865. if (htiParent != TVI_ROOT)
  3866. {
  3867. // Check to see if it's expanded.
  3868. tvi.mask = TVIF_STATE;
  3869. tvi.stateMask = (TVIS_EXPANDEDONCE | TVIS_EXPANDED | TVIS_EXPANDPARTIAL);
  3870. tvi.hItem = htiParent;
  3871. if (!TreeView_GetItem(_hwndTree, &tvi))
  3872. {
  3873. goto LGone;
  3874. }
  3875. // If not, Expand it.
  3876. if (!(tvi.state & TVIS_EXPANDED))
  3877. {
  3878. _pidlExpandingTo = pidlItem;
  3879. _ExpandNode(htiParent, TVE_EXPAND, 1);
  3880. _pidlExpandingTo = NULL;
  3881. }
  3882. }
  3883. // Now that we have it enumerated, check to see if the child if there.
  3884. hti = _FindChild(psf, htiParent, pidlItem);
  3885. // fReinsert will allow us to force the item to be reinserted
  3886. if (hti && fReinsert)
  3887. {
  3888. ASSERT(fCreate);
  3889. TreeView_DeleteItem(_hwndTree, hti);
  3890. hti = NULL;
  3891. }
  3892. // Do we have a child in the newly expanded tree?
  3893. if (NULL == hti)
  3894. {
  3895. // No. We must have to create it.
  3896. if (!fCreate)
  3897. {
  3898. // But, we're not allowed to... Shoot.
  3899. goto LGone;
  3900. }
  3901. if (S_OK != _InsertChild(htiParent, psf, pidlItem, FALSE, FALSE, DEFAULTORDERPOSITION, &hti))
  3902. {
  3903. goto LGone;
  3904. }
  3905. }
  3906. if (htiParent != TVI_ROOT)
  3907. {
  3908. tvi.mask = TVIF_STATE;
  3909. tvi.stateMask = (TVIS_EXPANDEDONCE | TVIS_EXPANDED | TVIS_EXPANDPARTIAL);
  3910. tvi.hItem = htiParent;
  3911. if (TreeView_GetItem(_hwndTree, &tvi))
  3912. {
  3913. if (!(tvi.state & TVIS_EXPANDED))
  3914. {
  3915. TreeView_SetChildren(_hwndTree, htiParent, NSC_CHILDREN_ADD); // Make sure the expand will do something
  3916. _fIgnoreNextItemExpanding = TRUE;
  3917. _ExpandNode(htiParent, TVE_EXPAND | TVE_EXPANDPARTIAL, 1);
  3918. _fIgnoreNextItemExpanding = FALSE;
  3919. }
  3920. }
  3921. }
  3922. // we don't need to bind if its the last one
  3923. // -- a half-implemented ISF might not like this bind...
  3924. if (!ILIsEmpty(pidlTemp))
  3925. hr = psf->BindToObject(pidlItem, NULL, IID_PPV_ARG(IShellFolder, &psfNext));
  3926. ILFree(pidlItem);
  3927. pidlItem = NULL;
  3928. if (FAILED(hr))
  3929. goto LGone;
  3930. htiParent = hti;
  3931. psf->Release();
  3932. psf = psfNext;
  3933. psfNext = NULL;
  3934. }
  3935. LGone:
  3936. if (psf != NULL)
  3937. psf->Release();
  3938. if (psfNext != NULL)
  3939. psfNext->Release();
  3940. if (pidlItem != NULL)
  3941. ILFree(pidlItem);
  3942. return hti;
  3943. }
  3944. HRESULT CNscTree::GetSelectedItem(LPITEMIDLIST * ppidl, int nItem)
  3945. {
  3946. HRESULT hr = E_INVALIDARG;
  3947. // nItem will be used in the future when we support multiple selections.
  3948. // GetSelectedItem() returns S_FALSE and (NULL == *ppidl) if not that many
  3949. // items are selected. Not yet implemented.
  3950. if (nItem > 0)
  3951. {
  3952. *ppidl = NULL;
  3953. return S_FALSE;
  3954. }
  3955. if (ppidl)
  3956. {
  3957. *ppidl = NULL;
  3958. // Is the ListView still there?
  3959. if (_fIsSelectionCached)
  3960. {
  3961. // No, so get the selection that was saved before
  3962. // the listview was destroyed.
  3963. if (_pidlSelected)
  3964. {
  3965. *ppidl = ILClone(_pidlSelected);
  3966. hr = S_OK;
  3967. }
  3968. else
  3969. hr = S_FALSE;
  3970. }
  3971. else
  3972. {
  3973. HTREEITEM htiSelected = TreeView_GetSelection(_hwndTree);
  3974. if (htiSelected)
  3975. {
  3976. *ppidl = _GetFullIDList(htiSelected);
  3977. hr = S_OK;
  3978. }
  3979. else
  3980. hr = S_FALSE;
  3981. }
  3982. }
  3983. return hr;
  3984. }
  3985. HRESULT CNscTree::SetSelectedItem(LPCITEMIDLIST pidl, BOOL fCreate, BOOL fReinsert, int nItem)
  3986. {
  3987. // nItem will be used in the future when we support multiple selections.
  3988. // Not yet implemented.
  3989. if (nItem > 0)
  3990. {
  3991. return S_FALSE;
  3992. }
  3993. // Override fCreate if the object no longer exists
  3994. DWORD dwAttributes = SFGAO_VALIDATE;
  3995. fCreate = fCreate && SUCCEEDED(IEGetAttributesOf(pidl, &dwAttributes));
  3996. // We probably haven't seen the ChangeNotify yet, so we tell
  3997. // _SelectPidl to create any folders that are there
  3998. // Then select the pidl, expanding as necessary
  3999. _fInSelectPidl = TRUE;
  4000. _SelectPidl(pidl, fCreate, fReinsert);
  4001. _fInSelectPidl = FALSE;
  4002. return S_OK;
  4003. }
  4004. //*** CNscTree::IWinEventHandler
  4005. HRESULT CNscTree::OnWinEvent(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres)
  4006. {
  4007. HRESULT hr = E_FAIL;
  4008. ULONG_PTR cookie = 0;
  4009. // FUSION: When nsc calls out to 3rd party code we want it to use
  4010. // the process default context. This means that the 3rd party code will get
  4011. // v5 in the explorer process. However, if shell32 is hosted in a v6 process,
  4012. // then the 3rd party code will still get v6.
  4013. // Future enhancements to this codepath may include using the fusion manifest
  4014. // tab <noinherit> which basically surplants the activat(null) in the following
  4015. // codepath. This disables the automatic activation from user32 for the duration
  4016. // of this wndproc, essentially doing this null push.
  4017. // we need to do this here as well as in _SubClassTreeWndProc as someone could have
  4018. // set v6 context before getting in here (band site,...)
  4019. NT5_ActivateActCtx(NULL, &cookie);
  4020. switch (uMsg)
  4021. {
  4022. case WM_NOTIFY:
  4023. *plres = _OnNotify((LPNMHDR)lParam);
  4024. hr = S_OK;
  4025. break;
  4026. case WM_PALETTECHANGED:
  4027. _OnPaletteChanged(wParam, lParam);
  4028. // are we really supposed to return E_FAIL here?
  4029. break;
  4030. default:
  4031. break;
  4032. }
  4033. if (cookie != 0)
  4034. NT5_DeactivateActCtx(cookie);
  4035. return hr;
  4036. }
  4037. void CNscTree::_OnChangeNotify(LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra)
  4038. {
  4039. switch (lEvent)
  4040. {
  4041. case SHCNE_RENAMEFOLDER:
  4042. case SHCNE_RENAMEITEM:
  4043. if (pidl && pidlExtra)
  4044. _OnSHNotifyRename(pidl, pidlExtra);
  4045. else
  4046. ASSERT(FALSE);
  4047. break;
  4048. case SHCNE_DELETE:
  4049. case SHCNE_RMDIR:
  4050. case SHCNE_DRIVEREMOVED:
  4051. if (pidl)
  4052. _OnSHNotifyDelete(pidl, NULL, NULL);
  4053. else
  4054. ASSERT(FALSE);
  4055. break;
  4056. case SHCNE_UPDATEITEM:
  4057. // when nsc browses other namespaces, sometimes an updateitem could be fired
  4058. // on a pidl thats actually expanded in the tree, so check for it.
  4059. if (pidl)
  4060. {
  4061. IShellFolder* psf = NULL;
  4062. LPCITEMIDLIST pidlChild;
  4063. if (SUCCEEDED(_ParentFromItem(pidl, &psf, &pidlChild)))
  4064. {
  4065. LPITEMIDLIST pidlReal;
  4066. if (SUCCEEDED(_IdlRealFromIdlSimple(psf, pidlChild, &pidlReal)) && pidlReal)
  4067. {
  4068. // zip files receive updateitem when they really mean updatedir
  4069. if (SHGetAttributes(psf, pidlReal, SFGAO_FOLDER | SFGAO_STREAM) == (SFGAO_FOLDER | SFGAO_STREAM))
  4070. {
  4071. _OnSHNotifyUpdateDir(pidl);
  4072. }
  4073. _OnSHNotifyUpdateItem(pidl, pidlReal);
  4074. ILFree(pidlReal);
  4075. }
  4076. psf->Release();
  4077. }
  4078. }
  4079. break;
  4080. case SHCNE_NETSHARE:
  4081. case SHCNE_NETUNSHARE:
  4082. if (pidl)
  4083. _OnSHNotifyUpdateItem(pidl, NULL);
  4084. break;
  4085. case SHCNE_CREATE:
  4086. case SHCNE_MKDIR:
  4087. case SHCNE_DRIVEADD:
  4088. if (pidl)
  4089. {
  4090. _OnSHNotifyCreate(pidl, DEFAULTORDERPOSITION, NULL);
  4091. if (SHCNE_MKDIR == lEvent &&
  4092. _pidlNewFolderParent &&
  4093. ILIsParent(_pidlNewFolderParent, pidl, TRUE)) // TRUE = immediate parent only
  4094. {
  4095. EVAL(SUCCEEDED(_EnterNewFolderEditMode(pidl)));
  4096. }
  4097. }
  4098. break;
  4099. case SHCNE_UPDATEDIR:
  4100. if (pidl)
  4101. {
  4102. _OnSHNotifyUpdateDir(pidl);
  4103. }
  4104. break;
  4105. case SHCNE_MEDIAREMOVED:
  4106. case SHCNE_MEDIAINSERTED:
  4107. if (pidl)
  4108. {
  4109. HTREEITEM hti = _FindFromRoot(NULL, pidl);
  4110. if (hti)
  4111. {
  4112. if (lEvent == SHCNE_MEDIAREMOVED)
  4113. {
  4114. LPITEMIDLIST pidlSelected;
  4115. if (SUCCEEDED(GetSelectedItem(&pidlSelected, 0)))
  4116. {
  4117. if (ILIsEqual(pidl, pidlSelected) || ILIsParent(pidl, pidlSelected, FALSE))
  4118. {
  4119. IShellFolder *psf;
  4120. if (SUCCEEDED(SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), NULL)))
  4121. {
  4122. BOOL fSelected = FALSE;
  4123. for (HTREEITEM htiSelect = TreeView_GetNextSibling(_hwndTree, hti); htiSelect; htiSelect = TreeView_GetNextSibling(_hwndTree, htiSelect))
  4124. {
  4125. PORDERITEM poi = _GetTreeOrderItem(htiSelect);
  4126. if (poi)
  4127. {
  4128. if (!SHGetAttributes(psf, poi->pidl, SFGAO_REMOVABLE))
  4129. {
  4130. _SelectNoExpand(_hwndTree, htiSelect);
  4131. fSelected = TRUE;
  4132. break;
  4133. }
  4134. }
  4135. }
  4136. if (!fSelected)
  4137. {
  4138. _SelectNoExpand(_hwndTree, TreeView_GetParent(_hwndTree, hti));
  4139. }
  4140. psf->Release();
  4141. }
  4142. }
  4143. ILFree(pidlSelected);
  4144. }
  4145. TreeView_DeleteChildren(_hwndTree, hti);
  4146. TreeView_Expand(_hwndTree, hti, TVE_COLLAPSE | TVE_COLLAPSERESET); // reset the item
  4147. TreeView_SetChildren(_hwndTree, hti, NSC_CHILDREN_REMOVE);
  4148. }
  4149. else
  4150. {
  4151. TreeView_SetChildren(_hwndTree, hti, NSC_CHILDREN_CALLBACK);
  4152. }
  4153. _TreeInvalidateItemInfo(hti, TVIF_TEXT);
  4154. }
  4155. }
  4156. break;
  4157. case SHCNE_DRIVEADDGUI:
  4158. case SHCNE_SERVERDISCONNECT:
  4159. case SHCNE_ASSOCCHANGED:
  4160. break;
  4161. case SHCNE_UPDATEIMAGE:
  4162. if (pidl)
  4163. {
  4164. int iIndex;
  4165. if (pidlExtra)
  4166. { // new style update image notification.....
  4167. iIndex = SHHandleUpdateImage(pidlExtra);
  4168. if (iIndex == -1)
  4169. break;
  4170. }
  4171. else
  4172. iIndex = *(int UNALIGNED *)((BYTE*)pidl + 2);
  4173. _InvalidateImageIndex(NULL, iIndex);
  4174. }
  4175. break;
  4176. case SHCNE_EXTENDED_EVENT:
  4177. {
  4178. SHChangeDWORDAsIDList UNALIGNED *pdwidl = (SHChangeDWORDAsIDList UNALIGNED *)pidl;
  4179. INT_PTR iEvent = pdwidl->dwItem1;
  4180. switch (iEvent)
  4181. {
  4182. case SHCNEE_ORDERCHANGED:
  4183. if (EVAL(pidl))
  4184. {
  4185. if (_fDropping || // If WE are dropping.
  4186. _fInLabelEdit || // We're editing a name (Kicks us out)
  4187. SHChangeMenuWasSentByMe(this, pidl) || // Ignore if we sent it.
  4188. (_mode == MODE_HISTORY)) // Always ignore history changes
  4189. {
  4190. TraceMsg(TF_NSC, "NSCBand: Ignoring Change Notify: We sent");
  4191. //ignore the notification
  4192. }
  4193. else
  4194. {
  4195. TraceMsg(TF_BAND, "NSCBand: OnChange SHCNEE_ORDERCHANGED accepted");
  4196. _dwOrderSig++;
  4197. HTREEITEM htiRoot = _FindFromRoot(TVI_ROOT, pidlExtra);
  4198. if (htiRoot != NULL)
  4199. _UpdateDir(htiRoot, FALSE);
  4200. }
  4201. }
  4202. break;
  4203. case SHCNEE_WININETCHANGED:
  4204. {
  4205. if (pdwidl->dwItem2 & (CACHE_NOTIFY_SET_ONLINE | CACHE_NOTIFY_SET_OFFLINE))
  4206. {
  4207. BOOL fOnline = !SHIsGlobalOffline();
  4208. if ((fOnline && !_fOnline) || (!fOnline && _fOnline))
  4209. {
  4210. // State changed
  4211. _fOnline = fOnline;
  4212. _OnSHNotifyOnlineChange(TVI_ROOT, _fOnline);
  4213. }
  4214. }
  4215. if (pdwidl->dwItem2 & (CACHE_NOTIFY_ADD_URL |
  4216. CACHE_NOTIFY_DELETE_URL |
  4217. CACHE_NOTIFY_DELETE_ALL |
  4218. CACHE_NOTIFY_URL_SET_STICKY |
  4219. CACHE_NOTIFY_URL_UNSET_STICKY))
  4220. {
  4221. // Something in the cache changed
  4222. _OnSHNotifyCacheChange(TVI_ROOT, pdwidl->dwItem2);
  4223. }
  4224. break;
  4225. }
  4226. }
  4227. break;
  4228. }
  4229. break;
  4230. }
  4231. return;
  4232. }
  4233. // note, this duplicates SHGetRealIDL() so we work in non integrated shell mode
  4234. // WARNING: if it is not a file system pidl SFGAO_FILESYSTEM, we don't need to do this...
  4235. // but this is only called in the case of SHCNE_CREATE for shell notify
  4236. // and all shell notify pidls are SFGAO_FILESYSTEM
  4237. HRESULT CNscTree::_IdlRealFromIdlSimple(IShellFolder *psf, LPCITEMIDLIST pidlSimple, LPITEMIDLIST *ppidlReal)
  4238. {
  4239. WCHAR wszPath[MAX_PATH];
  4240. ULONG cbEaten;
  4241. HRESULT hr = S_OK;
  4242. if (FAILED(DisplayNameOf(psf, pidlSimple, SHGDN_FORPARSING | SHGDN_INFOLDER, wszPath, ARRAYSIZE(wszPath))) ||
  4243. FAILED(psf->ParseDisplayName(NULL, NULL, wszPath, &cbEaten, ppidlReal, NULL)))
  4244. {
  4245. hr = SHILClone(pidlSimple, ppidlReal); // we don't own the lifetime of pidlSimple
  4246. }
  4247. return hr;
  4248. }
  4249. HRESULT CNscTree::Refresh(void)
  4250. {
  4251. _bSynchId++;
  4252. if (_bSynchId >= 16)
  4253. _bSynchId = 0;
  4254. TraceMsg(TF_NSC, "Expensive Refresh of tree");
  4255. _htiActiveBorder = NULL;
  4256. HRESULT hr = S_OK;
  4257. if (_pnscProxy)
  4258. {
  4259. DWORD dwStyle, dwExStyle;
  4260. if (SUCCEEDED(_pnscProxy->RefreshFlags(&dwStyle, &dwExStyle, &_grfFlags)))
  4261. {
  4262. dwStyle = _SetStyle(dwStyle); // initializes new _style and returns old one
  4263. if ((dwStyle ^ _style) & ~WS_VISIBLE) // don't care if only visible changed
  4264. {
  4265. DWORD dwMask = (_style | dwStyle) & ~WS_VISIBLE; // don't want to change visible style
  4266. SetWindowBits(_hwndTree, GWL_STYLE, dwMask, _style);
  4267. }
  4268. dwExStyle = _SetExStyle(dwExStyle);
  4269. if (dwExStyle != _dwExStyle)
  4270. TreeView_SetExtendedStyle(_hwndTree, _dwExStyle, dwExStyle | _dwExStyle);
  4271. }
  4272. }
  4273. if (MODE_NORMAL == _mode)
  4274. {
  4275. BOOL fOrdered;
  4276. _fUpdate = TRUE;
  4277. _StartBackgroundEnum(TreeView_GetChild(_hwndTree, TVI_ROOT), _pidlRoot, &fOrdered, TRUE);
  4278. _fUpdate = FALSE;
  4279. }
  4280. else
  4281. {
  4282. LPITEMIDLIST pidlRoot;
  4283. hr = SHILClone(_pidlRoot, &pidlRoot); // Need to do this because it's freed
  4284. if (SUCCEEDED(hr))
  4285. {
  4286. HTREEITEM htiSelected = TreeView_GetSelection(_hwndTree);
  4287. TV_ITEM tvi;
  4288. tvi.mask = TVIF_HANDLE | TVIF_STATE;
  4289. tvi.stateMask = TVIS_EXPANDED;
  4290. tvi.hItem = (HTREEITEM)htiSelected;
  4291. BOOL fExpanded = (TreeView_GetItem(_hwndTree, &tvi) && (tvi.state & TVIS_EXPANDED));
  4292. LPITEMIDLIST pidlSelect;
  4293. GetSelectedItem(&pidlSelect, 0);
  4294. _ChangePidlRoot(pidlRoot);
  4295. if (pidlSelect)
  4296. {
  4297. _Expand(pidlSelect, fExpanded ? 1 : 0);
  4298. ILFree(pidlSelect);
  4299. }
  4300. ILFree(pidlRoot);
  4301. }
  4302. }
  4303. return hr;
  4304. }
  4305. void CNscTree::_CacheDetails()
  4306. {
  4307. if (_ulDisplayCol == (ULONG)-1)
  4308. {
  4309. _ulSortCol = _ulDisplayCol = 0;
  4310. if (_psf2Cache)
  4311. {
  4312. _psf2Cache->GetDefaultColumn(0, &_ulSortCol, &_ulDisplayCol);
  4313. }
  4314. }
  4315. }
  4316. HRESULT CNscTree::_GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags,
  4317. LPSHELLDETAILS pdetails)
  4318. {
  4319. ASSERT(_psfCache);
  4320. _CacheDetails();
  4321. if (_ulDisplayCol)
  4322. return _psf2Cache->GetDetailsOf(pidl, _ulDisplayCol, pdetails);
  4323. return _psfCache->GetDisplayNameOf(pidl, uFlags, &pdetails->str);
  4324. }
  4325. // if fSort, then compare for sort, else compare for existence.
  4326. HRESULT CNscTree::_CompareIDs(IShellFolder *psf, LPITEMIDLIST pidl1, LPITEMIDLIST pidl2)
  4327. {
  4328. _CacheDetails();
  4329. return psf->CompareIDs(_ulSortCol, pidl1, pidl2);
  4330. }
  4331. HRESULT CNscTree::_ParentFromItem(LPCITEMIDLIST pidl, IShellFolder** ppsfParent, LPCITEMIDLIST *ppidlChild)
  4332. {
  4333. return IEBindToParentFolder(pidl, ppsfParent, ppidlChild);
  4334. }
  4335. COLORREF CNscTree::_GetRegColor(COLORREF clrDefault, LPCTSTR pszName)
  4336. {
  4337. // Fetch the specified alternate color
  4338. COLORREF clrValue;
  4339. DWORD cbData = sizeof(clrValue);
  4340. if (FAILED(SKGetValue(SHELLKEY_HKCU_EXPLORER, NULL, pszName, NULL, &clrValue, &cbData)))
  4341. {
  4342. return clrDefault;
  4343. }
  4344. return clrValue;
  4345. }
  4346. LRESULT CNscTree::_OnCDNotify(LPNMCUSTOMDRAW pnm)
  4347. {
  4348. LRESULT lres = CDRF_DODEFAULT;
  4349. ASSERT(pnm->hdr.idFrom == ID_CONTROL);
  4350. if (_dwFlags & NSS_NORMALTREEVIEW)
  4351. {
  4352. LPNMTVCUSTOMDRAW pnmTVCustomDraw = (LPNMTVCUSTOMDRAW) pnm;
  4353. if (pnmTVCustomDraw->nmcd.dwDrawStage == CDDS_PREPAINT)
  4354. {
  4355. if (_fShowCompColor)
  4356. {
  4357. return CDRF_NOTIFYITEMDRAW;
  4358. }
  4359. else
  4360. {
  4361. return lres;
  4362. }
  4363. }
  4364. if (pnmTVCustomDraw->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)
  4365. {
  4366. PORDERITEM pOrderItem = GetPoi(pnmTVCustomDraw->nmcd.lItemlParam);
  4367. if (pOrderItem && pOrderItem->pidl)
  4368. {
  4369. LPCITEMIDLIST pidl = _CacheParentShellFolder((HTREEITEM)pnmTVCustomDraw->nmcd.dwItemSpec, pOrderItem->pidl);
  4370. if (pidl)
  4371. {
  4372. DWORD dwAttribs = SHGetAttributes(_psfCache, pidl, SFGAO_COMPRESSED | SFGAO_ENCRYPTED);
  4373. // either compressed, or encrypted, can never be both
  4374. if (dwAttribs & SFGAO_COMPRESSED)
  4375. {
  4376. // If it is the item is hi-lited (selected, and has focus), blue text is not visible with the hi-lite...
  4377. if ((pnmTVCustomDraw->nmcd.uItemState & CDIS_SELECTED) && (pnmTVCustomDraw->nmcd.uItemState & CDIS_FOCUS))
  4378. pnmTVCustomDraw->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
  4379. else
  4380. pnmTVCustomDraw->clrText = _GetRegColor(RGB(0, 0, 255), TEXT("AltColor")); // default Blue
  4381. }
  4382. else if (dwAttribs & SFGAO_ENCRYPTED)
  4383. {
  4384. if ((pnmTVCustomDraw->nmcd.uItemState & CDIS_SELECTED) && (pnmTVCustomDraw->nmcd.uItemState & CDIS_FOCUS))
  4385. pnmTVCustomDraw->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
  4386. else
  4387. pnmTVCustomDraw->clrText = _GetRegColor(RGB(19, 146, 13), TEXT("AltEncryptionColor")); // default Luna Mid Green
  4388. }
  4389. }
  4390. }
  4391. }
  4392. return lres;
  4393. }
  4394. switch (pnm->dwDrawStage)
  4395. {
  4396. case CDDS_PREPAINT:
  4397. if (NSS_BROWSERSELECT & _dwFlags)
  4398. lres = CDRF_NOTIFYITEMDRAW;
  4399. break;
  4400. case CDDS_ITEMPREPAINT:
  4401. {
  4402. //APPCOMPAT davemi: why is comctl giving us empty rects?
  4403. if (IsRectEmpty(&(pnm->rc)))
  4404. break;
  4405. PORDERITEM poi = GetPoi(pnm->lItemlParam);
  4406. DWORD dwFlags = 0;
  4407. COLORREF clrBk, clrText;
  4408. LPNMTVCUSTOMDRAW pnmtv = (LPNMTVCUSTOMDRAW)pnm;
  4409. TV_ITEM tvi;
  4410. TCHAR sz[MAX_URL_STRING];
  4411. tvi.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_STATE;
  4412. tvi.stateMask = TVIS_EXPANDED | TVIS_STATEIMAGEMASK | TVIS_DROPHILITED;
  4413. tvi.pszText = sz;
  4414. tvi.cchTextMax = MAX_URL_STRING;
  4415. tvi.hItem = (HTREEITEM)pnm->dwItemSpec;
  4416. if (!TreeView_GetItem(_hwndTree, &tvi))
  4417. break;
  4418. //
  4419. // See if we have fetched greyed/pinned information for this item yet
  4420. //
  4421. ITEMINFO * pii = GetPii(pnm->lItemlParam);
  4422. pii->fFetched = TRUE;
  4423. if (pii->fGreyed && !(_mode & MODE_CONTROL))
  4424. dwFlags |= DIGREYED;
  4425. if (pii->fPinned)
  4426. dwFlags |= DIPINNED;
  4427. if (!pii->fNavigable)
  4428. dwFlags |= DIFOLDER;
  4429. dwFlags |= DIICON;
  4430. if (_style & TVS_RTLREADING)
  4431. dwFlags |= DIRTLREADING;
  4432. clrBk = TreeView_GetBkColor(_hwndTree);
  4433. clrText = GetSysColor(COLOR_WINDOWTEXT);
  4434. //if we're renaming an item, don't draw any text for it (otherwise it shows behind the item)
  4435. if (tvi.hItem == _htiRenaming)
  4436. sz[0] = 0;
  4437. if (tvi.state & TVIS_EXPANDED)
  4438. dwFlags |= DIFOLDEROPEN;
  4439. if (!(_dwFlags & NSS_MULTISELECT) && ((pnm->uItemState & CDIS_SELECTED) || (tvi.state & TVIS_DROPHILITED)))
  4440. {
  4441. if (_fHasFocus || tvi.state & TVIS_DROPHILITED)
  4442. {
  4443. clrBk = GetSysColor(COLOR_HIGHLIGHT);
  4444. clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
  4445. }
  4446. else
  4447. {
  4448. clrBk = GetSysColor(COLOR_BTNFACE);
  4449. }
  4450. // dwFlags |= DIFOCUSRECT;
  4451. }
  4452. if (pnm->uItemState & CDIS_HOT)
  4453. {
  4454. if (!(_mode & MODE_CONTROL))
  4455. dwFlags |= DIHOT;
  4456. clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
  4457. if (clrText == clrBk)
  4458. clrText = GetSysColor(COLOR_HIGHLIGHT);
  4459. }
  4460. if ((_dwFlags & NSS_MULTISELECT) && (pnm->uItemState & CDIS_SELECTED))
  4461. dwFlags |= DIACTIVEBORDER | DISUBFIRST | DISUBLAST;
  4462. if (tvi.state & NSC_TVIS_MARKED)
  4463. {
  4464. if (_dwFlags & NSS_MULTISELECT)
  4465. {
  4466. if (_fHasFocus)
  4467. {
  4468. clrBk = GetSysColor(COLOR_HIGHLIGHT);
  4469. clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
  4470. }
  4471. else
  4472. {
  4473. clrBk = GetSysColor(COLOR_BTNFACE);
  4474. }
  4475. }
  4476. else
  4477. {
  4478. dwFlags |= DIACTIVEBORDER;
  4479. //top level item
  4480. if (_IsTopParentItem((HTREEITEM)pnm->dwItemSpec))
  4481. {
  4482. dwFlags |= DISUBFIRST;
  4483. if (!(tvi.state & TVIS_EXPANDED))
  4484. dwFlags |= DISUBLAST;
  4485. }
  4486. else // lower level items
  4487. {
  4488. HTREEITEM hti;
  4489. dwFlags |= DISUBITEM;
  4490. if (((HTREEITEM)pnm->dwItemSpec) == _htiActiveBorder)
  4491. dwFlags |= DISUBFIRST;
  4492. hti = TreeView_GetNextVisible(_hwndTree, (HTREEITEM)pnm->dwItemSpec);
  4493. if ((hti && !_IsMarked(hti)) || (hti == NULL))
  4494. dwFlags |= DISUBLAST;
  4495. }
  4496. }
  4497. }
  4498. if ((_dwFlags & NSS_HEADER) && _hwndHdr &&
  4499. _CacheParentShellFolder((HTREEITEM)pnm->dwItemSpec, poi->pidl) &&
  4500. _psf2Cache)
  4501. {
  4502. // with header we don't draw active order because it looks ugly,
  4503. // but with multiselect we do because that's how we differentiate selected items
  4504. if (!(_dwFlags & NSS_MULTISELECT))
  4505. dwFlags &= ~DIACTIVEBORDER;
  4506. RECT rc;
  4507. CopyRect(&rc, &(pnm->rc));
  4508. for (int i=0; i<DPA_GetPtrCount(_hdpaColumns); i++)
  4509. {
  4510. RECT rcHeader;
  4511. int iLevel = 0;
  4512. HEADERINFO *phinfo = (HEADERINFO *)DPA_GetPtr(_hdpaColumns, i);
  4513. ASSERT(phinfo);
  4514. Header_GetItemRect(_hwndHdr, i, &rcHeader);
  4515. rc.left = rcHeader.left;
  4516. rc.right = rcHeader.right;
  4517. if (i == 0) //it is name column
  4518. {
  4519. iLevel = pnmtv->iLevel;
  4520. //use sz set above in the function
  4521. }
  4522. else
  4523. {
  4524. // in multiselect draw border only around the name
  4525. dwFlags &= ~DIACTIVEBORDER;
  4526. dwFlags = 0;
  4527. if (phinfo->fmt & LVCFMT_RIGHT)
  4528. dwFlags |= DIRIGHT;
  4529. clrBk = TreeView_GetBkColor(_hwndTree);
  4530. clrText = GetSysColor(COLOR_WINDOWTEXT);
  4531. sz[0] = 0;
  4532. VARIANT var;
  4533. if (SUCCEEDED(_psf2Cache->GetDetailsEx(poi->pidl, phinfo->pscid, &var)))
  4534. {
  4535. VariantToStr(&var, sz, ARRAYSIZE(sz));
  4536. }
  4537. }
  4538. _DrawItem((HTREEITEM)pnm->dwItemSpec, sz, pnm->hdc, &rc, dwFlags, iLevel, clrBk, clrText);
  4539. }
  4540. }
  4541. else
  4542. {
  4543. _DrawItem((HTREEITEM)pnm->dwItemSpec, sz, pnm->hdc, &(pnm->rc), dwFlags, pnmtv->iLevel, clrBk, clrText);
  4544. }
  4545. lres = CDRF_SKIPDEFAULT;
  4546. break;
  4547. }
  4548. case CDDS_POSTPAINT:
  4549. break;
  4550. }
  4551. return lres;
  4552. }
  4553. // *******droptarget implementation.
  4554. void CNscTree::_DtRevoke()
  4555. {
  4556. if (_fDTRegistered)
  4557. {
  4558. RevokeDragDrop(_hwndTree);
  4559. _fDTRegistered = FALSE;
  4560. }
  4561. }
  4562. void CNscTree::_DtRegister()
  4563. {
  4564. if (!_fDTRegistered && (_dwFlags & NSS_DROPTARGET))
  4565. {
  4566. if (::IsWindow(_hwndTree))
  4567. {
  4568. HRESULT hr = THR(RegisterDragDrop(_hwndTree, SAFECAST(this, IDropTarget*)));
  4569. _fDTRegistered = BOOLIFY(SUCCEEDED(hr));
  4570. }
  4571. else
  4572. ASSERT(FALSE);
  4573. }
  4574. }
  4575. HRESULT CNscTree::GetWindowsDDT(HWND * phwndLock, HWND * phwndScroll)
  4576. {
  4577. if (!::IsWindow(_hwndTree))
  4578. {
  4579. ASSERT(FALSE);
  4580. return S_FALSE;
  4581. }
  4582. *phwndLock = /*_hwndDD*/_hwndTree;
  4583. *phwndScroll = _hwndTree;
  4584. return S_OK;
  4585. }
  4586. const int iInsertThresh = 6;
  4587. // We use this as the sentinal "This is where you started"
  4588. #define DDT_SENTINEL ((DWORD_PTR)(INT_PTR)-1)
  4589. HRESULT CNscTree::HitTestDDT(UINT nEvent, LPPOINT ppt, DWORD_PTR *pdwId, DWORD * pdwDropEffect)
  4590. {
  4591. switch (nEvent)
  4592. {
  4593. case HTDDT_ENTER:
  4594. break;
  4595. case HTDDT_LEAVE:
  4596. {
  4597. _fDragging = FALSE;
  4598. _fDropping = FALSE;
  4599. DAD_ShowDragImage(FALSE);
  4600. TreeView_SetInsertMark(_hwndTree, NULL, !_fInsertBefore);
  4601. TreeView_SelectDropTarget(_hwndTree, NULL);
  4602. DAD_ShowDragImage(TRUE);
  4603. break;
  4604. }
  4605. case HTDDT_OVER:
  4606. {
  4607. // review chrisny: make function TreeView_InsertMarkHittest!!!!!
  4608. RECT rc;
  4609. TV_HITTESTINFO tvht;
  4610. HTREEITEM htiOver; // item to insert before or after.
  4611. BOOL fWasInserting = BOOLIFY(_fInserting);
  4612. BOOL fOldInsertBefore = BOOLIFY(_fInsertBefore);
  4613. TV_ITEM tvi;
  4614. PORDERITEM poi = NULL;
  4615. IDropTarget *pdtgt = NULL;
  4616. HRESULT hr;
  4617. LPITEMIDLIST pidl;
  4618. _fDragging = TRUE;
  4619. *pdwDropEffect = DROPEFFECT_NONE; // dropping from without.
  4620. tvht.pt = *ppt;
  4621. htiOver = TreeView_HitTest(_hwndTree, &tvht);
  4622. // if no hittest assume we are dropping on the evil root.
  4623. if (htiOver != NULL)
  4624. {
  4625. TreeView_GetItemRect(_hwndTree, (HTREEITEM)htiOver, &rc, TRUE);
  4626. tvi.mask = TVIF_STATE | TVIF_PARAM | TVIF_HANDLE;
  4627. tvi.stateMask = TVIS_EXPANDED;
  4628. tvi.hItem = (HTREEITEM)htiOver;
  4629. if (TreeView_GetItem(_hwndTree, &tvi))
  4630. poi = GetPoi(tvi.lParam);
  4631. if (poi == NULL)
  4632. {
  4633. ASSERT(FALSE);
  4634. return S_FALSE;
  4635. }
  4636. }
  4637. else if (_mode != MODE_NORMAL) //need parity with win2k Explorer band
  4638. {
  4639. htiOver = TVI_ROOT;
  4640. }
  4641. // NO DROPPY ON HISTORY
  4642. if (_mode & MODE_HISTORY)
  4643. {
  4644. *pdwId = (DWORD_PTR)(htiOver);
  4645. *pdwDropEffect = DROPEFFECT_NONE; // dropping from without.
  4646. return S_OK;
  4647. }
  4648. pidl = (poi == NULL) ? _pidlRoot : poi->pidl;
  4649. pidl = _CacheParentShellFolder(htiOver, pidl);
  4650. if (pidl)
  4651. {
  4652. // Is this the desktop pidl?
  4653. if (ILIsEmpty(pidl))
  4654. {
  4655. // Desktop's GetUIObject does not support the Empty pidl, so
  4656. // create the view object.
  4657. hr = _psfCache->CreateViewObject(_hwndTree, IID_PPV_ARG(IDropTarget, &pdtgt));
  4658. }
  4659. else
  4660. hr = _psfCache->GetUIObjectOf(_hwndTree, 1, (LPCITEMIDLIST *)&pidl, IID_PPV_ARG_NULL(IDropTarget, &pdtgt));
  4661. }
  4662. _fInserting = ((htiOver != TVI_ROOT) && ((ppt->y < (rc.top + iInsertThresh)
  4663. || (ppt->y > (rc.bottom - iInsertThresh))) || !pdtgt));
  4664. // review chrisny: do I need folderstart == folder over?
  4665. // If in normal mode, we never want to insert before, always _ON_...
  4666. if (_mode != MODE_NORMAL && _fInserting)
  4667. {
  4668. ASSERT(poi);
  4669. _iDragDest = poi->nOrder; // index of item within folder pdwId
  4670. if ((ppt->y < (rc.top + iInsertThresh)) || !pdtgt)
  4671. _fInsertBefore = TRUE;
  4672. else
  4673. {
  4674. ASSERT (ppt->y > (rc.bottom - iInsertThresh));
  4675. _fInsertBefore = FALSE;
  4676. }
  4677. if (_iDragSrc != -1)
  4678. *pdwDropEffect = DROPEFFECT_MOVE; // moving from within.
  4679. else
  4680. *pdwDropEffect = DROPEFFECT_NONE; // dropping from without.
  4681. // inserting, drop target is actually parent folder of this item
  4682. if (_fInsertBefore || ((htiOver != TVI_ROOT) && !(tvi.state & TVIS_EXPANDED)))
  4683. {
  4684. _htiDropInsert = TreeView_GetParent(_hwndTree, (HTREEITEM)htiOver);
  4685. }
  4686. else
  4687. _htiDropInsert = htiOver;
  4688. if (_htiDropInsert == NULL)
  4689. _htiDropInsert = TVI_ROOT;
  4690. *pdwId = (DWORD_PTR)(_htiDropInsert);
  4691. }
  4692. else
  4693. {
  4694. _htiDropInsert = htiOver;
  4695. *pdwId = (DWORD_PTR)(htiOver);
  4696. _iDragDest = -1; // no insertion point.
  4697. *pdwDropEffect = DROPEFFECT_NONE;
  4698. }
  4699. // if we're over the item we're dragging, don't allow drop here
  4700. if ((_htiDragging == htiOver) || (IsParentOfItem(_hwndTree, _htiDragging, htiOver)))
  4701. {
  4702. *pdwDropEffect = DROPEFFECT_NONE;
  4703. *pdwId = DDT_SENTINEL;
  4704. _fInserting = FALSE;
  4705. ATOMICRELEASE(pdtgt);
  4706. }
  4707. // update UI
  4708. if (_htiCur != (HTREEITEM)htiOver || fWasInserting != BOOLIFY(_fInserting) || fOldInsertBefore != BOOLIFY(_fInsertBefore))
  4709. {
  4710. // change in target
  4711. _dwLastTime = GetTickCount(); // keep track for auto-expanding the tree
  4712. DAD_ShowDragImage(FALSE);
  4713. if (_fInserting)
  4714. {
  4715. TraceMsg(TF_NSC, "NSCBand: drop insert now");
  4716. if (htiOver != TVI_ROOT)
  4717. {
  4718. if (_mode != MODE_NORMAL)
  4719. {
  4720. TreeView_SelectDropTarget(_hwndTree, NULL);
  4721. TreeView_SetInsertMark(_hwndTree, htiOver, !_fInsertBefore);
  4722. }
  4723. }
  4724. }
  4725. else
  4726. {
  4727. TraceMsg(TF_NSC, "NSCBand: drop select now");
  4728. if (_mode != MODE_NORMAL)
  4729. TreeView_SetInsertMark(_hwndTree, NULL, !_fInsertBefore);
  4730. if (htiOver != TVI_ROOT)
  4731. {
  4732. if (pdtgt)
  4733. {
  4734. TreeView_SelectDropTarget(_hwndTree, htiOver);
  4735. }
  4736. else if (_mode != MODE_NORMAL)
  4737. {
  4738. // We do not want to select the drop target in normal mode
  4739. // because it causes a weird flashing of some item unrelated
  4740. // to the drag and drop when the drop is not supported.
  4741. TreeView_SelectDropTarget(_hwndTree, NULL);
  4742. }
  4743. }
  4744. }
  4745. ::UpdateWindow(_hwndTree);
  4746. DAD_ShowDragImage(TRUE);
  4747. }
  4748. else
  4749. {
  4750. // No target change
  4751. // auto expand the tree
  4752. if (_htiCur)
  4753. {
  4754. DWORD dwNow = GetTickCount();
  4755. if ((dwNow - _dwLastTime) >= 1000)
  4756. {
  4757. _dwLastTime = dwNow;
  4758. DAD_ShowDragImage(FALSE);
  4759. _fAutoExpanding = TRUE;
  4760. if (_htiCur != TVI_ROOT)
  4761. TreeView_Expand(_hwndTree, _htiCur, TVE_EXPAND);
  4762. _fAutoExpanding = FALSE;
  4763. ::UpdateWindow(_hwndTree);
  4764. DAD_ShowDragImage(TRUE);
  4765. }
  4766. }
  4767. }
  4768. _htiCur = (HTREEITEM)htiOver;
  4769. ATOMICRELEASE(pdtgt);
  4770. }
  4771. break;
  4772. }
  4773. return S_OK;
  4774. }
  4775. HRESULT CNscTree::GetObjectDDT(DWORD_PTR dwId, REFIID riid, void **ppv)
  4776. {
  4777. HRESULT hr = S_FALSE;
  4778. if (dwId != DDT_SENTINEL)
  4779. {
  4780. LPCITEMIDLIST pidl = _CacheParentShellFolder((HTREEITEM)dwId, NULL);
  4781. if (pidl)
  4782. {
  4783. if (ILIsEmpty(pidl))
  4784. hr = _psfCache->CreateViewObject(_hwndTree, riid, ppv);
  4785. else
  4786. hr = _psfCache->GetUIObjectOf(_hwndTree, 1, &pidl, riid, NULL, ppv);
  4787. }
  4788. }
  4789. return hr;
  4790. }
  4791. HRESULT CNscTree::OnDropDDT(IDropTarget *pdt, IDataObject *pdtobj, DWORD * pgrfKeyState, POINTL pt, DWORD *pdwEffect)
  4792. {
  4793. HRESULT hr;
  4794. _fAsyncDrop = FALSE; //ASSUME
  4795. _fDropping = TRUE;
  4796. // move within same folder, else let Drop() handle it.
  4797. if (_iDragSrc >= 0)
  4798. {
  4799. if (_htiFolderStart == _htiDropInsert && _mode != MODE_NORMAL)
  4800. {
  4801. if (_iDragSrc != _iDragDest) // no moving needed
  4802. {
  4803. int iNewPos = _fInsertBefore ? (_iDragDest - 1) : _iDragDest;
  4804. if (_MoveNode(_iDragSrc, iNewPos, _pidlDrag))
  4805. {
  4806. TraceMsg(TF_NSC, "NSCBand: Reordering");
  4807. _fDropping = TRUE;
  4808. _Dropped();
  4809. // Remove this notify message immediately (so _fDropping is set
  4810. // and we'll ignore this event in above OnChange method)
  4811. //
  4812. _FlushNotifyMessages(_hwndTree);
  4813. _fDropping = FALSE;
  4814. }
  4815. Pidl_Set(&_pidlDrag, NULL);
  4816. }
  4817. DragLeave();
  4818. _htiCur = _htiFolderStart = NULL;
  4819. _htiDropInsert = (HTREEITEM)-1;
  4820. _fDragging = _fInserting = _fDropping = FALSE;
  4821. _iDragDest = -1;
  4822. hr = S_FALSE; // handled
  4823. }
  4824. else
  4825. {
  4826. hr = S_OK;
  4827. }
  4828. }
  4829. else
  4830. {
  4831. // the item will get created in SHNotifyCreate()
  4832. TraceMsg(TF_NSC, "NSCBand: Dropped and External Item");
  4833. BOOL fSafe = TRUE;
  4834. LPITEMIDLIST pidl;
  4835. if (SUCCEEDED(SHPidlFromDataObject(pdtobj, &pidl, NULL, 0)))
  4836. {
  4837. fSafe = IEIsLinkSafe(_hwndParent, pidl, ILS_ADDTOFAV);
  4838. ILFree(pidl);
  4839. }
  4840. if (fSafe)
  4841. {
  4842. _fAsyncDrop = TRUE;
  4843. hr = S_OK;
  4844. }
  4845. else
  4846. {
  4847. hr = S_FALSE;
  4848. }
  4849. }
  4850. TreeView_SetInsertMark(_hwndTree, NULL, !_fInsertBefore);
  4851. TreeView_SelectDropTarget(_hwndTree, NULL);
  4852. ILFree(_pidlDrag);
  4853. _pidlDrag = NULL;
  4854. return hr;
  4855. }
  4856. IStream * CNscTree::GetOrderStream(LPCITEMIDLIST pidl, DWORD grfMode)
  4857. {
  4858. // only do this for favorites
  4859. if (!ILIsEmpty(pidl) && (_mode & MODE_FAVORITES))
  4860. return OpenPidlOrderStream((LPCITEMIDLIST)CSIDL_FAVORITES, pidl, REG_SUBKEY_FAVORITESA, grfMode);
  4861. return NULL;
  4862. }
  4863. BOOL CNscTree::_MoveNode(int iDragSrc, int iNewPos, LPITEMIDLIST pidl)
  4864. {
  4865. HTREEITEM hti, htiAfter = TVI_LAST, htiDel = NULL;
  4866. // if we are not moving and not dropping directly on a folder with no insert.
  4867. if ((iDragSrc == iNewPos) && (iNewPos != -1))
  4868. return FALSE; // no need to move
  4869. int i = 0;
  4870. for (hti = TreeView_GetChild(_hwndTree, _htiDropInsert); hti; hti = TreeView_GetNextSibling(_hwndTree, hti), i++)
  4871. {
  4872. if (i == iDragSrc)
  4873. htiDel = hti; // save node to be deleted, can't deelete it while enumerating
  4874. // cuz the treeview will go down the tubes.
  4875. if (i == iNewPos)
  4876. htiAfter = hti;
  4877. }
  4878. if (iNewPos == -1) // must be the first item
  4879. htiAfter = TVI_FIRST;
  4880. // add before delete to handle add after deleteable item case.
  4881. _AddItemToTree(_htiDropInsert, pidl, I_CHILDRENCALLBACK, _iDragDest, htiAfter, FALSE);
  4882. if (htiDel)
  4883. TreeView_DeleteItem(_hwndTree, htiDel);
  4884. _PopulateOrderList(_htiDropInsert);
  4885. _fWeChangedOrder = TRUE;
  4886. return TRUE;
  4887. }
  4888. void CNscTree::_Dropped(void)
  4889. {
  4890. // Persist the new order out to the registry
  4891. LPITEMIDLIST pidl = _GetFullIDList(_htiDropInsert);
  4892. if (pidl)
  4893. {
  4894. IStream* pstm = GetOrderStream(pidl, STGM_WRITE | STGM_CREATE);
  4895. if (pstm)
  4896. {
  4897. if (_CacheShellFolder(_htiDropInsert))
  4898. {
  4899. #ifdef DEBUG
  4900. if (_hdpaOrd)
  4901. {
  4902. for (int i=0; i<DPA_GetPtrCount(_hdpaOrd); i++)
  4903. {
  4904. PORDERITEM poi = (PORDERITEM)DPA_GetPtr(_hdpaOrd, i);
  4905. if (poi)
  4906. {
  4907. ASSERTMSG(poi->nOrder >= 0, "nsc saving bogus order list nOrder (%d), get reljai", poi->nOrder);
  4908. }
  4909. }
  4910. }
  4911. #endif
  4912. OrderList_SaveToStream(pstm, _hdpaOrd, _psfCache);
  4913. // remember we are now ordered.
  4914. if (_htiDropInsert == TVI_ROOT)
  4915. {
  4916. _fOrdered = TRUE;
  4917. }
  4918. else
  4919. {
  4920. PORDERITEM poi = _GetTreeOrderItem(_htiDropInsert);
  4921. if (poi)
  4922. {
  4923. poi->lParam = (DWORD)FALSE;
  4924. }
  4925. }
  4926. // Notify everyone that the order changed
  4927. SHSendChangeMenuNotify(this, SHCNEE_ORDERCHANGED, 0, pidl);
  4928. _dwOrderSig++;
  4929. }
  4930. pstm->Release();
  4931. }
  4932. ILFree(pidl);
  4933. }
  4934. DPA_Destroy(_hdpaOrd);
  4935. _hdpaOrd = NULL;
  4936. _UpdateActiveBorder(_htiDropInsert);
  4937. }
  4938. CNscTree::CSelectionContextMenu::~CSelectionContextMenu()
  4939. {
  4940. ATOMICRELEASE(_pcmSelection);
  4941. ATOMICRELEASE(_pcm2Selection);
  4942. }
  4943. HRESULT CNscTree::CSelectionContextMenu::QueryInterface(REFIID riid, void **ppv)
  4944. {
  4945. static const QITAB qit[] = {
  4946. QITABENT(CNscTree::CSelectionContextMenu, IContextMenu2), // IID_IContextMenu2
  4947. QITABENTMULTI(CNscTree::CSelectionContextMenu, IContextMenu, IContextMenu2), // IID_IContextMenu
  4948. { 0 },
  4949. };
  4950. return QISearch(this, qit, riid, ppv);
  4951. }
  4952. ULONG CNscTree::CSelectionContextMenu::AddRef(void)
  4953. {
  4954. CComObject<CNscTree> *pnsc = IToClass(CComObject<CNscTree>, _scm, this);
  4955. _ulRefs++;
  4956. return pnsc->AddRef();
  4957. }
  4958. ULONG CNscTree::CSelectionContextMenu::Release(void)
  4959. {
  4960. CComObject<CNscTree> *pnsc = IToClass(CComObject<CNscTree>, _scm, this);
  4961. ASSERT(_ulRefs > 0);
  4962. _ulRefs--;
  4963. if (0 == _ulRefs)
  4964. {
  4965. ATOMICRELEASE(_pcmSelection);
  4966. ATOMICRELEASE(_pcm2Selection);
  4967. }
  4968. return pnsc->Release();
  4969. }
  4970. HRESULT CNscTree::CSelectionContextMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu,
  4971. UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  4972. {
  4973. if (NULL == _pcmSelection)
  4974. {
  4975. return E_FAIL;
  4976. }
  4977. else
  4978. {
  4979. return _pcmSelection->QueryContextMenu(hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
  4980. }
  4981. }
  4982. HRESULT CNscTree::CSelectionContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
  4983. {
  4984. HTREEITEM hti;
  4985. CNscTree* pnsc = IToClass(CNscTree, _scm, this);
  4986. UINT idCmd;
  4987. if (!HIWORD(pici->lpVerb))
  4988. {
  4989. idCmd = LOWORD(pici->lpVerb);
  4990. }
  4991. else
  4992. {
  4993. return E_FAIL;
  4994. }
  4995. HRESULT hr = pnsc->_QuerySelection(NULL, &hti);
  4996. if (SUCCEEDED(hr))
  4997. {
  4998. pnsc->_ApplyCmd(hti, _pcmSelection, idCmd);
  4999. }
  5000. return hr;
  5001. }
  5002. HRESULT CNscTree::CSelectionContextMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
  5003. {
  5004. if (NULL == _pcm2Selection)
  5005. {
  5006. return E_FAIL;
  5007. }
  5008. else
  5009. {
  5010. // HACK alert. Work around bug in win95 user code for WM_DRAWITEM that sign extends
  5011. // itemID
  5012. if (!g_fRunningOnNT && WM_DRAWITEM == uMsg)
  5013. {
  5014. LPDRAWITEMSTRUCT lpDraw = (LPDRAWITEMSTRUCT)lParam;
  5015. if (0xFFFF0000 == (lpDraw->itemID & 0xFFFF0000) &&
  5016. (lpDraw->itemID & 0xFFFF) >= FCIDM_BROWSERFIRST &&
  5017. (lpDraw->itemID & 0xFFFF) <= FCIDM_BROWSERLAST)
  5018. {
  5019. lpDraw->itemID = lpDraw->itemID & 0xFFFF;
  5020. }
  5021. }
  5022. return _pcm2Selection->HandleMenuMsg(uMsg,wParam,lParam);
  5023. }
  5024. }
  5025. IContextMenu *CNscTree::CSelectionContextMenu::_QuerySelection()
  5026. {
  5027. CNscTree* pnsc = IToClass(CNscTree, _scm, this);
  5028. ATOMICRELEASE(_pcmSelection);
  5029. ATOMICRELEASE(_pcm2Selection);
  5030. pnsc->_QuerySelection(&_pcmSelection, NULL);
  5031. if (_pcmSelection)
  5032. {
  5033. _pcmSelection->QueryInterface(IID_PPV_ARG(IContextMenu2, &_pcm2Selection));
  5034. AddRef();
  5035. return SAFECAST(this, IContextMenu*);
  5036. }
  5037. return NULL;
  5038. }
  5039. LRESULT CALLBACK CNscTree::s_SubClassTreeWndProc(
  5040. HWND hwnd, UINT uMsg,
  5041. WPARAM wParam, LPARAM lParam,
  5042. UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
  5043. {
  5044. CNscTree* pns = (CNscTree*)dwRefData;
  5045. ASSERT(pns);
  5046. if (pns == NULL)
  5047. return 0;
  5048. ULONG_PTR cookie = 0;
  5049. // FUSION: When nsc calls out to 3rd party code we want it to use
  5050. // the process default context. This means that the 3rd party code will get
  5051. // v5 in the explorer process. However, if shell32 is hosted in a v6 process,
  5052. // then the 3rd party code will still get v6.
  5053. // Future enhancements to this codepath may include using the fusion manifest
  5054. // tab <noinherit> which basically surplants the activat(null) in the following
  5055. // codepath. This disables the automatic activation from user32 for the duration
  5056. // of this wndproc, essentially doing this null push.
  5057. NT5_ActivateActCtx(NULL, &cookie);
  5058. LRESULT lres = pns->_SubClassTreeWndProc(hwnd, uMsg, wParam, lParam);
  5059. if (cookie != 0)
  5060. NT5_DeactivateActCtx(cookie);
  5061. return lres;
  5062. }
  5063. LRESULT CNscTree::_SubClassTreeWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  5064. {
  5065. LRESULT lres = 0;
  5066. BOOL fCallDefWndProc = TRUE;
  5067. switch (uMsg)
  5068. {
  5069. case WM_COMMAND:
  5070. lres = _OnCommand(wParam, lParam);
  5071. break;
  5072. case WM_SIZE:
  5073. // if the width changes, we need to invalidate to redraw the ...'s at the end of the lines
  5074. if (GET_X_LPARAM(lParam) != _cxOldWidth) {
  5075. //FEATURE: be a bit more clever and only inval the right part where the ... can be
  5076. ::InvalidateRect(_hwndTree, NULL, FALSE);
  5077. _cxOldWidth = GET_X_LPARAM(lParam);
  5078. }
  5079. break;
  5080. case WM_CONTEXTMENU:
  5081. _OnContextMenu(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  5082. return TRUE;
  5083. break;
  5084. case WM_INITMENUPOPUP:
  5085. case WM_MEASUREITEM:
  5086. case WM_DRAWITEM:
  5087. if (_pcmSendTo)
  5088. {
  5089. _pcmSendTo->HandleMenuMsg(uMsg, wParam, lParam);
  5090. return TRUE;
  5091. }
  5092. break;
  5093. case WM_NSCUPDATEICONOVERLAY:
  5094. {
  5095. NSC_OVERLAYCALLBACKINFO noci = {(DWORD) (lParam & 0x0FFFFFFF),
  5096. (DWORD) ((lParam & 0xF0000000) >> 28) };
  5097. // make sure the magic numbers match
  5098. if (noci.nMagic == _bSynchId)
  5099. {
  5100. TVITEM tvi;
  5101. tvi.mask = TVIF_STATE;
  5102. tvi.stateMask = TVIS_OVERLAYMASK;
  5103. tvi.state = 0;
  5104. tvi.hItem = (HTREEITEM)wParam;
  5105. // This can fail if the item was moved before the async icon
  5106. // extraction finished for that item.
  5107. if (TreeView_GetItem(_hwndTree, &tvi))
  5108. {
  5109. tvi.state = INDEXTOOVERLAYMASK(noci.iOverlayIndex);
  5110. TreeView_SetItem(_hwndTree, &tvi);
  5111. }
  5112. }
  5113. }
  5114. break;
  5115. case WM_NSCUPDATEICONINFO:
  5116. {
  5117. NSC_ICONCALLBACKINFO nici = {(DWORD) (lParam&0x00000FFF),
  5118. (DWORD) ((lParam&0x00FFF000) >> 12),
  5119. (DWORD) ((lParam&0x0F000000) >> 24),
  5120. (DWORD) ((lParam&0xF0000000) >> 28) };
  5121. // make sure the magic numbers match
  5122. if (nici.nMagic == _bSynchId)
  5123. {
  5124. TVITEM tvi;
  5125. tvi.mask = TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
  5126. tvi.hItem = (HTREEITEM)wParam;
  5127. // This can fail if the item was moved before the async icon
  5128. // extraction finished for that item.
  5129. if (TreeView_GetItem(_hwndTree, &tvi))
  5130. {
  5131. ITEMINFO* pii = GetPii(tvi.lParam);
  5132. pii->fGreyed = BOOLIFY(nici.nFlags & NSCICON_GREYED);
  5133. pii->fPinned = BOOLIFY(nici.nFlags & NSCICON_PINNED);
  5134. pii->fDontRefetch = BOOLIFY(nici.nFlags & NSCICON_DONTREFETCH);
  5135. tvi.iImage = nici.iIcon;
  5136. tvi.iSelectedImage = nici.iOpenIcon;
  5137. TreeView_SetItem(_hwndTree, &tvi);
  5138. }
  5139. }
  5140. }
  5141. break;
  5142. case WM_NSCBACKGROUNDENUMDONE:
  5143. {
  5144. if (_fShouldShowAppStartCursor)
  5145. {
  5146. // Restore cursor now
  5147. _fShouldShowAppStartCursor = FALSE;
  5148. SetCursor(LoadCursor(NULL, IDC_ARROW));
  5149. }
  5150. NSC_BKGDENUMDONEDATA * pbedd;
  5151. do
  5152. {
  5153. EnterCriticalSection(&_csBackgroundData);
  5154. // Extract the first element of the list
  5155. pbedd = _pbeddList;
  5156. if (pbedd)
  5157. {
  5158. _pbeddList = pbedd->pNext;
  5159. }
  5160. LeaveCriticalSection(&_csBackgroundData);
  5161. if (pbedd)
  5162. {
  5163. pbedd->pNext = NULL;
  5164. _EnumBackgroundDone(pbedd);
  5165. }
  5166. } while (pbedd);
  5167. }
  5168. break;
  5169. // UGLY: Win95/NT4 shell DefView code sends this msg and does not deal
  5170. // with the failure case. other ISVs do the same so this needs to stay forever
  5171. case CWM_GETISHELLBROWSER:
  5172. return (LRESULT)SAFECAST(this, IShellBrowser*); // not ref counted!
  5173. case WM_TIMER:
  5174. if (wParam == IDT_SELECTION)
  5175. {
  5176. ::KillTimer(_hwndTree, IDT_SELECTION);
  5177. _OnSetSelection();
  5178. }
  5179. break;
  5180. case WM_HELP:
  5181. {
  5182. // Let controls provide thier own help (organize favorites). The default help
  5183. // also doesn't make sence for history (really need separate help id for history)
  5184. if (!(_mode & (MODE_CONTROL | MODE_HISTORY)))
  5185. {
  5186. if (_mode & MODE_FAVORITES)
  5187. {
  5188. const static DWORD aBrowseHelpIDs[] =
  5189. { // Context Help IDs
  5190. ID_CONTROL, IDH_ORGFAVS_LIST,
  5191. 0, 0
  5192. };
  5193. ::WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, c_szHelpFile, HELP_WM_HELP, (ULONG_PTR)(LPTSTR) aBrowseHelpIDs);
  5194. }
  5195. else
  5196. {
  5197. // default help
  5198. const static DWORD aBrowseHelpIDs[] =
  5199. { // Context Help IDs
  5200. ID_CONTROL, IDH_BROWSELIST,
  5201. 0, 0
  5202. };
  5203. ::WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR)(LPTSTR) aBrowseHelpIDs);
  5204. }
  5205. }
  5206. }
  5207. break;
  5208. case WM_SYSCOLORCHANGE:
  5209. case WM_WININICHANGE:
  5210. // _HandleWinIniChange does an item height calculation that
  5211. // depends on treeview having computed the default item height
  5212. // already. So we need to let treeview handle the settings
  5213. // change before calling _HandleWinIniChange. Also, we need
  5214. // to reset the height to default so that treeview will
  5215. // calculate a new default.
  5216. TreeView_SetItemHeight(hwnd, -1);
  5217. lres = DefSubclassProc(hwnd, uMsg, wParam, lParam);
  5218. _HandleWinIniChange();
  5219. break;
  5220. case WM_KEYDOWN:
  5221. // Only do this when the CTRL key is not down
  5222. if (GetKeyState(VK_CONTROL) >= 0)
  5223. {
  5224. if (wParam == VK_MULTIPLY)
  5225. {
  5226. // We set _pidlNavigatingTo to NULL here to ensure that we will be doing full expands.
  5227. // When _pidlNavigatingTo is non null, we are doing partial expands by default, which is not
  5228. // what we want here.
  5229. Pidl_Set(&_pidlNavigatingTo, NULL);
  5230. _uDepth = (UINT)-1; // to recursive expand all the way to the end
  5231. lres = DefSubclassProc(hwnd, uMsg, wParam, lParam);
  5232. _uDepth = 0;
  5233. fCallDefWndProc = FALSE; // Don't call DefSubclassProc again.
  5234. }
  5235. }
  5236. break;
  5237. default:
  5238. break;
  5239. }
  5240. if (fCallDefWndProc && lres == 0)
  5241. lres = DefSubclassProc(hwnd, uMsg, wParam, lParam);
  5242. return lres;
  5243. }
  5244. HRESULT CNscTree::_OnPaletteChanged(WPARAM wParam, LPARAM lParam)
  5245. {
  5246. // forward this to our child view by invalidating their window (they should never realize their palette
  5247. // in the foreground so they don't need the message parameters.) ...
  5248. RECT rc;
  5249. ::GetClientRect(_hwndTree, &rc);
  5250. ::InvalidateRect(_hwndTree, &rc, FALSE);
  5251. return NOERROR;
  5252. }
  5253. void CNscTree::_InvalidateImageIndex(HTREEITEM hItem, int iImage)
  5254. {
  5255. HTREEITEM hChild;
  5256. TV_ITEM tvi;
  5257. if (hItem)
  5258. {
  5259. tvi.mask = TVIF_SELECTEDIMAGE | TVIF_IMAGE;
  5260. tvi.hItem = hItem;
  5261. TreeView_GetItem(_hwndTree, &tvi);
  5262. if (iImage == -1 || tvi.iImage == iImage || tvi.iSelectedImage == iImage)
  5263. _TreeInvalidateItemInfo(hItem, 0);
  5264. }
  5265. hChild = TreeView_GetChild(_hwndTree, hItem);
  5266. if (!hChild)
  5267. return;
  5268. for (; hChild; hChild = TreeView_GetNextSibling(_hwndTree, hChild))
  5269. _InvalidateImageIndex(hChild, iImage);
  5270. }
  5271. void CNscTree::_TreeInvalidateItemInfo(HTREEITEM hItem, UINT mask)
  5272. {
  5273. TV_ITEM tvi;
  5274. tvi.mask = mask | TVIF_CHILDREN | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE;
  5275. tvi.stateMask = TVIS_OVERLAYMASK;
  5276. tvi.state = 0;
  5277. tvi.hItem = hItem;
  5278. tvi.cChildren = I_CHILDRENCALLBACK;
  5279. tvi.iImage = I_IMAGECALLBACK;
  5280. tvi.iSelectedImage = I_IMAGECALLBACK;
  5281. tvi.pszText = LPSTR_TEXTCALLBACK;
  5282. TreeView_SetItem(_hwndTree, &tvi);
  5283. }
  5284. void CNscTree::_DrawActiveBorder(HDC hdc, LPRECT prc)
  5285. {
  5286. MoveToEx(hdc, prc->left, prc->top, NULL);
  5287. LineTo(hdc, prc->right, prc->bottom);
  5288. }
  5289. #define DXLEFT 8
  5290. #define MAGICINDENT 3
  5291. void CNscTree::_DrawIcon(HTREEITEM hti, HDC hdc, int iLevel, RECT *prc, DWORD dwFlags)
  5292. {
  5293. HIMAGELIST himl = TreeView_GetImageList(_hwndTree, TVSIL_NORMAL);
  5294. TV_ITEM tvi;
  5295. int dx, dy, x, y;
  5296. tvi.mask = TVIF_SELECTEDIMAGE | TVIF_IMAGE | TVIF_HANDLE;
  5297. tvi.hItem = hti;
  5298. if (TreeView_GetItem(_hwndTree, &tvi))
  5299. {
  5300. ImageList_GetIconSize(himl, &dx, &dy);
  5301. if (!_fStartingDrag)
  5302. x = DXLEFT;
  5303. else
  5304. x = 0;
  5305. x += (iLevel * TreeView_GetIndent(_hwndTree)); // - ((dwFlags & DIFOLDEROPEN) ? 1 : 0);
  5306. y = prc->top + (((prc->bottom - prc->top) - dy) >> 1);
  5307. int iImage = (dwFlags & DIFOLDEROPEN) ? tvi.iSelectedImage : tvi.iImage;
  5308. ImageList_DrawEx(himl, iImage, hdc, x, y, 0, 0, CLR_NONE, GetSysColor(COLOR_WINDOW), (dwFlags & DIGREYED) ? ILD_BLEND50 : ILD_TRANSPARENT);
  5309. if (dwFlags & DIPINNED)
  5310. {
  5311. ASSERT(_hicoPinned);
  5312. DrawIconEx(hdc, x, y, _hicoPinned, 16, 16, 0, NULL, DI_NORMAL);
  5313. }
  5314. }
  5315. return;
  5316. }
  5317. #define TreeView_GetFont(hwnd) (HFONT)::SendMessage(hwnd, WM_GETFONT, 0, 0)
  5318. void CNscTree::_DrawItem(HTREEITEM hti, TCHAR * psz, HDC hdc
  5319. , LPRECT prc, DWORD dwFlags, int iLevel, COLORREF clrbk, COLORREF clrtxt)
  5320. {
  5321. SIZE size;
  5322. HIMAGELIST himl = TreeView_GetImageList(_hwndTree, TVSIL_NORMAL);
  5323. HFONT hfont = NULL;
  5324. HFONT hfontOld = NULL;
  5325. int x, y, dx, dy;
  5326. LOGFONT lf;
  5327. COLORREF clrGreyed = GetSysColor(COLOR_BTNSHADOW);
  5328. if ((dwFlags & DIGREYED) && (clrbk != clrGreyed))
  5329. {
  5330. clrtxt = clrGreyed;
  5331. }
  5332. // For the history and favorites bars, we use the default
  5333. // font (for UI consistency with the folders bar).
  5334. if (_mode != MODE_FAVORITES && _mode != MODE_HISTORY)
  5335. hfont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
  5336. if ((dwFlags & DIHOT) && !(dwFlags & DIFOLDER))
  5337. {
  5338. if (!hfont)
  5339. hfont = TreeView_GetFont(_hwndTree);
  5340. // create the underline font
  5341. GetObject(hfont, sizeof(lf), &lf);
  5342. lf.lfUnderline = TRUE;
  5343. hfont = CreateFontIndirect(&lf);
  5344. }
  5345. if (hfont)
  5346. hfontOld = (HFONT)SelectObject(hdc, hfont);
  5347. GetTextExtentPoint32(hdc, psz, lstrlen(psz), &size);
  5348. if (himl)
  5349. ImageList_GetIconSize(himl, &dx, &dy);
  5350. else
  5351. {
  5352. dx = 0;
  5353. dy = 0;
  5354. }
  5355. x = prc->left + ((dwFlags & DIICON) ? (iLevel * TreeView_GetIndent(_hwndTree) + dx + DXLEFT + MAGICINDENT) : DXLEFT);
  5356. if (_fStartingDrag)
  5357. x -= DXLEFT;
  5358. y = prc->top + (((prc->bottom - prc->top) - size.cy) >> 1);
  5359. UINT eto = ETO_CLIPPED;
  5360. RECT rc;
  5361. rc.left = prc->left + 2;
  5362. rc.top = prc->top;
  5363. rc.bottom = prc->bottom;
  5364. rc.right = prc->right - 2;
  5365. SetBkColor(hdc, clrbk);
  5366. eto |= ETO_OPAQUE;
  5367. ExtTextOut(hdc, 0, 0, eto, &rc, NULL, 0, NULL);
  5368. SetTextColor(hdc, clrtxt);
  5369. rc.left = x;
  5370. rc.top = y;
  5371. rc.bottom = rc.top + size.cy;
  5372. UINT uFormat = DT_VCENTER | DT_END_ELLIPSIS | DT_NOPREFIX;
  5373. if (dwFlags & DIRIGHT)
  5374. uFormat |= DT_RIGHT;
  5375. if (dwFlags & DIRTLREADING)
  5376. uFormat |= DT_RTLREADING;
  5377. DrawTextWrap(hdc, psz, lstrlen(psz), &rc, uFormat);
  5378. if (dwFlags & DIICON)
  5379. _DrawIcon(hti, hdc, iLevel, prc, dwFlags);
  5380. if (hfontOld)
  5381. SelectObject(hdc, hfontOld);
  5382. if (dwFlags & DIACTIVEBORDER)
  5383. {
  5384. if (dwFlags & DIFIRST)
  5385. {
  5386. rc = *prc;
  5387. rc.left += 2;
  5388. rc.bottom = rc.top + 1;
  5389. rc.right -= 2;
  5390. SHFillRectClr(hdc, &rc, GetSysColor(COLOR_BTNSHADOW));
  5391. }
  5392. if (dwFlags & DISUBITEM)
  5393. {
  5394. rc = *prc;
  5395. rc.left += 2;
  5396. rc.right = rc.left + 1;
  5397. SHFillRectClr(hdc, &rc, GetSysColor(COLOR_BTNSHADOW));
  5398. rc.right = prc->right - 2;
  5399. rc.left = rc.right - 1;
  5400. SHFillRectClr(hdc, &rc, GetSysColor(COLOR_BTNSHADOW));
  5401. }
  5402. if (dwFlags & DILAST)
  5403. {
  5404. rc = *prc;
  5405. rc.left += 2;
  5406. rc.top = rc.bottom - 1;
  5407. rc.right -= 2;
  5408. SHFillRectClr(hdc, &rc, GetSysColor(COLOR_BTNSHADOW));
  5409. }
  5410. }
  5411. #if 0
  5412. //focus is currently shown by drawing the selection with a different color
  5413. // (in default scheme, it's blue when has focus, gray when not)
  5414. if (dwFlags & DIFOCUSRECT)
  5415. {
  5416. rc = *prc;
  5417. InflateRect(&rc, -1, -1);
  5418. DrawFocusRect(hdc, &rc);
  5419. }
  5420. #endif
  5421. if (hfont)
  5422. DeleteObject(hfont);
  5423. }
  5424. //+-------------------------------------------------------------------------
  5425. // If going online, ungreys all items that were unavailable. If going
  5426. // offline, refreshes all items to see if they are still available.
  5427. //--------------------------------------------------------------------------
  5428. void CNscTree::_OnSHNotifyOnlineChange(HTREEITEM htiRoot, BOOL fGoingOnline)
  5429. {
  5430. HTREEITEM hItem;
  5431. for (hItem = TreeView_GetChild(_hwndTree, htiRoot); hItem
  5432. ; hItem = TreeView_GetNextSibling(_hwndTree, hItem))
  5433. {
  5434. ITEMINFO *pii = _GetTreeItemInfo(hItem);
  5435. if (pii)
  5436. {
  5437. if (fGoingOnline)
  5438. {
  5439. // Going online, if previously greyed then ungrey it
  5440. if (pii->fGreyed)
  5441. {
  5442. pii->fGreyed = FALSE;
  5443. _UpdateItemDisplayInfo(hItem);
  5444. }
  5445. }
  5446. else
  5447. {
  5448. // Recheck each item to see if they should be greyed
  5449. if (pii->fFetched && !pii->fDontRefetch)
  5450. {
  5451. pii->fFetched = FALSE;
  5452. _UpdateItemDisplayInfo(hItem);
  5453. }
  5454. }
  5455. }
  5456. // Inform children too
  5457. _OnSHNotifyOnlineChange(hItem, fGoingOnline);
  5458. }
  5459. }
  5460. //+-------------------------------------------------------------------------
  5461. // Force items to recheck to see if the should be pinned or greyed
  5462. //--------------------------------------------------------------------------
  5463. void CNscTree::_OnSHNotifyCacheChange
  5464. (
  5465. HTREEITEM htiRoot, // recurse through all children
  5466. DWORD_PTR dwFlags // CACHE_NOTIFY_* flags
  5467. )
  5468. {
  5469. HTREEITEM hItem;
  5470. for (hItem = TreeView_GetChild(_hwndTree, htiRoot); hItem
  5471. ; hItem = TreeView_GetNextSibling(_hwndTree, hItem))
  5472. {
  5473. ITEMINFO *pii = _GetTreeItemInfo(hItem);
  5474. if (pii)
  5475. {
  5476. // If we have cached info for this item, refresh it if it's state may have toggled
  5477. if ((pii->fFetched && !pii->fDontRefetch) &&
  5478. ((pii->fGreyed && (dwFlags & CACHE_NOTIFY_ADD_URL)) ||
  5479. // We only need to check ungreyed items for changes to the
  5480. // stickey bit in the cache!
  5481. (!pii->fGreyed &&
  5482. ((dwFlags & (CACHE_NOTIFY_DELETE_URL | CACHE_NOTIFY_DELETE_ALL))) ||
  5483. (!pii->fPinned && (dwFlags & CACHE_NOTIFY_URL_SET_STICKY)) ||
  5484. (pii->fPinned && (dwFlags & CACHE_NOTIFY_URL_UNSET_STICKY))
  5485. )
  5486. ))
  5487. {
  5488. pii->fFetched = FALSE;
  5489. _UpdateItemDisplayInfo(hItem);
  5490. }
  5491. }
  5492. // Do it's children too
  5493. _OnSHNotifyCacheChange(hItem, dwFlags);
  5494. }
  5495. }
  5496. //
  5497. // Calls the appropriate routine in shdocvw to favorites import or export on
  5498. // the currently selected item
  5499. //
  5500. HRESULT CNscTree::DoImportOrExport(BOOL fImport)
  5501. {
  5502. HTREEITEM htiSelected = TreeView_GetSelection(_hwndTree);
  5503. LPITEMIDLIST pidl = _GetFullIDList(htiSelected);
  5504. if (pidl)
  5505. {
  5506. //
  5507. // If current selection is not a folder get the parent pidl
  5508. //
  5509. if (!ILIsFileSysFolder(pidl))
  5510. ILRemoveLastID(pidl);
  5511. //
  5512. // Create the actual routine in shdocvw to do the import/export work
  5513. //
  5514. IShellUIHelper *pShellUIHelper;
  5515. HRESULT hr = CoCreateInstance(CLSID_ShellUIHelper, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellUIHelper, &pShellUIHelper));
  5516. if (SUCCEEDED(hr))
  5517. {
  5518. VARIANT_BOOL vbImport = fImport ? VARIANT_TRUE : VARIANT_FALSE;
  5519. WCHAR wszPath[MAX_PATH];
  5520. SHGetPathFromIDListW(pidl, wszPath);
  5521. hr = pShellUIHelper->ImportExportFavorites(vbImport, wszPath);
  5522. if (SUCCEEDED(hr) && fImport)
  5523. {
  5524. //
  5525. // Successfully imported favorites so need to update view
  5526. // FEATURE ie5 24973 - flicker alert, should optimize to just redraw selected
  5527. //
  5528. Refresh();
  5529. //TreeView_SelectItem(_hwndTree, htiSelected);
  5530. }
  5531. pShellUIHelper->Release();
  5532. }
  5533. ILFree(pidl);
  5534. }
  5535. return S_OK;
  5536. }
  5537. HRESULT CNscTree::GetSelectedItemName(LPWSTR pszName, DWORD cchName)
  5538. {
  5539. HRESULT hr = E_FAIL;
  5540. TCHAR szPath[MAX_PATH];
  5541. TV_ITEM tvi;
  5542. tvi.hItem = TreeView_GetSelection(_hwndTree);
  5543. if (tvi.hItem != NULL)
  5544. {
  5545. tvi.mask = TVIF_HANDLE | TVIF_TEXT;
  5546. tvi.pszText = szPath;
  5547. tvi.cchTextMax = ARRAYSIZE(szPath);
  5548. if (TreeView_GetItem(_hwndTree, &tvi))
  5549. {
  5550. SHTCharToUnicode(szPath, pszName, cchName);
  5551. hr = S_OK;
  5552. }
  5553. }
  5554. return hr;
  5555. }
  5556. HRESULT CNscTree::BindToSelectedItemParent(REFIID riid, void **ppv, LPITEMIDLIST *ppidl)
  5557. {
  5558. HRESULT hr = E_FAIL;
  5559. if (!_fClosing)
  5560. {
  5561. LPCITEMIDLIST pidlItem = _CacheParentShellFolder(TreeView_GetSelection(_hwndTree), NULL);
  5562. if (pidlItem)
  5563. {
  5564. hr = _psfCache->QueryInterface(riid, ppv);
  5565. if (SUCCEEDED(hr) && ppidl)
  5566. {
  5567. *ppidl = ILClone(pidlItem);
  5568. if (*ppidl == NULL)
  5569. {
  5570. hr = E_OUTOFMEMORY;
  5571. ((IUnknown *)*ppv)->Release();
  5572. *ppv = NULL;
  5573. }
  5574. }
  5575. }
  5576. }
  5577. return hr;
  5578. }
  5579. // takes ownership of pidl
  5580. HRESULT CNscTree::RightPaneNavigationStarted(LPITEMIDLIST pidl)
  5581. {
  5582. _fExpandNavigateTo = FALSE;
  5583. _fNavigationFinished = FALSE;
  5584. Pidl_Set(&_pidlNavigatingTo, pidl);
  5585. return S_OK;
  5586. }
  5587. // takes ownership of pidl
  5588. HRESULT CNscTree::RightPaneNavigationFinished(LPITEMIDLIST pidl)
  5589. {
  5590. HRESULT hr = S_OK;
  5591. _fNavigationFinished = TRUE;
  5592. if (_fExpandNavigateTo)
  5593. {
  5594. _fExpandNavigateTo = FALSE; // only do this once
  5595. hr = E_OUTOFMEMORY;
  5596. HDPA hdpa = DPA_Create(2);
  5597. if (hdpa)
  5598. {
  5599. IDVGetEnum *pdvge; // private defview interface
  5600. hr = IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IDVGetEnum, &pdvge));
  5601. if (SUCCEEDED(hr))
  5602. {
  5603. HTREEITEM hti = _FindFromRoot(NULL, pidl);
  5604. // Try to find the tree item using the pidl
  5605. if (hti)
  5606. {
  5607. IShellFolder* psf;
  5608. hr = IEBindToObject(pidl, &psf);
  5609. if (S_OK == hr)
  5610. {
  5611. ITEMINFO *pii = _GetTreeItemInfo(hti);
  5612. DWORD grfFlags;
  5613. _GetEnumFlags(psf, pidl, &grfFlags, NULL);
  5614. IEnumIDList *penum;
  5615. hr = pdvge->CreateEnumIDListFromContents(pidl, grfFlags, &penum);
  5616. if (S_OK == hr)
  5617. {
  5618. ULONG celt;
  5619. LPITEMIDLIST pidlTemp;
  5620. while (S_OK == penum->Next(1, &pidlTemp, &celt))
  5621. {
  5622. if (!OrderList_Append(hdpa, pidlTemp, -1))
  5623. {
  5624. hr = E_OUTOFMEMORY;
  5625. ILFree(pidlTemp);
  5626. break;
  5627. }
  5628. }
  5629. penum->Release();
  5630. }
  5631. if (hr == S_OK)
  5632. {
  5633. ORDERINFO oinfo;
  5634. oinfo.psf = psf;
  5635. oinfo.dwSortBy = OI_SORTBYNAME; // merge depends on by name.
  5636. DPA_Sort(hdpa, OrderItem_Compare, (LPARAM)&oinfo);
  5637. OrderList_Reorder(hdpa);
  5638. LPITEMIDLIST pidlExpClone = ILClone(_pidlExpandingTo); // NULL is OK
  5639. s_NscEnumCallback(this, pidl, (UINT_PTR)hti, pii->dwSig, hdpa, pidlExpClone, _dwOrderSig, 0, FALSE, FALSE);
  5640. hdpa = NULL;
  5641. pidl = NULL;
  5642. }
  5643. psf->Release();
  5644. }
  5645. }
  5646. pdvge->Release();
  5647. }
  5648. }
  5649. if (hr != S_OK)
  5650. {
  5651. if (hdpa)
  5652. OrderList_Destroy(&hdpa, TRUE); // calls DPA_Destroy(hdpa)
  5653. if (pidl)
  5654. {
  5655. HTREEITEM hti = _FindFromRoot(NULL, pidl);
  5656. if (hti)
  5657. {
  5658. BOOL fOrdered;
  5659. hr = _StartBackgroundEnum(hti, pidl, &fOrdered, FALSE);
  5660. }
  5661. }
  5662. }
  5663. }
  5664. ILFree(pidl);
  5665. return hr;
  5666. }
  5667. HRESULT CNscTree::MoveSelectionTo(void)
  5668. {
  5669. return MoveItemsIntoFolder(::GetParent(_hwndParent)) ? S_OK : S_FALSE;
  5670. }
  5671. BOOL CNscTree::MoveItemsIntoFolder(HWND hwndParent)
  5672. {
  5673. BOOL fSuccess = FALSE;
  5674. BROWSEINFO browse = {0};
  5675. TCHAR szDisplayName[MAX_PATH];
  5676. TCHAR szInstructionString[MAX_PATH];
  5677. LPITEMIDLIST pidlDest = NULL, pidlSelected = NULL;
  5678. HTREEITEM htiSelected = NULL;
  5679. //Initialize the BROWSEINFO struct.
  5680. browse.pidlRoot = ILClone(_pidlRoot);
  5681. if (!browse.pidlRoot)
  5682. return FALSE;
  5683. htiSelected = TreeView_GetSelection(_hwndTree);
  5684. pidlSelected = _GetFullIDList(htiSelected);
  5685. if (!pidlSelected)
  5686. {
  5687. ILFree((LPITEMIDLIST)browse.pidlRoot);
  5688. return FALSE;
  5689. }
  5690. MLLoadShellLangString(IDS_FAVORITEBROWSE, szInstructionString, ARRAYSIZE(szInstructionString));
  5691. browse.pszDisplayName = szDisplayName;
  5692. browse.hwndOwner = hwndParent;
  5693. browse.lpszTitle = szInstructionString;
  5694. browse.ulFlags = BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;
  5695. browse.lpfn = NULL;
  5696. browse.lParam = 0;
  5697. browse.iImage = 0;
  5698. pidlDest = SHBrowseForFolder(&browse);
  5699. if (pidlDest)
  5700. {
  5701. TCHAR szFrom[MAX_PATH+1]; // +1 for double null
  5702. TCHAR szDest[MAX_PATH+1];
  5703. SHGetPathFromIDList(pidlDest, szDest);
  5704. SHGetPathFromIDList(pidlSelected, szFrom);
  5705. ASSERT(szDest[0]); // must be a file system thing...
  5706. ASSERT(szFrom[0]);
  5707. szDest[lstrlen(szDest) + 1] = 0; // double null
  5708. szFrom[lstrlen(szFrom) + 1] = 0; // double null
  5709. SHFILEOPSTRUCT shop = {hwndParent, FO_MOVE, szFrom, szDest, 0, };
  5710. SHFileOperation(&shop);
  5711. fSuccess = TRUE;
  5712. ILFree(pidlDest);
  5713. }
  5714. ILFree((LPITEMIDLIST)browse.pidlRoot);
  5715. ILFree(pidlSelected);
  5716. return fSuccess;
  5717. }
  5718. // the following guid goo and IsChannelFolder are mostly lifted from cdfview
  5719. #define GUID_STR_LEN 80
  5720. const GUID CLSID_CDFINI = {0xf3aa0dc0, 0x9cc8, 0x11d0, {0xa5, 0x99, 0x0, 0xc0, 0x4f, 0xd6, 0x44, 0x34}};
  5721. // {f3aa0dc0-9cc8-11d0-a599-00c04fd64434}
  5722. // REARCHITECT: total hack. looks into the desktop.ini for this guy
  5723. //
  5724. // pwzChannelURL is assumed to be INTERNET_MAX_URL_LENGTH
  5725. BOOL IsChannelFolder(LPCWSTR pwzPath, LPWSTR pwzChannelURL)
  5726. {
  5727. ASSERT(pwzPath);
  5728. BOOL fRet = FALSE;
  5729. WCHAR wzFolderGUID[GUID_STR_LEN];
  5730. WCHAR wzIniFile[MAX_PATH];
  5731. if (!PathCombineW(wzIniFile, pwzPath, L"desktop.ini"))
  5732. return FALSE;
  5733. if (GetPrivateProfileString(L".ShellClassInfo", L"CLSID", L"", wzFolderGUID, ARRAYSIZE(wzFolderGUID), wzIniFile))
  5734. {
  5735. WCHAR wzChannelGUID[GUID_STR_LEN];
  5736. //it's only a channel if it's got the right guid and an url
  5737. if (SHStringFromGUID(CLSID_CDFINI, wzChannelGUID, ARRAYSIZE(wzChannelGUID)))
  5738. {
  5739. fRet = (StrCmpN(wzFolderGUID, wzChannelGUID, ARRAYSIZE(wzChannelGUID)) == 0);
  5740. if (fRet && pwzChannelURL)
  5741. {
  5742. fRet = (SHGetIniStringW(L"Channel", L"CDFURL", pwzChannelURL, INTERNET_MAX_URL_LENGTH, wzIniFile) != 0);
  5743. }
  5744. }
  5745. }
  5746. return fRet;
  5747. }
  5748. BOOL CNscTree::_IsChannelFolder(HTREEITEM hti)
  5749. {
  5750. BOOL fRet = FALSE;
  5751. LPITEMIDLIST pidl = _GetFullIDList(hti);
  5752. if (pidl)
  5753. {
  5754. WCHAR szPath[MAX_PATH];
  5755. if (SUCCEEDED(IEGetNameAndFlags(pidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), NULL)))
  5756. {
  5757. fRet = IsChannelFolder(szPath, NULL);
  5758. }
  5759. ILFree(pidl);
  5760. }
  5761. return fRet;
  5762. }
  5763. HRESULT CNscTree::CreateSubscriptionForSelection(/*[out, retval]*/ VARIANT_BOOL *pBool)
  5764. {
  5765. HRESULT hr = DoSubscriptionForSelection(TRUE);
  5766. if (pBool)
  5767. *pBool = (SUCCEEDED(hr) ? TRUE : FALSE);
  5768. return FIX_SCRIPTING_ERRORS(hr);
  5769. }
  5770. HRESULT CNscTree::DeleteSubscriptionForSelection(/*[out, retval]*/ VARIANT_BOOL *pBool)
  5771. {
  5772. HRESULT hr = DoSubscriptionForSelection(FALSE);
  5773. if (pBool)
  5774. *pBool = (SUCCEEDED(hr) ? TRUE : FALSE);
  5775. return FIX_SCRIPTING_ERRORS(hr);
  5776. }
  5777. //
  5778. // 1. get the selected item
  5779. // 2. get it's name
  5780. // 3. get it's url
  5781. // 4. create a Subscription manager and do the right thing for channels
  5782. // 5. return Subscription manager's result
  5783. HRESULT CNscTree::DoSubscriptionForSelection(BOOL fCreate)
  5784. {
  5785. #ifndef DISABLE_SUBSCRIPTIONS
  5786. HRESULT hr = E_FAIL;
  5787. WCHAR wzUrl[MAX_URL_STRING];
  5788. WCHAR wzName[MAX_PATH];
  5789. HTREEITEM htiSelected = TreeView_GetSelection(_hwndTree);
  5790. if (htiSelected == NULL)
  5791. return E_FAIL;
  5792. TV_ITEM tvi;
  5793. tvi.mask = TVIF_HANDLE | TVIF_TEXT;
  5794. tvi.hItem = htiSelected;
  5795. tvi.pszText = wzName;
  5796. tvi.cchTextMax = ARRAYSIZE(wzName);
  5797. TreeView_GetItem(_hwndTree, &tvi);
  5798. WCHAR wzPath[MAX_PATH];
  5799. LPITEMIDLIST pidlItem = _CacheParentShellFolder(htiSelected, NULL);
  5800. if (pidlItem)
  5801. {
  5802. GetPathForItem(_psfCache, pidlItem, wzPath, NULL);
  5803. hr = GetNavTargetName(_psfCache, pidlItem, wzUrl, ARRAYSIZE(wzUrl));
  5804. }
  5805. if (FAILED(hr)) //if we couldn't get an url, not much to do
  5806. return hr;
  5807. ISubscriptionMgr *psm;
  5808. hr = JITCoCreateInstance(CLSID_SubscriptionMgr, NULL,
  5809. CLSCTX_INPROC_SERVER, IID_PPV_ARG(ISubscriptionMgr, &psm),
  5810. _hwndTree, FIEF_FLAG_FORCE_JITUI);
  5811. if (SUCCEEDED(hr))
  5812. {
  5813. HCURSOR hCursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
  5814. //IsChannelFolder will fixup wzUrl if it's a channel
  5815. BOOL fChannel = IsChannelFolder(wzPath, wzUrl);
  5816. if (fCreate)
  5817. {
  5818. SUBSCRIPTIONINFO si = { sizeof(SUBSCRIPTIONINFO) };
  5819. TASK_TRIGGER tt;
  5820. BOOL bIsSoftware = FALSE;
  5821. if (fChannel)
  5822. {
  5823. IChannelMgrPriv *pChannelMgrPriv;
  5824. hr = JITCoCreateInstance(CLSID_ChannelMgr, NULL, CLSCTX_INPROC_SERVER,
  5825. IID_PPV_ARG(IChannelMgrPriv, &pChannelMgrPriv),
  5826. _hwndTree, FIEF_FLAG_PEEK);
  5827. if (SUCCEEDED(hr))
  5828. {
  5829. WCHAR wszTitle[MAX_PATH];
  5830. si.fUpdateFlags |= SUBSINFO_SCHEDULE;
  5831. si.schedule = SUBSSCHED_AUTO;
  5832. si.pTrigger = (void *)&tt;
  5833. hr = pChannelMgrPriv->DownloadMinCDF(_hwndTree, wzUrl,
  5834. wszTitle, ARRAYSIZE(wszTitle),
  5835. &si, &bIsSoftware);
  5836. pChannelMgrPriv->Release();
  5837. }
  5838. }
  5839. if (SUCCEEDED(hr))
  5840. {
  5841. DWORD dwFlags = CREATESUBS_NOUI | CREATESUBS_FROMFAVORITES |
  5842. ((!bIsSoftware) ? 0 : CREATESUBS_SOFTWAREUPDATE);
  5843. hr = psm->CreateSubscription(_hwndTree, wzUrl, wzName, dwFlags,
  5844. (fChannel ? SUBSTYPE_CHANNEL : SUBSTYPE_URL),
  5845. &si);
  5846. }
  5847. }
  5848. else
  5849. {
  5850. hr = psm->DeleteSubscription(wzUrl, NULL);
  5851. }
  5852. BOOL bSubscribed;
  5853. // This is in case subscribing or unsubscribing return a failed result even
  5854. // though the action succeeded from our standpoint (ie. item was subscribed
  5855. // successfully but creating a schedule failed or the item was unsubscribed
  5856. // successfully but we couldn't abort a running download in syncmgr).
  5857. psm->IsSubscribed(wzUrl, &bSubscribed);
  5858. hr = ((fCreate && bSubscribed) || (!fCreate && !bSubscribed)) ? S_OK : E_FAIL;
  5859. psm->Release();
  5860. SetCursor(hCursorOld);
  5861. }
  5862. return hr;
  5863. #else /* !DISABLE_SUBSCRIPTIONS */
  5864. return E_FAIL;
  5865. #endif /* !DISABLE_SUBSCRIPTIONS */
  5866. }
  5867. // Causes NSC to re-root on a different pidl --
  5868. HRESULT CNscTree::_ChangePidlRoot(LPCITEMIDLIST pidl)
  5869. {
  5870. _fClosing = TRUE;
  5871. ::SendMessage(_hwndTree, WM_SETREDRAW, FALSE, 0);
  5872. _bSynchId++;
  5873. if (_bSynchId >= 16)
  5874. _bSynchId = 0;
  5875. TreeView_DeleteAllItemsQuickly(_hwndTree);
  5876. _htiActiveBorder = NULL;
  5877. _fClosing = FALSE;
  5878. if (_psfCache)
  5879. _ReleaseCachedShellFolder();
  5880. // We do this even for (NULL == pidl) because (CSIDL_DESKTOP == NULL)
  5881. if ((LPCITEMIDLIST)INVALID_HANDLE_VALUE != pidl)
  5882. {
  5883. _UnSubClass();
  5884. _SetRoot(pidl, 3/*random*/, NULL, NSSR_CREATEPIDL);
  5885. _SubClass(pidl);
  5886. }
  5887. ::SendMessage(_hwndTree, WM_SETREDRAW, TRUE, 0);
  5888. return S_OK;
  5889. }
  5890. BOOL CNscTree::_IsMarked(HTREEITEM hti)
  5891. {
  5892. if ((hti == NULL) || (hti == TVI_ROOT))
  5893. return FALSE;
  5894. TVITEM tvi;
  5895. tvi.mask = TVIF_HANDLE | TVIF_STATE;
  5896. tvi.stateMask = TVIS_STATEIMAGEMASK;
  5897. tvi.state = 0;
  5898. tvi.hItem = hti;
  5899. TreeView_GetItem(_hwndTree, &tvi);
  5900. return BOOLIFY(tvi.state & NSC_TVIS_MARKED);
  5901. }
  5902. void CNscTree::_MarkChildren(HTREEITEM htiParent, BOOL fOn)
  5903. {
  5904. TVITEM tvi;
  5905. tvi.mask = TVIF_HANDLE | TVIF_STATE;
  5906. tvi.stateMask = TVIS_STATEIMAGEMASK;
  5907. tvi.state = (fOn ? NSC_TVIS_MARKED : 0);
  5908. tvi.hItem = htiParent;
  5909. TreeView_SetItem(_hwndTree, &tvi);
  5910. for (HTREEITEM htiTemp = TreeView_GetChild(_hwndTree, htiParent); htiTemp; htiTemp = TreeView_GetNextSibling(_hwndTree, htiTemp))
  5911. {
  5912. tvi.hItem = htiTemp;
  5913. TreeView_SetItem(_hwndTree, &tvi);
  5914. _MarkChildren(htiTemp, fOn);
  5915. }
  5916. }
  5917. //Updates the tree and internal state for the active border (the 1 pixel line)
  5918. // htiSelected is the item that was just clicked on/selected
  5919. void CNscTree::_UpdateActiveBorder(HTREEITEM htiSelected)
  5920. {
  5921. HTREEITEM htiNewBorder;
  5922. if (MODE_NORMAL == _mode)
  5923. return;
  5924. //if an item is a folder, then it should have the border
  5925. if (htiSelected != TVI_ROOT)
  5926. {
  5927. if (TreeView_GetChild(_hwndTree, htiSelected))
  5928. htiNewBorder = htiSelected;
  5929. else
  5930. htiNewBorder = TreeView_GetParent(_hwndTree, htiSelected);
  5931. }
  5932. else
  5933. htiNewBorder = NULL;
  5934. //clear the old state
  5935. // in multiselect mode we don't unselect the previously selected folder
  5936. if ((!(_dwFlags & NSS_MULTISELECT)) && (_htiActiveBorder != TVI_ROOT) && (_htiActiveBorder != NULL)
  5937. && (htiNewBorder != _htiActiveBorder))
  5938. _MarkChildren(_htiActiveBorder, FALSE);
  5939. //set the new state
  5940. BOOL bMark = TRUE;
  5941. if (_dwFlags & NSS_MULTISELECT)
  5942. {
  5943. bMark = TreeView_GetItemState(_hwndTree, htiNewBorder, NSC_TVIS_MARKED) & NSC_TVIS_MARKED;
  5944. }
  5945. if (bMark && (htiNewBorder != TVI_ROOT) && (htiNewBorder != NULL))
  5946. _MarkChildren(htiNewBorder, TRUE);
  5947. //treeview knows to invalidate itself
  5948. _htiActiveBorder = htiNewBorder;
  5949. }
  5950. void CNscTree::_UpdateItemDisplayInfo(HTREEITEM hti)
  5951. {
  5952. if (_GetTreeItemInfo(hti) && _pTaskScheduler)
  5953. {
  5954. LPITEMIDLIST pidl = _GetFullIDList(hti);
  5955. if (pidl)
  5956. {
  5957. LPITEMIDLIST pidl2 = _mode == MODE_NORMAL ? ILClone(pidl) : NULL;
  5958. AddNscIconTask(_pTaskScheduler, pidl, s_NscIconCallback, this, (UINT_PTR) hti, (UINT)_bSynchId);
  5959. if (pidl2)
  5960. {
  5961. AddNscOverlayTask(_pTaskScheduler, pidl2, &s_NscOverlayCallback, this, (UINT_PTR)hti, (UINT)_bSynchId);
  5962. }
  5963. }
  5964. }
  5965. //pidls get freed by CNscIconTask
  5966. }
  5967. void CNscTree::s_NscOverlayCallback(CNscTree *pns, UINT_PTR uId, int iOverlayIndex, UINT uMagic)
  5968. {
  5969. ASSERT(pns);
  5970. ASSERT(uId);
  5971. //this function gets called on a background thread, so use PostMessage to do treeview ops
  5972. //on the main thread only.
  5973. //assert that wacky packing is going to work
  5974. ASSERT(((iOverlayIndex & 0x0fffffff) == iOverlayIndex) && (uMagic < 16));
  5975. LPARAM lParam = (uMagic << 28) + iOverlayIndex;
  5976. if (uMagic == pns->_bSynchId && ::IsWindow(pns->_hwndTree))
  5977. ::PostMessage(pns->_hwndTree, WM_NSCUPDATEICONOVERLAY, (WPARAM)uId, lParam);
  5978. }
  5979. void CNscTree::s_NscIconCallback(CNscTree *pns, UINT_PTR uId, int iIcon, int iIconOpen, DWORD dwFlags, UINT uMagic)
  5980. {
  5981. ASSERT(pns);
  5982. ASSERT(uId);
  5983. //this function gets called on a background thread, so use PostMessage to do treeview ops
  5984. //on the main thread only.
  5985. //assert that wacky packing is going to work
  5986. ASSERT((iIcon < 4096) && (iIconOpen < 4096) && (dwFlags < 16) && (uMagic < 16));
  5987. LPARAM lParam = (uMagic << 28) + (dwFlags << 24) + (iIconOpen << 12) + iIcon;
  5988. if (uMagic == pns->_bSynchId && ::IsWindow(pns->_hwndTree))
  5989. ::PostMessage(pns->_hwndTree, WM_NSCUPDATEICONINFO, (WPARAM)uId, lParam);
  5990. }
  5991. LRESULT CNscTree::_OnCommand(WPARAM wParam, LPARAM lParam)
  5992. {
  5993. UINT idCmd = GET_WM_COMMAND_ID(wParam, lParam);
  5994. switch(idCmd)
  5995. {
  5996. case FCIDM_MOVE:
  5997. InvokeContextMenuCommand(L"cut");
  5998. break;
  5999. case FCIDM_COPY:
  6000. InvokeContextMenuCommand(L"copy");
  6001. break;
  6002. case FCIDM_PASTE:
  6003. InvokeContextMenuCommand(L"paste");
  6004. break;
  6005. case FCIDM_LINK:
  6006. InvokeContextMenuCommand(L"link");
  6007. break;
  6008. case FCIDM_DELETE:
  6009. InvokeContextMenuCommand(L"delete");
  6010. if (_hwndTree)
  6011. {
  6012. SHChangeNotifyHandleEvents();
  6013. }
  6014. break;
  6015. case FCIDM_PROPERTIES:
  6016. InvokeContextMenuCommand(L"properties");
  6017. break;
  6018. case FCIDM_RENAME:
  6019. {
  6020. // HACKHACK (lamadio): This is to hack around tree view renaming on click and hover
  6021. _fOkToRename = TRUE;
  6022. HTREEITEM hti = TreeView_GetSelection(_hwndTree);
  6023. if (hti)
  6024. TreeView_EditLabel(_hwndTree, hti);
  6025. _fOkToRename = FALSE;
  6026. }
  6027. break;
  6028. default:
  6029. return FALSE;
  6030. }
  6031. return TRUE;
  6032. }
  6033. void CNscTree::_TreeSetItemState(HTREEITEM hti, UINT stateMask, UINT state)
  6034. {
  6035. if (hti)
  6036. {
  6037. TV_ITEM tvi;
  6038. tvi.mask = TVIF_STATE;
  6039. tvi.stateMask = stateMask;
  6040. tvi.hItem = hti;
  6041. tvi.state = state;
  6042. TreeView_SetItem(_hwndTree, &tvi);
  6043. }
  6044. }
  6045. void CNscTree::_TreeNukeCutState()
  6046. {
  6047. _TreeSetItemState(_htiCut, TVIS_CUT, 0);
  6048. _htiCut = NULL;
  6049. ::ChangeClipboardChain(_hwndTree, _hwndNextViewer);
  6050. _hwndNextViewer = NULL;
  6051. }
  6052. // *** IFolderFilterSite methods ***
  6053. HRESULT CNscTree::SetFilter(IUnknown* punk)
  6054. {
  6055. HRESULT hr = S_OK;
  6056. ATOMICRELEASE(_pFilter);
  6057. if (punk)
  6058. hr = punk->QueryInterface(IID_PPV_ARG(IFolderFilter, &_pFilter));
  6059. return hr;
  6060. }
  6061. int DPADeletePidlsCB(void *pItem, void *pData)
  6062. {
  6063. if (pItem)
  6064. ILFree((LPITEMIDLIST)pItem);
  6065. return TRUE;
  6066. }
  6067. int DPADeleteItemCB(void *pItem, void *pData)
  6068. {
  6069. if (pItem)
  6070. {
  6071. LocalFree(pItem);
  6072. pItem = NULL;
  6073. }
  6074. return TRUE;
  6075. }
  6076. HRESULT CNscTree::get_SubscriptionsEnabled(VARIANT_BOOL * pVal)
  6077. {
  6078. *pVal = BOOLIFY(!SHRestricted2(REST_NoAddingSubscriptions, NULL, 0));
  6079. return S_OK;
  6080. }
  6081. HRESULT CNscTree::Synchronize()
  6082. {
  6083. return S_OK;
  6084. }
  6085. HRESULT CNscTree::NewFolder()
  6086. {
  6087. //we should do this activates stuff only in control mode
  6088. //hack to get control to be activated fully
  6089. m_bUIActive = FALSE;
  6090. InPlaceActivate(OLEIVERB_UIACTIVATE);
  6091. return CreateNewFolder(TreeView_GetSelection(_hwndTree));
  6092. }
  6093. HRESULT CNscTree::InvokeContextMenuCommand(BSTR strCommand)
  6094. {
  6095. ASSERT(strCommand);
  6096. if (strCommand)
  6097. {
  6098. //again activate only if in control mode
  6099. //only if renaming, activate control
  6100. if (StrStr(strCommand, L"rename") != NULL)
  6101. {
  6102. //hack to get control to be activated fully
  6103. m_bUIActive = FALSE;
  6104. InPlaceActivate(OLEIVERB_UIACTIVATE);
  6105. }
  6106. return _InvokeContextMenuCommand(strCommand);
  6107. }
  6108. return S_OK;
  6109. }
  6110. HRESULT CNscTree::get_EnumOptions(LONG *pVal)
  6111. {
  6112. *pVal = _grfFlags;
  6113. return S_OK;
  6114. }
  6115. HRESULT CNscTree::put_EnumOptions(LONG lVal)
  6116. {
  6117. _grfFlags = lVal;
  6118. return S_OK;
  6119. }
  6120. HRESULT CreateFolderItem(LPCITEMIDLIST pidl, IDispatch **ppItem)
  6121. {
  6122. *ppItem = NULL;
  6123. IPersistFolder *ppf;
  6124. HRESULT hr = CoCreateInstance(CLSID_FolderItem, NULL, CLSCTX_INPROC_SERVER,
  6125. IID_PPV_ARG(IPersistFolder, &ppf));
  6126. if (SUCCEEDED(hr))
  6127. {
  6128. if (S_OK == ppf->Initialize(pidl))
  6129. {
  6130. hr = ppf->QueryInterface(IID_PPV_ARG(IDispatch, ppItem));
  6131. }
  6132. else
  6133. hr = E_FAIL;
  6134. ppf->Release();
  6135. }
  6136. return hr;
  6137. }
  6138. HRESULT CNscTree::get_SelectedItem(IDispatch **ppItem)
  6139. {
  6140. *ppItem = NULL;
  6141. LPITEMIDLIST pidl;
  6142. if (SUCCEEDED(GetSelectedItem(&pidl, 0)) && pidl)
  6143. {
  6144. CreateFolderItem(pidl, ppItem);
  6145. ILFree(pidl);
  6146. }
  6147. return *ppItem ? S_OK : S_FALSE;
  6148. }
  6149. HRESULT CNscTree::put_SelectedItem(IDispatch *pItem)
  6150. {
  6151. return S_FALSE;
  6152. }
  6153. HRESULT CNscTree::get_Root(VARIANT *pvar)
  6154. {
  6155. pvar->vt = VT_EMPTY;
  6156. return S_OK;
  6157. }
  6158. HRESULT CNscTree::put_Root(VARIANT var)
  6159. {
  6160. if (_csidl != -1)
  6161. {
  6162. SetNscMode(MODE_CONTROL);
  6163. _csidl = -1; // unknown
  6164. }
  6165. return _PutRootVariant(&var);
  6166. }
  6167. HRESULT CNscTree::_PutRootVariant(VARIANT *pvar)
  6168. {
  6169. BOOL bReady = _pidlRoot != NULL;
  6170. LPITEMIDLIST pidl = VariantToIDList(pvar);
  6171. if (_hdpaViews)
  6172. {
  6173. DPA_DestroyCallback(_hdpaViews, DPADeletePidlsCB, NULL);
  6174. _hdpaViews = NULL;
  6175. }
  6176. HRESULT hr = S_OK;
  6177. if (bReady)
  6178. hr = _ChangePidlRoot(pidl);
  6179. ILFree(pidl);
  6180. return S_OK;
  6181. }
  6182. HRESULT CNscTree::SetRoot(BSTR bstrFullPath)
  6183. {
  6184. // SetRoot is from IShellFavoritesNamespace so turn on Favorites mode
  6185. _csidl = CSIDL_FAVORITES;
  6186. SetNscMode(MODE_FAVORITES | MODE_CONTROL);
  6187. CComVariant varPath(bstrFullPath);
  6188. return FIX_SCRIPTING_ERRORS(_PutRootVariant(&varPath));
  6189. }
  6190. HRESULT CNscTree::put_Mode(UINT uMode)
  6191. {
  6192. SetNscMode(uMode);
  6193. _csidl = -1;
  6194. return S_OK;
  6195. }
  6196. HRESULT CNscTree::put_Flags(DWORD dwFlags)
  6197. {
  6198. _dwFlags = dwFlags;
  6199. return S_OK;
  6200. }
  6201. HRESULT CNscTree::get_Columns(BSTR *pbstrColumns)
  6202. {
  6203. *pbstrColumns = SysAllocString(TEXT(""));
  6204. return *pbstrColumns? S_OK: E_FAIL;
  6205. }
  6206. typedef struct
  6207. {
  6208. TCHAR szName[20];
  6209. const SHCOLUMNID *pscid;
  6210. } COLUMNS;
  6211. static COLUMNS s_Columns[] =
  6212. {
  6213. {TEXT("name"), &SCID_NAME},
  6214. {TEXT("attribs"), &SCID_ATTRIBUTES},
  6215. {TEXT("size"), &SCID_SIZE},
  6216. {TEXT("type"), &SCID_TYPE},
  6217. {TEXT("create"), &SCID_CREATETIME},
  6218. };
  6219. int _SCIDsFromNames(LPTSTR pszNames, int nSize, const SHCOLUMNID *apscid[])
  6220. {
  6221. int cItems = 0;
  6222. if (!pszNames || !apscid || !nSize)
  6223. return -1;
  6224. do
  6225. {
  6226. BOOL bInsert = FALSE;
  6227. LPTSTR pszTemp = StrChr(pszNames, TEXT(';'));
  6228. if (pszTemp)
  6229. {
  6230. *pszTemp = 0;
  6231. pszTemp++;
  6232. }
  6233. for (int i = 0; i < ARRAYSIZE(s_Columns); i++)
  6234. {
  6235. if (StrCmpI(pszNames, s_Columns[i].szName) == 0)
  6236. {
  6237. bInsert = TRUE;
  6238. #ifdef NO_DUPLICATES
  6239. for (int j = 0; j < cItems; j++)
  6240. {
  6241. if (IsEqualSCID(*(s_Columns[i].pscid), *apscid[j]))
  6242. {
  6243. bInsert = FALSE;
  6244. break;
  6245. }
  6246. }
  6247. #endif
  6248. break;
  6249. }
  6250. }
  6251. if (bInsert)
  6252. {
  6253. apscid[cItems++] = s_Columns[i].pscid;
  6254. if (cItems >= nSize)
  6255. break;
  6256. }
  6257. pszNames = pszTemp;
  6258. }
  6259. while(pszNames);
  6260. return cItems;
  6261. }
  6262. HRESULT CNscTree::put_Columns(BSTR bstrColumns)
  6263. {
  6264. HRESULT hr = E_FAIL;
  6265. if (_dwFlags & NSS_HEADER)
  6266. {
  6267. if (!_hdpaColumns)
  6268. {
  6269. _hdpaColumns = DPA_Create(3);
  6270. hr = E_OUTOFMEMORY;
  6271. }
  6272. else
  6273. {
  6274. DPA_EnumCallback(_hdpaColumns, DPADeleteItemCB, NULL);
  6275. DPA_DeleteAllPtrs(_hdpaColumns);
  6276. }
  6277. if (_hdpaColumns)
  6278. {
  6279. const SHCOLUMNID *apscid[5];
  6280. int cItems = _SCIDsFromNames(bstrColumns, ARRAYSIZE(apscid), apscid);
  6281. hr = S_OK;
  6282. for (int i = 0; i < cItems; i++)
  6283. {
  6284. HEADERINFO *phinfo = (HEADERINFO *)LocalAlloc(LPTR, sizeof(HEADERINFO));
  6285. if (phinfo)
  6286. {
  6287. phinfo->pscid = apscid[i];
  6288. phinfo->iFldrCol = -1;
  6289. if (DPA_AppendPtr(_hdpaColumns, (void *)phinfo) == -1)
  6290. {
  6291. hr = E_FAIL;
  6292. LocalFree(phinfo);
  6293. phinfo = NULL;
  6294. break;
  6295. }
  6296. }
  6297. else
  6298. {
  6299. hr = E_OUTOFMEMORY;
  6300. break;
  6301. }
  6302. }
  6303. if (DPA_GetPtrCount(_hdpaColumns) > 0)
  6304. _CreateHeader();
  6305. }
  6306. }
  6307. return hr;
  6308. }
  6309. HRESULT CNscTree::get_CountViewTypes(int *piTypes)
  6310. {
  6311. *piTypes = 0;
  6312. if (_pidlRoot && !_hdpaViews)
  6313. {
  6314. IShellFolder *psf;
  6315. if (SUCCEEDED(IEBindToObject(_pidlRoot, &psf))) //do we have this cached?
  6316. {
  6317. IShellFolderViewType *psfvt;
  6318. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolderViewType, &psfvt))))
  6319. {
  6320. IEnumIDList *penum;
  6321. if (SUCCEEDED(psfvt->EnumViews(0, &penum)))
  6322. {
  6323. LPITEMIDLIST pidl;
  6324. ULONG cFetched;
  6325. _hdpaViews = DPA_Create(4);
  6326. if (_hdpaViews)
  6327. {
  6328. while (penum->Next(1, &pidl, &cFetched) == S_OK && cFetched == 1)
  6329. {
  6330. if (DPA_AppendPtr(_hdpaViews, pidl) == -1)
  6331. {
  6332. ILFree(pidl);
  6333. break;
  6334. }
  6335. }
  6336. }
  6337. penum->Release();
  6338. }
  6339. psfvt->Release();
  6340. }
  6341. psf->Release();
  6342. }
  6343. }
  6344. if (_hdpaViews)
  6345. *piTypes = DPA_GetPtrCount(_hdpaViews);
  6346. return S_OK;
  6347. }
  6348. HRESULT CNscTree::SetViewType(int iType)
  6349. {
  6350. HRESULT hr = S_FALSE;
  6351. if (_hdpaViews && iType < DPA_GetPtrCount(_hdpaViews)) // allow negative types to reset to _pidlRoot
  6352. {
  6353. LPITEMIDLIST pidl = (LPITEMIDLIST)DPA_GetPtr(_hdpaViews, iType);
  6354. LPITEMIDLIST pidlType;
  6355. if (pidl)
  6356. pidlType = ILCombine(_pidlRoot, pidl);
  6357. else
  6358. pidlType = _pidlRoot;
  6359. if (pidlType)
  6360. {
  6361. hr = _ChangePidlRoot(pidlType);
  6362. if (pidlType != _pidlRoot)
  6363. ILFree(pidlType);
  6364. }
  6365. }
  6366. return hr;
  6367. }
  6368. HRESULT CreateFolderItemsFDF(LPCITEMIDLIST pidl, IDispatch **ppItems)
  6369. {
  6370. *ppItems = NULL;
  6371. IPersistFolder *ppf;
  6372. HRESULT hr = CoCreateInstance(CLSID_FolderItemsFDF, NULL, CLSCTX_INPROC_SERVER,
  6373. IID_PPV_ARG(IPersistFolder, &ppf));
  6374. if (SUCCEEDED(hr))
  6375. {
  6376. if (S_OK == ppf->Initialize(pidl))
  6377. {
  6378. hr = ppf->QueryInterface(IID_PPV_ARG(IDispatch, ppItems));
  6379. }
  6380. else
  6381. hr = E_FAIL;
  6382. ppf->Release();
  6383. }
  6384. return hr;
  6385. }
  6386. void CNscTree::_InsertMarkedChildren(HTREEITEM htiParent, LPCITEMIDLIST pidlParent, IInsertItem *pii)
  6387. {
  6388. TV_ITEM tvi;
  6389. tvi.mask = TVIF_PARAM | TVIF_HANDLE;
  6390. for (HTREEITEM htiTemp = TreeView_GetChild(_hwndTree, htiParent); htiTemp; htiTemp = TreeView_GetNextSibling(_hwndTree, htiTemp))
  6391. {
  6392. BOOL bMarked = TreeView_GetItemState(_hwndTree, htiTemp, NSC_TVIS_MARKED) & NSC_TVIS_MARKED;
  6393. tvi.hItem = htiTemp;
  6394. if (TreeView_GetItem(_hwndTree, &tvi))
  6395. {
  6396. if (tvi.lParam)
  6397. {
  6398. PORDERITEM poi = ((ITEMINFO *)tvi.lParam)->poi;
  6399. if (poi)
  6400. {
  6401. LPITEMIDLIST pidl = ILCombine(pidlParent, poi->pidl);
  6402. if (pidl)
  6403. {
  6404. if (bMarked)
  6405. {
  6406. pii->InsertItem(pidl);
  6407. }
  6408. _InsertMarkedChildren(htiTemp, pidl, pii);
  6409. ILFree(pidl);
  6410. }
  6411. }
  6412. }
  6413. }
  6414. }
  6415. }
  6416. HRESULT CNscTree::SelectedItems(IDispatch **ppItems)
  6417. {
  6418. HRESULT hr = CreateFolderItemsFDF(_pidlRoot, ppItems);
  6419. // poke all marked items in ppitems)
  6420. if (SUCCEEDED(hr) && _hwndTree)
  6421. {
  6422. IInsertItem *pii;
  6423. hr = (*ppItems)->QueryInterface(IID_PPV_ARG(IInsertItem, &pii));
  6424. if (SUCCEEDED(hr))
  6425. {
  6426. if (!(_mode & MODE_NORMAL) && (_dwFlags & NSS_MULTISELECT))
  6427. {
  6428. _InsertMarkedChildren(TVI_ROOT, NULL, pii);
  6429. }
  6430. else
  6431. {
  6432. LPITEMIDLIST pidl;
  6433. if (SUCCEEDED(GetSelectedItem(&pidl, 0)) && pidl)
  6434. {
  6435. hr = pii->InsertItem(pidl);
  6436. ILFree(pidl);
  6437. }
  6438. }
  6439. pii->Release();
  6440. }
  6441. }
  6442. return hr;
  6443. }
  6444. HRESULT CNscTree::Expand(VARIANT var, int iDepth)
  6445. {
  6446. HRESULT hr = E_FAIL;
  6447. LPITEMIDLIST pidl;
  6448. if (var.vt == VT_EMPTY)
  6449. pidl = ILClone(_pidlRoot);
  6450. else
  6451. pidl = VariantToIDList(&var);
  6452. if (pidl)
  6453. {
  6454. hr = _Expand(pidl, iDepth);
  6455. if (FAILED(hr))
  6456. hr = S_FALSE;
  6457. ILFree(pidl);
  6458. }
  6459. return hr;
  6460. }
  6461. /*
  6462. HRESULT CNscTree::get_ReadyState(READYSTATE *plReady)
  6463. {
  6464. *plReady = _lReadyState;
  6465. return S_OK;
  6466. }
  6467. void CNscTree::_ReadyStateChange(READYSTATE lReady)
  6468. {
  6469. if (_lReadyState < lReady)
  6470. {
  6471. _lReadyState = lReady;
  6472. //post beta 1 item (need trident v3)
  6473. //IUnknown_CPContainerOnChanged(SAFECAST(this, IShellNameSpace *), DISPID_READYSTATE);
  6474. }
  6475. }
  6476. */
  6477. HRESULT CNscTree::UnselectAll()
  6478. {
  6479. if (_dwFlags & NSS_MULTISELECT)
  6480. _MarkChildren(TVI_ROOT, FALSE);
  6481. return S_OK;
  6482. }
  6483. LRESULT CNscTree::OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  6484. {
  6485. // when in label edit mode, don't try to activate the control or you'll get out of label editing,
  6486. // even when you click on the label edit control
  6487. if (!InLabelEdit())
  6488. InPlaceActivate(OLEIVERB_UIACTIVATE);
  6489. return S_OK;
  6490. }
  6491. LRESULT CNscTree::OnGetIShellBrowser(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
  6492. {
  6493. LRESULT lResult = NULL; // This will be the IShellBrowser *.
  6494. IShellBrowser * psb;
  6495. if (SUCCEEDED(_InternalQueryInterface(IID_PPV_ARG(IShellBrowser, &psb))))
  6496. {
  6497. lResult = (LRESULT) psb;
  6498. psb->Release();
  6499. }
  6500. bHandled = TRUE;
  6501. return lResult;
  6502. }
  6503. LRESULT CNscTree::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
  6504. {
  6505. if (!m_bUIActive)
  6506. CComControlBase::InPlaceActivate(OLEIVERB_UIACTIVATE);
  6507. if ((HWND)wParam != _hwndTree)
  6508. ::SendMessage(_hwndTree, uMsg, wParam, lParam);
  6509. bHandled = TRUE;
  6510. return 0;
  6511. }
  6512. LRESULT CNscTree::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
  6513. {
  6514. bHandled = TRUE;
  6515. return S_OK;
  6516. }
  6517. LRESULT CNscTree::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  6518. {
  6519. LPNMHDR pnm = (LPNMHDR)lParam;
  6520. if (pnm)
  6521. {
  6522. switch (pnm->code)
  6523. {
  6524. case TVN_SELCHANGEDA:
  6525. case TVN_SELCHANGED:
  6526. {
  6527. if (CSIDL_FAVORITES == _csidl)
  6528. {
  6529. IShellFolder *psf = NULL;
  6530. LPITEMIDLIST pidl = NULL;
  6531. UINT cItems, cVisits;
  6532. WCHAR szTitle[MAX_PATH], szUrl[INTERNET_MAX_URL_LENGTH], szLastVisited[MAX_PATH];
  6533. BOOL fAvailableOffline;
  6534. szTitle[0] = szUrl[0] = szLastVisited[0] = 0;
  6535. HRESULT hr = BindToSelectedItemParent(IID_PPV_ARG(IShellFolder, &psf), &pidl);
  6536. if (SUCCEEDED(hr) && (SUCCEEDED(GetSelectedItemName(szTitle, ARRAYSIZE(szTitle)))))
  6537. {
  6538. GetEventInfo(psf, pidl, &cItems, szUrl, ARRAYSIZE(szUrl), &cVisits, szLastVisited, &fAvailableOffline);
  6539. CComBSTR strName(szTitle);
  6540. CComBSTR strUrl(szUrl);
  6541. CComBSTR strDate(szLastVisited);
  6542. _FireFavoritesSelectionChange(cItems, 0, strName, strUrl, cVisits, strDate, fAvailableOffline);
  6543. }
  6544. else
  6545. _FireFavoritesSelectionChange(0, 0, NULL, NULL, 0, NULL, FALSE);
  6546. ILFree(pidl);
  6547. ATOMICRELEASE(psf);
  6548. }
  6549. IUnknown_CPContainerInvokeParam(SAFECAST(this, IShellNameSpace *),
  6550. DIID_DShellNameSpaceEvents, DISPID_SELECTIONCHANGE, NULL, 0);
  6551. }
  6552. break;
  6553. case NM_DBLCLK:
  6554. IUnknown_CPContainerInvokeParam(SAFECAST(this, IShellNameSpace *),
  6555. DIID_DShellNameSpaceEvents, DISPID_DOUBLECLICK, NULL, 0);
  6556. break;
  6557. default:
  6558. break;
  6559. }
  6560. }
  6561. LRESULT lResult;
  6562. HRESULT hr = OnWinEvent(_hwndTree, uMsg, wParam, lParam, &lResult);
  6563. bHandled = (lResult ? TRUE : FALSE);
  6564. return SUCCEEDED(hr) ? lResult : hr;
  6565. }
  6566. void CNscTree::_InitHeaderInfo()
  6567. {
  6568. if (!_pidlRoot || !_hdpaColumns || DPA_GetPtrCount(_hdpaColumns) == 0)
  6569. return;
  6570. IShellFolder *psf;
  6571. if (SUCCEEDED(IEBindToObject(_pidlRoot, &psf)))
  6572. {
  6573. IShellFolder2 *psf2;
  6574. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
  6575. {
  6576. int i;
  6577. SHCOLUMNID scid;
  6578. for (i=0; SUCCEEDED(psf2->MapColumnToSCID(i, &scid)); i++)
  6579. {
  6580. BOOL bFound = FALSE;
  6581. HEADERINFO *phinfo;
  6582. for (int iCol=0; iCol < DPA_GetPtrCount(_hdpaColumns); iCol++)
  6583. {
  6584. phinfo = (HEADERINFO *)DPA_GetPtr(_hdpaColumns, iCol);
  6585. if (phinfo && phinfo->iFldrCol == -1 && IsEqualSCID(*(phinfo->pscid), scid))
  6586. {
  6587. bFound = TRUE;
  6588. break;
  6589. }
  6590. }
  6591. if (bFound)
  6592. {
  6593. DETAILSINFO di;
  6594. di.fmt = LVCFMT_LEFT;
  6595. di.cxChar = 20;
  6596. di.str.uType = (UINT)-1;
  6597. //di.pidl = NULL;
  6598. if (SUCCEEDED(psf2->GetDetailsOf(NULL, i, (SHELLDETAILS *)&di.fmt)))
  6599. {
  6600. phinfo->fmt = di.fmt;
  6601. phinfo->iFldrCol = i;
  6602. phinfo->cxChar = di.cxChar;
  6603. StrRetToBuf(&di.str, NULL, phinfo->szName, ARRAYSIZE(phinfo->szName));
  6604. }
  6605. }
  6606. }
  6607. for (i=DPA_GetPtrCount(_hdpaColumns)-1; i >= 0; i--)
  6608. {
  6609. HEADERINFO *phinfo = (HEADERINFO *)DPA_GetPtr(_hdpaColumns, i);
  6610. if (!phinfo || phinfo->iFldrCol == -1)
  6611. {
  6612. if (phinfo)
  6613. {
  6614. LocalFree(phinfo);
  6615. phinfo = NULL;
  6616. }
  6617. DPA_DeletePtr(_hdpaColumns, i);
  6618. }
  6619. }
  6620. psf2->Release();
  6621. }
  6622. psf->Release();
  6623. }
  6624. }
  6625. HWND CNscTree::Create(HWND hWndParent, RECT& rcPos, LPCTSTR pszWindowName, DWORD dwStyle, DWORD dwExStyle, UINT nID)
  6626. {
  6627. CWindowImpl<CNscTree>::Create(hWndParent, rcPos, pszWindowName, dwStyle, dwExStyle, nID);
  6628. LPITEMIDLIST pidl = _pidlRoot, pidlToFree = NULL;
  6629. ASSERT(m_spClientSite);
  6630. SetSite(m_spClientSite); // hook up the site chain
  6631. _dwTVFlags |= TVS_TRACKSELECT | TVS_INFOTIP | TVS_FULLROWSELECT;
  6632. if (!(_mode & MODE_CUSTOM))
  6633. {
  6634. DWORD dwValue;
  6635. DWORD dwSize = sizeof(dwValue);
  6636. BOOL fDefault = TRUE;
  6637. SHRegGetUSValue(L"Software\\Microsoft\\Internet Explorer\\Main",
  6638. L"NscSingleExpand", NULL, (LPBYTE)&dwValue, &dwSize, FALSE,
  6639. (void *) &fDefault, sizeof(fDefault));
  6640. if (dwValue)
  6641. _dwTVFlags |= TVS_SINGLEEXPAND;
  6642. }
  6643. _hwndTree = NULL;
  6644. CreateTree(m_hWnd, _dwTVFlags, &_hwndTree);
  6645. if (NULL == pidl)
  6646. {
  6647. SHGetSpecialFolderLocation(NULL, _csidl, &pidl);
  6648. pidlToFree = pidl;
  6649. }
  6650. if (pidl)
  6651. {
  6652. if (_dwFlags & NSS_HEADER)
  6653. {
  6654. if (!_hdpaColumns || DPA_GetPtrCount(_hdpaColumns) == 0)
  6655. {
  6656. _dwFlags &= ~NSS_HEADER;
  6657. }
  6658. else
  6659. {
  6660. _InitHeaderInfo();
  6661. }
  6662. }
  6663. Initialize(pidl, _grfFlags, _dwFlags);
  6664. ShowWindow(TRUE);
  6665. IUnknown_CPContainerInvokeParam(SAFECAST(this, IShellNameSpace *), DIID_DShellNameSpaceEvents, DISPID_INITIALIZED, NULL, 0);
  6666. ILFree(pidlToFree);
  6667. }
  6668. return m_hWnd;
  6669. }
  6670. HRESULT CNscTree::InPlaceActivate(LONG iVerb, const RECT* prcPosRect /*= NULL*/)
  6671. {
  6672. HRESULT hr = CComControl<CNscTree>::InPlaceActivate(iVerb, prcPosRect);
  6673. if (::GetFocus() != _hwndTree)
  6674. ::SetFocus(_hwndTree);
  6675. return hr;
  6676. }
  6677. STDMETHODIMP CNscTree::GetWindow(HWND* phwnd)
  6678. {
  6679. return IOleInPlaceActiveObjectImpl<CNscTree>::GetWindow(phwnd);
  6680. }
  6681. STDMETHODIMP CNscTree::TranslateAccelerator(MSG *pMsg)
  6682. {
  6683. // label editing edit control is taking the keystrokes, TAing them will just duplicate them
  6684. if (InLabelEdit())
  6685. return S_FALSE;
  6686. // hack so that the escape can get out to the document, because TA won't do it
  6687. // WM_KEYDOWN is because some keyup's come through that need to not close the dialog
  6688. if ((pMsg->wParam == VK_ESCAPE) && (pMsg->message == WM_KEYDOWN))
  6689. {
  6690. _FireFavoritesSelectionChange(-1, 0, NULL, NULL, 0, NULL, FALSE);
  6691. return S_FALSE;
  6692. }
  6693. //except for tabs and sys keys, let nsctree take all the keystrokes
  6694. if ((pMsg->wParam != VK_TAB) && (pMsg->message != WM_SYSCHAR) && (pMsg->message != WM_SYSKEYDOWN) && (pMsg->message != WM_SYSKEYUP))
  6695. {
  6696. // TreeView will return TRUE if it processes the key, so we return S_OK to indicate
  6697. // the keystroke was used and prevent further processing
  6698. return ::SendMessage(pMsg->hwnd, TVM_TRANSLATEACCELERATOR, 0, (LPARAM)pMsg) ? S_OK : S_FALSE;
  6699. }
  6700. else
  6701. {
  6702. CComQIPtr<IOleControlSite, &IID_IOleControlSite>spCtrlSite(m_spClientSite);
  6703. if (spCtrlSite)
  6704. return spCtrlSite->TranslateAccelerator(pMsg,0);
  6705. }
  6706. return S_FALSE;
  6707. }
  6708. HRESULT CNscTree::SetObjectRects(LPCRECT prcPos, LPCRECT prcClip)
  6709. {
  6710. HRESULT hr = IOleInPlaceObjectWindowlessImpl<CNscTree>::SetObjectRects(prcPos, prcClip);
  6711. LONG lTop = 0;
  6712. if (_hwndHdr)
  6713. {
  6714. RECT rc;
  6715. ::GetWindowRect(_hwndHdr, &rc);
  6716. lTop = RECTHEIGHT(rc);
  6717. ::SetWindowPos(_hwndHdr, NULL, 0, 0, RECTWIDTH(*prcPos), lTop, SWP_NOZORDER | SWP_NOACTIVATE);
  6718. }
  6719. if (_hwndTree)
  6720. {
  6721. ::SetWindowPos(_hwndTree, NULL, 0, lTop, RECTWIDTH(*prcPos), RECTHEIGHT(*prcPos)-lTop,
  6722. SWP_NOZORDER | SWP_NOACTIVATE);
  6723. }
  6724. return hr;
  6725. }
  6726. STDMETHODIMP CNscTree::SetClientSite(IOleClientSite *pClientSite)
  6727. {
  6728. SetSite(pClientSite);
  6729. return IOleObjectImpl<CNscTree>::SetClientSite(pClientSite);
  6730. }
  6731. HRESULT CNscTree::OnDraw(ATL_DRAWINFO& di)
  6732. {
  6733. //should only get called before CNscTree is initialized
  6734. return S_OK;
  6735. }
  6736. LRESULT CNscTree::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
  6737. {
  6738. bHandled = FALSE; //let default handler also do it's work
  6739. _OnWindowCleanup();
  6740. return 0;
  6741. }
  6742. BOOL IsChannelFolder(LPCWSTR pwzPath, LPWSTR pwzChannelURL);
  6743. HRESULT CNscTree::GetEventInfo(IShellFolder *psf, LPCITEMIDLIST pidl,
  6744. UINT *pcItems, LPWSTR pszUrl, DWORD cchUrl,
  6745. UINT *pcVisits, LPWSTR pszLastVisited, BOOL *pfAvailableOffline)
  6746. {
  6747. HRESULT hr = S_OK;
  6748. TCHAR szPath[MAX_PATH];
  6749. TCHAR szUrl[MAX_URL_STRING];
  6750. szPath[0] = szUrl[0] = 0;
  6751. *pcItems = 1;
  6752. ULONG ulAttr = SFGAO_FOLDER; // make sure item is actually a folder
  6753. hr = GetPathForItem(psf, pidl, szPath, &ulAttr);
  6754. if (SUCCEEDED(hr) && (ulAttr & SFGAO_FOLDER))
  6755. {
  6756. pszLastVisited[0] = 0;
  6757. StrCpyN(pszUrl, szPath, cchUrl);
  6758. WIN32_FIND_DATA fd;
  6759. HANDLE hfind = FindFirstFile(szPath, &fd);
  6760. if (hfind != INVALID_HANDLE_VALUE)
  6761. {
  6762. SHFormatDateTime(&(fd.ftLastWriteTime), NULL, pszLastVisited, MAX_PATH);
  6763. FindClose(hfind);
  6764. }
  6765. *pcVisits = -1;
  6766. *pfAvailableOffline = 0;
  6767. return S_OK;
  6768. }
  6769. if (FAILED(hr))
  6770. {
  6771. // GetPathForItem fails on channel folders, but the following GetDisplayNameOf
  6772. // succeeds.
  6773. STRRET str;
  6774. if (SUCCEEDED(psf->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str)))
  6775. StrRetToBuf(&str, pidl, szPath, ARRAYSIZE(szPath));
  6776. }
  6777. hr = GetNavTargetName(psf, pidl, szUrl, ARRAYSIZE(szUrl));
  6778. // IsChannelFolder will fixup szUrl if it's a channel
  6779. IsChannelFolder(szPath, szUrl);
  6780. if (szUrl[0])
  6781. {
  6782. SHTCharToUnicode(szUrl, pszUrl, cchUrl);
  6783. //
  6784. // Get the cache info for this item. Note that we use GetUrlCacheEntryInfoEx instead
  6785. // of GetUrlCacheEntryInfo because it follows any redirects that occured. This wacky
  6786. // api uses a variable length buffer, so we have to guess the size and retry if the
  6787. // call fails.
  6788. //
  6789. BOOL fInCache = FALSE;
  6790. TCHAR szBuf[512];
  6791. LPINTERNET_CACHE_ENTRY_INFO pCE = (LPINTERNET_CACHE_ENTRY_INFO)szBuf;
  6792. DWORD dwEntrySize = sizeof(szBuf);
  6793. fInCache = GetUrlCacheEntryInfoEx(szUrl, pCE, &dwEntrySize, NULL, NULL, NULL, 0);
  6794. if (!fInCache)
  6795. {
  6796. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  6797. {
  6798. // We guessed too small for the buffer so allocate the correct size & retry
  6799. pCE = (LPINTERNET_CACHE_ENTRY_INFO)LocalAlloc(LPTR, dwEntrySize);
  6800. if (pCE)
  6801. {
  6802. fInCache = GetUrlCacheEntryInfoEx(szUrl, pCE, &dwEntrySize, NULL, NULL, NULL, 0);
  6803. }
  6804. }
  6805. }
  6806. *pfAvailableOffline = IsSubscribed(szUrl);
  6807. if (fInCache)
  6808. {
  6809. *pcVisits = pCE->dwHitRate;
  6810. SHFormatDateTime(&(pCE->LastAccessTime), NULL, pszLastVisited, MAX_PATH);
  6811. }
  6812. else
  6813. {
  6814. *pcVisits = 0;
  6815. pszLastVisited[0] = 0;
  6816. }
  6817. if ((TCHAR*)pCE != szBuf)
  6818. {
  6819. LocalFree(pCE);
  6820. pCE = NULL;
  6821. }
  6822. }
  6823. else
  6824. {
  6825. *pcVisits = 0;
  6826. SHTCharToUnicode(szPath, pszUrl, cchUrl);
  6827. }
  6828. return hr;
  6829. }
  6830. // [id(DISPID_FAVSELECTIONCHANGE)] void FavoritesSelectionChange([in] long cItems, [in] long hItem, [in] BSTR strName,
  6831. // [in] BSTR strUrl, [in] long cVisits, [in] BSTR strDate,
  6832. // [in] BOOL fAvailableOffline);
  6833. void CNscTree::_FireFavoritesSelectionChange(
  6834. long cItems, long hItem, BSTR strName, BSTR strUrl,
  6835. long cVisits, BSTR strDate, long fAvailableOffline)
  6836. {
  6837. VARIANTARG args[7];
  6838. IUnknown_CPContainerInvokeParam(SAFECAST(this, IShellNameSpace *),
  6839. DIID_DShellNameSpaceEvents, DISPID_FAVSELECTIONCHANGE,
  6840. args, ARRAYSIZE(args),
  6841. VT_I4, cItems,
  6842. VT_I4, hItem,
  6843. VT_BSTR, strName,
  6844. VT_BSTR, strUrl,
  6845. VT_I4, cVisits,
  6846. VT_BSTR, strDate,
  6847. VT_I4, fAvailableOffline);
  6848. }