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

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