Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1472 lines
38 KiB

  1. #define NOSHELLDEBUG // don't take shell versions of this
  2. // todo:
  3. //
  4. // "New Folder" capability
  5. //
  6. // dlb click on folders should expand/collapse
  7. //
  8. // delayed keyboard selection so keyboard navigation does not generate a sel change
  9. //
  10. // drag and drop image drawing (not just OLE cursors)
  11. //
  12. // name space change notification. hook up notify handler
  13. //
  14. // default keyboard accelerators (F2 = Rename, etc)
  15. //
  16. // Partial expanded nodes in the tree "Tree Down" (TVIS_EXPANDPARTIAL) for net cases
  17. //
  18. // Programbility:
  19. // notifies - sel changed, node expanded, verb executed, etc.
  20. // cmds - do verb, get item, etc.
  21. #include <windows.h>
  22. #include <shlobj.h>
  23. #include <shlobjp.h> // for SHChangeNotifyReigster
  24. #include "idlist.h"
  25. #include "nsc.h"
  26. #include "dropsrc.h"
  27. #include "common.h"
  28. #define ID_CONTROL 100
  29. #define WM_CHANGENOTIFY (WM_USER + 11)
  30. #define SHCNE_FOLDERS \
  31. (SHCNE_MKDIR | SHCNE_RMDIR | \
  32. SHCNE_MEDIAINSERTED | SHCNE_MEDIAREMOVED | \
  33. SHCNE_DRIVEREMOVED | SHCNE_DRIVEADD | \
  34. SHCNE_RENAMEFOLDER | \
  35. SHCNE_UPDATEDIR | \
  36. SHCNE_UPDATEITEM | \
  37. SHCNE_SERVERDISCONNECT | \
  38. SHCNE_UPDATEIMAGE | \
  39. SHCNE_DRIVEADDGUI)
  40. #define SHCNE_ITEMS (SHCNE_CREATE | SHCNE_DELETE | SHCNE_RENAMEITEM | SHCNE_ASSOCCHANGED)
  41. void _RegisterNotify(NSC *pns, BOOL fReRegister)
  42. {
  43. if (pns->nChangeNotifyID)
  44. {
  45. SHChangeNotifyDeregister(pns->nChangeNotifyID);
  46. pns->nChangeNotifyID = 0;
  47. }
  48. if (fReRegister)
  49. {
  50. SHChangeNotifyEntry fsne;
  51. fsne.pidl = pns->pidlRoot;
  52. fsne.fRecursive = TRUE;
  53. pns->nChangeNotifyID = SHChangeNotifyRegister(pns->hwnd,
  54. // SHCNRF_NewDelivery |
  55. SHCNRF_ShellLevel | SHCNRF_InterruptLevel,
  56. pns->style & NSS_SHOWNONFOLDERS ? (SHCNE_FOLDERS | SHCNE_ITEMS) : (SHCNE_FOLDERS),
  57. WM_CHANGENOTIFY, 1, &fsne);
  58. }
  59. }
  60. LRESULT _OnNCCreate(HWND hwnd, LPCREATESTRUCT pcs)
  61. {
  62. NSC *pns = LocalAlloc(LPTR, sizeof(NSC));
  63. if (pns)
  64. {
  65. pns->hwnd = hwnd;
  66. pns->hwndParent = pcs->hwndParent;
  67. pns->style = pcs->style;
  68. pns->id = (UINT)pcs->hMenu;
  69. // remove border styles from our window that we propogated to
  70. // our child control
  71. SetWindowLong(hwnd, GWL_STYLE, pcs->style & ~WS_BORDER);
  72. SetWindowLong(hwnd, GWL_EXSTYLE, pcs->dwExStyle & ~WS_EX_CLIENTEDGE);
  73. pns->hwndTree = CreateWindowEx(pcs->dwExStyle, WC_TREEVIEW, NULL,
  74. (pcs->style & 0xFFFF0000) | (WS_CHILD | TVS_HASBUTTONS | TVS_EDITLABELS | TVS_SHOWSELALWAYS), // TVS_HASLINES |
  75. pcs->x, pcs->y, pcs->cx, pcs->cy,
  76. hwnd, (HMENU)ID_CONTROL, pcs->hInstance, NULL);
  77. if (pns->hwndTree)
  78. {
  79. SHFILEINFO sfi;
  80. HIMAGELIST himl = (HIMAGELIST)SHGetFileInfo("C:\\", 0, &sfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
  81. SetWindowLong(hwnd, 0, (LONG)pns);
  82. TreeView_SetImageList(pns->hwndTree, himl, TVSIL_NORMAL);
  83. if (pns->style & NSS_DROPTARGET)
  84. CTreeDropTarget_Register(pns);
  85. return TRUE; // success
  86. }
  87. LocalFree(pns);
  88. }
  89. DebugMsg(DM_ERROR, "Failing NameSpaceControl create");
  90. return FALSE; // fail the create
  91. }
  92. void _ReleaseCachedShellFolder(NSC *pns)
  93. {
  94. if (pns->psfCache)
  95. {
  96. Release(pns->psfCache);
  97. pns->psfCache = NULL;
  98. pns->htiCache = NULL;
  99. }
  100. }
  101. void _ReleaseRootFolder(NSC *pns)
  102. {
  103. if (pns->psfRoot)
  104. {
  105. Release(pns->psfRoot);
  106. pns->psfRoot = NULL;
  107. if (pns->pidlRoot)
  108. {
  109. ILFree(pns->pidlRoot);
  110. pns->pidlRoot = NULL;
  111. }
  112. }
  113. }
  114. void _OnNCDestroy(NSC *pns)
  115. {
  116. _ReleaseRootFolder(pns);
  117. Assert(pns->pidlRoot == NULL);
  118. // ILFree(pns->pidlRoot);
  119. // pns->pidlRoot = NULL;
  120. _ReleaseCachedShellFolder(pns);
  121. if (pns->style & NSS_DROPTARGET)
  122. CTreeDropTarget_Revoke(pns);
  123. _RegisterNotify(pns, FALSE);
  124. LocalFree(pns);
  125. }
  126. // builds a fully qualified IDLIST from a given tree node by walking up the tree
  127. // be sure to free this when you are done!
  128. LPITEMIDLIST _GetFullIDList(HWND hwndTree, HTREEITEM hti)
  129. {
  130. LPITEMIDLIST pidl;
  131. TV_ITEM tvi;
  132. Assert(hti);
  133. // now lets get the information about the item
  134. tvi.mask = TVIF_PARAM | TVIF_HANDLE;
  135. tvi.hItem = hti;
  136. if (!TreeView_GetItem(hwndTree, &tvi))
  137. {
  138. DebugMsg(DM_ERROR, "bogus tree item passed");
  139. return NULL;
  140. }
  141. pidl = ILClone((LPITEMIDLIST)tvi.lParam);
  142. // Now walk up parents.
  143. while ((tvi.hItem = TreeView_GetParent(hwndTree, tvi.hItem)) && pidl)
  144. {
  145. LPITEMIDLIST pidlT;
  146. if (!TreeView_GetItem(hwndTree, &tvi))
  147. return pidl; // will assume I messed up...
  148. pidlT = ILCombine((LPITEMIDLIST)tvi.lParam, pidl);
  149. ILFree(pidl);
  150. pidl = pidlT;
  151. }
  152. return pidl;
  153. }
  154. /*
  155. pitem->flags;
  156. pitem->hitem;
  157. pitem->psf;
  158. pitem->pidl;
  159. pitem->dwAttributes;
  160. */
  161. BOOL _GetItem(NSC *pns, NSC_ITEMINFO *pitem)
  162. {
  163. // BUGBUG: validate pitem->hitem
  164. if (pitem->flags & NSIF_HITEM)
  165. {
  166. }
  167. if (pitem->flags & NSIF_FOLDER)
  168. {
  169. Assert(!(pitem->flags & NSIF_PARENTFOLDER)); // should be exclusive
  170. }
  171. else if (pitem->flags & NSIF_PARENTFOLDER)
  172. {
  173. }
  174. if (pitem->flags & (NSIF_IDLIST | NSIF_FULLIDLIST))
  175. {
  176. pitem->pidl = NULL;
  177. if (pitem->flags & NSIF_FULLIDLIST)
  178. pitem->pidl = _GetFullIDList(pns->hwndTree, (HTREEITEM)pitem->hitem);
  179. }
  180. else
  181. {
  182. TV_ITEM tvi;
  183. tvi.mask = TVIF_PARAM | TVIF_HANDLE;
  184. tvi.hItem = (HTREEITEM)pitem->hitem;
  185. if (TreeView_GetItem(pns->hwndTree, &tvi))
  186. {
  187. pitem->pidl = (LPITEMIDLIST)tvi.lParam;
  188. }
  189. }
  190. if (pitem->flags & NSIF_ATTRIBUTES)
  191. {
  192. }
  193. return TRUE;
  194. }
  195. BOOL _SetItemNotify(NSC *pns, NSI_FLAGS flags)
  196. {
  197. // NS_NOTIFY nsn;
  198. return TRUE;
  199. }
  200. // Some helper functions for processing the dialog
  201. HTREEITEM _AddItemToTree(HWND hwndTree, HTREEITEM htiParent, LPCITEMIDLIST pidl, int cChildren)
  202. {
  203. TV_INSERTSTRUCT tii;
  204. // Initialize item to add with callback for everything
  205. tii.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_CHILDREN;
  206. tii.hParent = htiParent;
  207. tii.hInsertAfter = TVI_FIRST;
  208. tii.item.iImage = I_IMAGECALLBACK;
  209. tii.item.iSelectedImage = I_IMAGECALLBACK;
  210. tii.item.pszText = LPSTR_TEXTCALLBACK; //
  211. tii.item.cChildren = cChildren; // Assume it has children
  212. tii.item.lParam = (LPARAM)pidl;
  213. return TreeView_InsertItem(hwndTree, &tii);
  214. }
  215. void _ExpandTree(NSC *pns, HTREEITEM htiRoot, int iDepth)
  216. {
  217. HTREEITEM hti;
  218. if (iDepth == 0)
  219. return;
  220. TreeView_Expand(pns->hwndTree, htiRoot, TVE_EXPAND);
  221. if (iDepth == 1)
  222. return; // avoid useless loop
  223. // recurse to children, expanding them
  224. for (hti = TreeView_GetChild(pns->hwndTree, htiRoot); hti; hti = TreeView_GetNextSibling(pns->hwndTree, hti))
  225. {
  226. _ExpandTree(pns, hti, iDepth - 1);
  227. }
  228. }
  229. // set the root of the name space control.
  230. //
  231. // in:
  232. // pidlRoot NULL means the desktop
  233. // HIWORD 0 -> LOWORD == ID of special folder (CSIDL_* values)
  234. BOOL _OnSetRoot(NSC *pns, NSC_SETROOT *psr)
  235. {
  236. _ReleaseRootFolder(pns);
  237. if (psr->psf)
  238. {
  239. pns->psfRoot = psr->psf;
  240. }
  241. else if (FAILED(SHGetDesktopFolder(&pns->psfRoot)))
  242. {
  243. DebugMsg(DM_ERROR, "Failed to get desktop folder");
  244. return FALSE;
  245. }
  246. AddRef(pns->psfRoot); // we hang on to this
  247. // HIWORD/LOWORD stuff is to support pidl IDs instead of full pidl here
  248. if (HIWORD(psr->pidlRoot))
  249. pns->pidlRoot = ILClone(psr->pidlRoot);
  250. else
  251. {
  252. Assert(psr->psf == NULL); // special folders are only valid for desktop shell folder
  253. SHGetSpecialFolderLocation(NULL, LOWORD(psr->pidlRoot) ? LOWORD(psr->pidlRoot) : CSIDL_DESKTOP, &pns->pidlRoot);
  254. }
  255. if (pns->pidlRoot)
  256. {
  257. HTREEITEM htiRoot = _AddItemToTree(pns->hwndTree, TVI_ROOT, pns->pidlRoot, 1);
  258. if (htiRoot)
  259. {
  260. _ExpandTree(pns, htiRoot, psr->iExpandDepth);
  261. TreeView_SelectItem(pns->hwndTree, htiRoot);
  262. _RegisterNotify(pns, TRUE);
  263. return TRUE;
  264. }
  265. }
  266. DebugMsg(DM_ERROR, "set root failed");
  267. _ReleaseRootFolder(pns);
  268. return FALSE;
  269. }
  270. // cache the shell folder for a given tree item
  271. // in:
  272. // hti tree node to cache shell folder for. this my be
  273. // NULL indicating the root item.
  274. //
  275. BOOL _CacheShellFolder(NSC *pns, HTREEITEM hti)
  276. {
  277. // in the cache?
  278. if ((hti != pns->htiCache) || (pns->psfCache == NULL))
  279. {
  280. // cache miss, do the work
  281. LPITEMIDLIST pidl;
  282. _ReleaseCachedShellFolder(pns);
  283. if (hti)
  284. pidl = _GetFullIDList(pns->hwndTree, hti);
  285. else
  286. {
  287. // root item...
  288. pidl = ILClone(pns->pidlRoot);
  289. if (pidl && pidl->mkid.cb != 0)
  290. {
  291. ILRemoveLastID(pidl);
  292. }
  293. }
  294. if (pidl)
  295. {
  296. // special case for root of evil...
  297. if (pidl->mkid.cb == 0)
  298. {
  299. pns->psfCache = pns->psfRoot;
  300. AddRef(pns->psfCache); // to match ref count
  301. }
  302. else
  303. {
  304. pns->psfRoot->lpVtbl->BindToObject(pns->psfRoot, pidl, NULL, &IID_IShellFolder, &pns->psfCache);
  305. }
  306. ILFree(pidl);
  307. if (pns->psfCache)
  308. {
  309. pns->htiCache = hti; // this is for the cache match
  310. return TRUE;
  311. }
  312. else
  313. DebugMsg(DM_ERROR, "failed to get cached shell folder");
  314. }
  315. else
  316. DebugMsg(DM_ERROR, "failed to get create PIDL for cached shell folder");
  317. return FALSE;
  318. }
  319. return TRUE;
  320. }
  321. // pidlItem is typically a relative pidl, except in the case of the root where
  322. // it can be a fully qualified pidl
  323. LPITEMIDLIST _CacheParentShellFolder(NSC *pns, HTREEITEM hti, LPITEMIDLIST pidl)
  324. {
  325. Assert(hti);
  326. if (_CacheShellFolder(pns, TreeView_GetParent(pns->hwndTree, hti)))
  327. {
  328. if (pidl == NULL)
  329. {
  330. TV_ITEM tvi;
  331. tvi.mask = TVIF_PARAM | TVIF_HANDLE;
  332. tvi.hItem = hti;
  333. if (!TreeView_GetItem(pns->hwndTree, &tvi))
  334. return NULL;
  335. pidl = (LPITEMIDLIST)tvi.lParam;
  336. }
  337. return ILFindLastID(pidl);
  338. }
  339. return NULL;
  340. }
  341. int CALLBACK _TreeCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  342. {
  343. IShellFolder *psf = (IShellFolder *)lParamSort;
  344. HRESULT hres = psf->lpVtbl->CompareIDs(psf, 0, (LPITEMIDLIST)lParam1, (LPITEMIDLIST)lParam2);
  345. Assert(SUCCEEDED(hres));
  346. return (short)SCODE_CODE(hres);
  347. }
  348. void _Sort(NSC *pns, HTREEITEM hti, IShellFolder *psf)
  349. {
  350. TV_SORTCB scb;
  351. scb.hParent = hti;
  352. scb.lpfnCompare = _TreeCompare;
  353. scb.lParam = (LPARAM)psf;
  354. TreeView_SortChildrenCB(pns->hwndTree, &scb, FALSE);
  355. }
  356. // filter function... let clients filter what gets added here
  357. BOOL _ShouldAdd(NSC *pns, LPCITEMIDLIST pidl)
  358. {
  359. #if 0
  360. //
  361. // We need to special case here in the netcase where we onlyu
  362. // browse down to workgroups...
  363. //
  364. //
  365. // Here is where I also need to special case to not go below
  366. // workgroups when the appropriate option is set.
  367. //
  368. bType = SIL_GetType(pidl);
  369. if ((pns->ulFlags & BIF_DONTGOBELOWDOMAIN) && (bType & SHID_NET))
  370. {
  371. switch (bType & (SHID_NET | SHID_INGROUPMASK))
  372. {
  373. case SHID_NET_SERVER:
  374. ILFree(pidl); // Dont want to add this one
  375. continue; // Try the next one
  376. case SHID_NET_DOMAIN:
  377. cChildren = 0; // Force to not have children;
  378. }
  379. }
  380. else if ((pns->ulFlags & BIF_BROWSEFORCOMPUTER) && (bType & SHID_NET))
  381. {
  382. if ((bType & (SHID_NET | SHID_INGROUPMASK)) == SHID_NET_SERVER)
  383. cChildren = 0; // Don't expand below it...
  384. }
  385. else if (fPrinterTest)
  386. {
  387. // Special case when we are only allowing printers.
  388. // for now I will simply key on the fact that it is non-FS.
  389. ULONG ulAttr = SFGAO_FILESYSTEM;
  390. psf->lpVtbl->GetAttributesOf(psf, 1, &pidl, &ulAttr);
  391. if ((ulAttr & SFGAO_FILESYSTEM)== 0)
  392. {
  393. cChildren = 0; // Force to not have children;
  394. }
  395. else
  396. {
  397. ILFree(pidl); // Dont want to add this one
  398. continue; // Try the next one
  399. }
  400. }
  401. #endif
  402. // send notify up to partent to let them filter
  403. return TRUE;
  404. }
  405. BOOL _OnItemExpanding(NSC *pns, NM_TREEVIEW *pnm)
  406. {
  407. IShellFolder *psf;
  408. IEnumIDList *penum; // Enumerator in use.
  409. DWORD grfFlags = SHCONTF_FOLDERS;
  410. int cAdded = 0;
  411. if ((pnm->action != TVE_EXPAND) || (pnm->itemNew.state & TVIS_EXPANDEDONCE))
  412. return FALSE;
  413. Assert(pnm->itemNew.hItem);
  414. if (!_CacheShellFolder(pns, pnm->itemNew.hItem))
  415. return FALSE;
  416. psf = pns->psfCache;
  417. AddRef(psf); // hang on as adding items may change the cached psfCache
  418. #if 0
  419. // Need to do a couple of special cases here to allow us to
  420. // browse for a network printer. In this case if we are at server
  421. // level we then need to change what we search for non folders when
  422. // we are the level of a server.
  423. if (pns->ulFlags & BIF_BROWSEFORPRINTER)
  424. {
  425. grfFlags = SHCONTF_FOLDERS | SHCONTF_NETPRINTERSRCH;
  426. pidl = ILFindLastID(pidlToExpand);
  427. bType = SIL_GetType(pidl);
  428. fPrinterTest = ((bType & (SHID_NET|SHID_INGROUPMASK))==SHID_NET_SERVER);
  429. if (fPrinterTest)
  430. grfFlags |= SHCONTF_NONFOLDERS;
  431. }
  432. else
  433. #endif
  434. if (pns->style & NSS_SHOWNONFOLDERS)
  435. grfFlags |= SHCONTF_NONFOLDERS;
  436. if (pns->style & NSS_SHOWHIDDEN)
  437. grfFlags |= SHCONTF_INCLUDEHIDDEN;
  438. // passing NULL hwnd makes the enum not put up UI. this is what we want
  439. // in auto-expand cases
  440. if (SUCCEEDED(psf->lpVtbl->EnumObjects(psf,
  441. pns->fAutoExpanding ? NULL : pns->hwnd, grfFlags, &penum)))
  442. {
  443. UINT celt;
  444. LPITEMIDLIST pidl;
  445. while (penum->lpVtbl->Next(penum, 1, &pidl, &celt) == S_OK && celt == 1)
  446. {
  447. int cChildren = I_CHILDRENCALLBACK; // Do call back for children
  448. if (_ShouldAdd(pns, pidl))
  449. {
  450. _AddItemToTree(pns->hwndTree, pnm->itemNew.hItem, pidl, cChildren);
  451. cAdded++;
  452. }
  453. else
  454. {
  455. ILFree(pidl);
  456. }
  457. }
  458. ReleaseLast(penum);
  459. _Sort(pns, pnm->itemNew.hItem, psf);
  460. }
  461. Release(psf);
  462. // If we did not add anything we should update this item to let
  463. // the user know something happened.
  464. //
  465. if (cAdded == 0)
  466. {
  467. TV_ITEM tvi;
  468. tvi.mask = TVIF_CHILDREN | TVIF_HANDLE; // only change the number of children
  469. tvi.hItem = pnm->itemNew.hItem;
  470. tvi.cChildren = 0;
  471. TreeView_SetItem(pns->hwndTree, &tvi);
  472. }
  473. return TRUE;
  474. }
  475. void _OnDeleteItem(NSC *pns, NM_TREEVIEW *pnm)
  476. {
  477. ILFree((LPITEMIDLIST)pnm->itemOld.lParam);
  478. }
  479. void _GetIconIndex(NSC *pns, LPITEMIDLIST pidl, ULONG ulAttrs, TVITEM *pitem)
  480. {
  481. IShellIcon *psi;
  482. if (SUCCEEDED(QueryInterface(pns->psfCache, &IID_IShellIcon, &psi)))
  483. {
  484. if (psi->lpVtbl->GetIconOf(psi, pidl, 0, &pitem->iImage) == S_OK)
  485. {
  486. if (!(ulAttrs & SFGAO_FOLDER) || FAILED(psi->lpVtbl->GetIconOf(psi, pidl, GIL_OPENICON, &pitem->iSelectedImage)))
  487. {
  488. pitem->iSelectedImage = pitem->iImage;
  489. }
  490. Release(psi);
  491. return;
  492. }
  493. Release(psi);
  494. }
  495. {
  496. // slow way...
  497. LPITEMIDLIST pidlFull = _GetFullIDList(pns->hwndTree, pitem->hItem);
  498. if (pidlFull)
  499. {
  500. SHFILEINFO sfi;
  501. SHGetFileInfo((LPCSTR)pidlFull, 0, &sfi, sizeof(SHFILEINFO), SHGFI_PIDL | SHGFI_SYSICONINDEX); // | SHGFI_SMALLICON
  502. pitem->iImage = sfi.iIcon;
  503. if (!(ulAttrs & SFGAO_FOLDER))
  504. pitem->iSelectedImage = pitem->iImage;
  505. else
  506. {
  507. SHGetFileInfo((LPCSTR)pidlFull, 0, &sfi, sizeof(SHFILEINFO), SHGFI_PIDL | SHGFI_OPENICON | SHGFI_SYSICONINDEX);
  508. pitem->iSelectedImage = sfi.iIcon;
  509. }
  510. ILFree(pidlFull);
  511. }
  512. }
  513. }
  514. int _GetChildren(NSC *pns, IShellFolder *psf, LPCITEMIDLIST pidl, ULONG ulAttrs)
  515. {
  516. int cChildren = 0; // assume none
  517. if (ulAttrs & SFGAO_FOLDER)
  518. {
  519. if (pns->style & NSS_SHOWNONFOLDERS)
  520. {
  521. // there is no SFGAO_ bit that includes non folders so we need to enum
  522. IShellFolder *psfItem;
  523. if (SUCCEEDED(psf->lpVtbl->BindToObject(psf, pidl, NULL, &IID_IShellFolder, &psfItem)))
  524. {
  525. // if we are showing non folders we have to do an enum to peek down at items below
  526. IEnumIDList *penum;
  527. DWORD grfFlags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
  528. if (pns->style & NSS_SHOWHIDDEN)
  529. grfFlags |= SHCONTF_INCLUDEHIDDEN;
  530. if (SUCCEEDED(psfItem->lpVtbl->EnumObjects(psfItem, NULL, grfFlags, &penum)))
  531. {
  532. UINT celt;
  533. LPITEMIDLIST pidlTemp;
  534. if (penum->lpVtbl->Next(penum, 1, &pidlTemp, &celt) == S_OK && celt == 1)
  535. {
  536. ILFree(pidlTemp);
  537. cChildren = 1;
  538. }
  539. Release(penum);
  540. }
  541. Release(psfItem);
  542. }
  543. }
  544. else
  545. {
  546. // if just folders we can peek at the attributes
  547. ULONG ulAttrs = SFGAO_HASSUBFOLDER;
  548. psf->lpVtbl->GetAttributesOf(psf, 1, &pidl, &ulAttrs);
  549. cChildren = (ulAttrs & SFGAO_HASSUBFOLDER) ? 1 : 0;
  550. }
  551. }
  552. return cChildren;
  553. }
  554. void _OnGetDisplayInfo(NSC *pns, TV_DISPINFO *pnm)
  555. {
  556. LPITEMIDLIST pidl = _CacheParentShellFolder(pns, pnm->item.hItem, (LPITEMIDLIST)pnm->item.lParam);
  557. Assert(pidl);
  558. if (pidl == NULL)
  559. return;
  560. Assert(pns->psfCache);
  561. Assert(pnm->item.mask & (TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_CHILDREN));
  562. if (pnm->item.mask & TVIF_TEXT)
  563. {
  564. STRRET str;
  565. pns->psfCache->lpVtbl->GetDisplayNameOf(pns->psfCache, pidl, SHGDN_INFOLDER, &str);
  566. StrRetToStrN(pnm->item.pszText, pnm->item.cchTextMax, &str, pidl);
  567. }
  568. // make sure we set the attributes for those flags that need them
  569. if (pnm->item.mask & (TVIF_CHILDREN | TVIF_IMAGE | TVIF_SELECTEDIMAGE))
  570. {
  571. ULONG ulAttrs = SFGAO_FOLDER;
  572. pns->psfCache->lpVtbl->GetAttributesOf(pns->psfCache, 1, &pidl, &ulAttrs);
  573. // Also see if this guy has any child folders
  574. if (pnm->item.mask & TVIF_CHILDREN)
  575. {
  576. pnm->item.cChildren = _GetChildren(pns, pns->psfCache, pidl, ulAttrs);
  577. }
  578. if (pnm->item.mask & (TVIF_IMAGE | TVIF_SELECTEDIMAGE))
  579. {
  580. // We now need to map the item into the right image index.
  581. _GetIconIndex(pns, pidl, ulAttrs, &pnm->item);
  582. }
  583. }
  584. // force the treeview to store this so we don't get called back again
  585. pnm->item.mask |= TVIF_DI_SETITEM;
  586. }
  587. // send up the sel changed to let clients enable/disable buttons, etc.
  588. void _OnSelChanged(NSC *pns, LPNM_TREEVIEW pnm)
  589. {
  590. #if 0
  591. LPITEMIDLIST pidl;
  592. ULONG ulAttrs = SFGAO_FILESYSTEM;
  593. BYTE bType;
  594. // We only need to do anything if we only want to return File system
  595. // level objects.
  596. if ((pns->ulFlags & (BIF_RETURNONLYFSDIRS | BIF_RETURNFSANCESTORS | BIF_BROWSEFORPRINTER | BIF_BROWSEFORCOMPUTER)) == 0)
  597. goto NotifySelChange;
  598. // We need to get the attributes of this object...
  599. if (_CacheParentShellFolder(pns, pnm->itemNew.hItem, (LPITEMIDLIST)pnm->itemNew.lParam))
  600. {
  601. BOOL fEnable = TRUE;
  602. bType = SIL_GetType(pidl);
  603. if ((pns->ulFlags & (BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS)) != 0)
  604. {
  605. int i;
  606. // if this is the root pidl, then do a get attribs on 0
  607. // so that we'll get the attributes on the root, rather than
  608. // random returned values returned by FSFolder
  609. if (ILIsEmpty(pidl)) {
  610. i = 0;
  611. } else
  612. i = 1;
  613. pns->psfCache->lpVtbl->GetAttributesOf(pns->psfCache,
  614. i, &pidl, &ulAttrs);
  615. fEnable = (((ulAttrs & SFGAO_FILESYSTEM) && (pns->ulFlags & BIF_RETURNONLYFSDIRS)) ||
  616. ((ulAttrs & SFGAO_FILESYSANCESTOR) && (pns->ulFlags & BIF_RETURNFSANCESTORS))) ||
  617. ((bType & (SHID_NET | SHID_INGROUPMASK)) == SHID_NET_SERVER);
  618. }
  619. else if ((pns->ulFlags & BIF_BROWSEFORCOMPUTER) != 0)
  620. {
  621. fEnable = ((bType & (SHID_NET | SHID_INGROUPMASK)) == SHID_NET_SERVER);
  622. }
  623. else if ((pns->ulFlags & BIF_BROWSEFORPRINTER) != 0)
  624. {
  625. // Printers are of type Share and usage Print...
  626. fEnable = ((bType & (SHID_NET | SHID_INGROUPMASK)) == SHID_NET_SHARE);
  627. }
  628. EnableWindow(GetDlgItem(pns->hwnd, IDOK), fEnable);
  629. }
  630. NotifySelChange:
  631. if (pns->lpfn)
  632. {
  633. pidl = _GetFullIDList(pns->hwndTree, pnm->itemNew.hItem);
  634. BFSFCallback(pns, BFFM_SELCHANGED, (LPARAM)pidl);
  635. ILFree(pidl);
  636. }
  637. #endif
  638. }
  639. const char c_szCut[] = "cut";
  640. const char c_szRename[] = "rename";
  641. LRESULT _ContextMenu(NSC *pns, short x, short y)
  642. {
  643. HTREEITEM hti;
  644. POINT ptPopup; // in screen coordinate
  645. if (x == -1 && y == -1)
  646. {
  647. // Keyboard-driven: Get the popup position from the selected item.
  648. hti = TreeView_GetSelection(pns->hwndTree);
  649. if (hti)
  650. {
  651. RECT rc;
  652. //
  653. // Note that TV_GetItemRect returns it in client coordinate!
  654. //
  655. TreeView_GetItemRect(pns->hwndTree, hti, &rc, TRUE);
  656. ptPopup.x = (rc.left + rc.right) / 2;
  657. ptPopup.y = (rc.top + rc.bottom) / 2;
  658. MapWindowPoints(pns->hwndTree, HWND_DESKTOP, &ptPopup, 1);
  659. }
  660. }
  661. else
  662. {
  663. TV_HITTESTINFO tvht;
  664. // Mouse-driven: Pick the treeitem from the position.
  665. ptPopup.x = x;
  666. ptPopup.y = y;
  667. tvht.pt = ptPopup;
  668. ScreenToClient(pns->hwndTree, &tvht.pt);
  669. hti = TreeView_HitTest(pns->hwndTree, &tvht);
  670. }
  671. if (hti)
  672. {
  673. LPCITEMIDLIST pidl = _CacheParentShellFolder(pns, hti, NULL);
  674. if (pidl)
  675. {
  676. IContextMenu *pcm;
  677. TreeView_SelectDropTarget(pns->hwndTree, hti);
  678. if (SUCCEEDED(pns->psfCache->lpVtbl->GetUIObjectOf(pns->psfCache, pns->hwnd, 1, &pidl, &IID_IContextMenu, NULL, &pcm)))
  679. {
  680. HMENU hmenu = CreatePopupMenu();
  681. if (hmenu)
  682. {
  683. UINT idCmd;
  684. pns->pcm = pcm; // for IContextMenu2 code
  685. pcm->lpVtbl->QueryContextMenu(pcm, hmenu, 0, 1, 0x7fff,
  686. CMF_EXPLORE | CMF_CANRENAME);
  687. // use pns->hwnd so menu msgs go there and I can forward them
  688. // using IContextMenu2 so "Sent To" works
  689. idCmd = TrackPopupMenu(hmenu,
  690. TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
  691. ptPopup.x, ptPopup.y, 0, pns->hwnd, NULL);
  692. if (idCmd)
  693. {
  694. char szCommandString[64];
  695. BOOL fHandled = FALSE;
  696. BOOL fCutting = FALSE;
  697. // We need to special case the rename command
  698. if (SUCCEEDED(pcm->lpVtbl->GetCommandString(pcm, idCmd - 1,
  699. 0, NULL, szCommandString, sizeof(szCommandString))))
  700. {
  701. if (lstrcmpi(szCommandString, c_szRename)==0)
  702. {
  703. TreeView_EditLabel(pns->hwndTree, hti);
  704. fHandled = TRUE;
  705. }
  706. else if (!lstrcmpi(szCommandString, c_szCut))
  707. {
  708. fCutting = TRUE;
  709. }
  710. }
  711. if (!fHandled)
  712. {
  713. CMINVOKECOMMANDINFO ici = {
  714. sizeof(CMINVOKECOMMANDINFO),
  715. 0L,
  716. pns->hwndTree,
  717. MAKEINTRESOURCE(idCmd - 1),
  718. NULL, NULL,
  719. SW_NORMAL,
  720. };
  721. HRESULT hres = pcm->lpVtbl->InvokeCommand(pcm, &ici);
  722. if (fCutting && SUCCEEDED(hres))
  723. {
  724. TV_ITEM tvi;
  725. tvi.mask = TVIF_STATE;
  726. tvi.stateMask = TVIS_CUT;
  727. tvi.state = TVIS_CUT;
  728. tvi.hItem = hti;
  729. TreeView_SetItem(pns->hwndTree, &tvi);
  730. // pns->hwndNextViewer = SetClipboardViewer(pns->hwndTree);
  731. // pns->htiCut = hti;
  732. }
  733. }
  734. }
  735. DestroyMenu(hmenu);
  736. pns->pcm = NULL;
  737. }
  738. ReleaseLast(pcm);
  739. }
  740. TreeView_SelectDropTarget(pns->hwndTree, NULL);
  741. }
  742. }
  743. return 0;
  744. }
  745. LRESULT _OnBeginLabelEdit(NSC *pns, TV_DISPINFO *ptvdi)
  746. {
  747. BOOL fCantRename = TRUE;
  748. LPCITEMIDLIST pidl = _CacheParentShellFolder(pns, ptvdi->item.hItem, NULL);
  749. if (pidl)
  750. {
  751. DWORD dwAttribs = SFGAO_CANRENAME;
  752. pns->psfCache->lpVtbl->GetAttributesOf(pns->psfCache, 1, &pidl, &dwAttribs);
  753. if (dwAttribs & SFGAO_CANRENAME)
  754. fCantRename = FALSE;
  755. }
  756. if (fCantRename)
  757. MessageBeep(0);
  758. return fCantRename;
  759. }
  760. LRESULT _OnEndLabelEdit(NSC *pns, TV_DISPINFO *ptvdi)
  761. {
  762. LPCITEMIDLIST pidl;
  763. // See if the user cancelled
  764. if (ptvdi->item.pszText == NULL)
  765. return TRUE; // Nothing to do here.
  766. Assert(ptvdi->item.hItem);
  767. pidl = _CacheParentShellFolder(pns, ptvdi->item.hItem, NULL);
  768. if (pidl)
  769. {
  770. UINT cch = lstrlen(ptvdi->item.pszText)+1;
  771. LPOLESTR pwsz = (LPOLESTR)LocalAlloc(LPTR, cch * sizeof(WCHAR));
  772. if (pwsz)
  773. {
  774. StrToOleStrN(pwsz, cch, ptvdi->item.pszText, -1);
  775. if (SUCCEEDED(pns->psfCache->lpVtbl->SetNameOf(pns->psfCache, pns->hwnd, pidl, pwsz, 0, NULL)))
  776. {
  777. // SHChangeNotifyHandleEvents();
  778. // NOTES: pidl is no longer valid here.
  779. //
  780. // Set the handle to NULL in the notification to let
  781. // the system know that the pointer is probably not
  782. // valid anymore.
  783. //
  784. ptvdi->item.hItem = NULL;
  785. }
  786. else
  787. {
  788. SendMessage(pns->hwndTree, TVM_EDITLABEL, (WPARAM)ptvdi->item.pszText, (LPARAM)ptvdi->item.hItem);
  789. }
  790. LocalFree((HLOCAL)pwsz);
  791. }
  792. }
  793. return 0; // We always return 0, "we handled it".
  794. }
  795. void _OnBeginDrag(NSC *pns, NM_TREEVIEW *pnmhdr)
  796. {
  797. LPCITEMIDLIST pidl = _CacheParentShellFolder(pns, pnmhdr->itemNew.hItem, NULL);
  798. if (pidl)
  799. {
  800. DWORD dwEffect = DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK;
  801. pns->psfCache->lpVtbl->GetAttributesOf(pns->psfCache, 1, &pidl, &dwEffect);
  802. dwEffect &= DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK;
  803. if (dwEffect)
  804. {
  805. IDataObject *pdtobj;
  806. HRESULT hres = pns->psfCache->lpVtbl->GetUIObjectOf(pns->psfCache, pns->hwnd, 1, &pidl, &IID_IDataObject, NULL, &pdtobj);
  807. if (SUCCEEDED(hres))
  808. {
  809. hres = OleInitialize(NULL);
  810. if (SUCCEEDED(hres))
  811. {
  812. IDropSource *pdsrc;
  813. if (SUCCEEDED(CDropSource_CreateInstance(&pdsrc)))
  814. {
  815. DWORD dwRet;
  816. pns->htiDragging = pnmhdr->itemNew.hItem;
  817. DoDragDrop(pdtobj, pdsrc, dwEffect, &dwRet);
  818. pns->htiDragging = NULL;
  819. DebugMsg(DM_TRACE, "DoDragDrop returns dwRet: %d", dwRet);
  820. Release(pdsrc);
  821. }
  822. OleUninitialize();
  823. }
  824. #if 0
  825. HIMAGELIST himlDrag = TreeView_CreateDragImage(pns->hwndTree, pnmhdr->itemNew.hItem);
  826. if (himlDrag)
  827. {
  828. if (DAD_SetDragImage(himlDrag, NULL))
  829. {
  830. SHDoDragDrop(hwndOwner, pdtobj, NULL, dwEffect, &dwEffect);
  831. DAD_SetDragImage((HIMAGELIST)-1, NULL);
  832. }
  833. else
  834. {
  835. DebugMsg(DM_TRACE, "sh ER - Tree_OnBeginDrag DAD_SetDragImage failed");
  836. Assert(0);
  837. }
  838. ImageList_Destroy(himlDrag);
  839. }
  840. #endif
  841. ReleaseLast(pdtobj);
  842. }
  843. }
  844. }
  845. }
  846. void _InvokeContextMenu(IShellFolder *psf, LPCITEMIDLIST pidl, HWND hwnd, LPCSTR pszVerb)
  847. {
  848. IContextMenu *pcm;
  849. if (SUCCEEDED(psf->lpVtbl->GetUIObjectOf(psf, hwnd, 1, &pidl, &IID_IContextMenu, NULL, &pcm)))
  850. {
  851. HMENU hmenu = CreatePopupMenu();
  852. if (hmenu)
  853. {
  854. pcm->lpVtbl->QueryContextMenu(pcm, hmenu, 0, 1, 255, pszVerb ? 0 : CMF_DEFAULTONLY);
  855. if (pszVerb == NULL)
  856. pszVerb = MAKEINTRESOURCE(GetMenuDefaultItem(hmenu, MF_BYCOMMAND, 0) - 1);
  857. if (pszVerb)
  858. {
  859. CMINVOKECOMMANDINFOEX ici = {
  860. sizeof(CMINVOKECOMMANDINFOEX),
  861. 0L,
  862. hwnd,
  863. pszVerb,
  864. NULL, NULL,
  865. SW_NORMAL,
  866. };
  867. pcm->lpVtbl->InvokeCommand(pcm, (LPCMINVOKECOMMANDINFO)&ici);
  868. }
  869. DestroyMenu(hmenu);
  870. }
  871. Release(pcm);
  872. }
  873. }
  874. void _DoVerb(NSC *pns, HTREEITEM hti, LPCSTR pszVerb)
  875. {
  876. hti = hti ? hti : TreeView_GetSelection(pns->hwndTree);
  877. if (hti)
  878. {
  879. LPCITEMIDLIST pidl = _CacheParentShellFolder(pns, hti, NULL);
  880. if (pidl)
  881. {
  882. _InvokeContextMenu(pns->psfCache, pidl, pns->hwnd, pszVerb);
  883. }
  884. }
  885. }
  886. BOOL _DoDlbClick(NSC *pns)
  887. {
  888. HTREEITEM hti = TreeView_GetSelection(pns->hwndTree);
  889. if (hti)
  890. {
  891. LPCITEMIDLIST pidl = _CacheParentShellFolder(pns, hti, NULL);
  892. if (pidl)
  893. {
  894. ULONG ulAttrs = SFGAO_FOLDER;
  895. pns->psfCache->lpVtbl->GetAttributesOf(pns->psfCache, 1, &pidl, &ulAttrs);
  896. if (ulAttrs & SFGAO_FOLDER)
  897. return FALSE; // do default action (expand/collapse)
  898. _InvokeContextMenu(pns->psfCache, pidl, pns->hwnd, NULL);
  899. }
  900. }
  901. return TRUE;
  902. }
  903. HTREEITEM _GetNodeFromIDList(LPITEMIDLIST pidl)
  904. {
  905. return NULL;
  906. }
  907. BOOL TryQuickRename(LPITEMIDLIST pidl, LPITEMIDLIST pidlExtra)
  908. {
  909. #if 0
  910. LPOneTreeNode lpnSrc;
  911. BOOL fRet = FALSE;
  912. // This can happen when a folder is moved from a "rooted" Explorer outside
  913. // of the root
  914. if (!pidl || !pidlExtra)
  915. return FALSE;
  916. // this one was deleted
  917. lpnSrc = _GetNodeFromIDList(pidl, 0);
  918. if (!lpnSrc)
  919. return FALSE;
  920. if (lpnSrc == s_lpnRoot)
  921. {
  922. OTInvalidateRoot();
  923. return TRUE;
  924. }
  925. else
  926. {
  927. // this one was created
  928. LPITEMIDLIST pidlClone = ILClone(pidlExtra);
  929. if (pidlClone)
  930. {
  931. LPOneTreeNode lpnDestParent;
  932. ILRemoveLastID(pidlClone);
  933. // if the parent isn't created yet, let's not bother
  934. lpnDestParent = _GetNodeFromIDList(pidlClone, 0);
  935. ILFree(pidlClone);
  936. if (lpnDestParent)
  937. {
  938. LPITEMIDLIST pidlLast = OTGetRealFolderIDL(lpnDestParent, ILFindLastID(pidlExtra));
  939. if (pidlLast)
  940. {
  941. LPSHELLFOLDER psf = OTBindToFolder(lpnDestParent);
  942. if (psf)
  943. {
  944. OTAddRef(lpnSrc); // addref because AdoptKid doesn't and OTAbandonKid releases
  945. // remove lpnSrc from its parent's list.
  946. OTAbandonKid(lpnSrc->lpnParent, lpnSrc);
  947. // invalidate the new node's parent to get any children flags right
  948. OTInvalidateNode(lpnDestParent);
  949. // free any cached folders
  950. OTSweepFolders(lpnSrc);
  951. SFCFreeNode(lpnSrc);
  952. OTFreeNodeData(lpnSrc);
  953. lpnSrc->pidl = pidlLast;
  954. lpnSrc->lpnParent = lpnDestParent;
  955. OTUpdateNodeName(psf, lpnSrc);
  956. AdoptKid(lpnDestParent, lpnSrc);
  957. fRet = TRUE;
  958. IUnknown_Release(psf);
  959. }
  960. else
  961. {
  962. ILFree(pidlLast);
  963. }
  964. }
  965. }
  966. }
  967. }
  968. return fRet;
  969. #else
  970. return FALSE;
  971. #endif
  972. }
  973. void _DoChangeNotify(NSC *pns, LONG lEvent, LPITEMIDLIST pidl, LPITEMIDLIST pidlExtra)
  974. {
  975. #if 0
  976. switch(lEvent)
  977. {
  978. case SHCNE_RENAMEFOLDER:
  979. // first try to just swap the nodes if it's true rename (not a move)
  980. if (!TryQuickRename(pidl, pidlExtra))
  981. {
  982. // Rename is special. We need to invalidate both
  983. // the pidl and the pidlExtra. so we call ourselves
  984. _DoHandleChangeNotify(pns, 0, pidlExtra, NULL);
  985. }
  986. break;
  987. case SHCNE_RMDIR:
  988. if (ILIsEmpty(pidl)) {
  989. // we've deleted the desktop dir.
  990. lpNode = s_lpnRoot;
  991. OTInvalidateRoot();
  992. break;
  993. }
  994. // Sitemaps are "inserted" items. We need the ability to remove them.
  995. // Unless the "fInserted" flag is reset, we can not remove them.
  996. if(lpNode = _GetNodeFromIDList(pidl, 0))
  997. {
  998. if(lpNode->fInserted)
  999. lpNode->fInserted = FALSE;
  1000. lpNode = OTGetParent(lpNode);
  1001. break;
  1002. }
  1003. case 0:
  1004. case SHCNE_MKDIR:
  1005. case SHCNE_DRIVEADD:
  1006. case SHCNE_DRIVEREMOVED:
  1007. if (pidl)
  1008. {
  1009. LPITEMIDLIST pidlClone = ILClone(pidl);
  1010. if (pidlClone)
  1011. {
  1012. ILRemoveLastID(pidlClone);
  1013. lpNode = _GetNodeFromIDList(pidlClone, 0);
  1014. ILFree(pidlClone);
  1015. }
  1016. }
  1017. break;
  1018. case SHCNE_MEDIAINSERTED:
  1019. case SHCNE_MEDIAREMOVED:
  1020. lpNode = _GetNodeFromIDList(pidl, 0);
  1021. if (lpNode)
  1022. lpNode = lpNode->lpnParent;
  1023. break;
  1024. case SHCNE_DRIVEADDGUI:
  1025. case SHCNE_UPDATEITEM:
  1026. case SHCNE_NETSHARE:
  1027. case SHCNE_NETUNSHARE:
  1028. case SHCNE_UPDATEDIR:
  1029. lpNode = _GetNodeFromIDList(pidl, 0);
  1030. break;
  1031. case SHCNE_SERVERDISCONNECT:
  1032. // nuke all our kids and mark ourselves invalid
  1033. lpNode = _GetNodeFromIDList(pidl, 0);
  1034. if (lpNode && NodeHasKids(lpNode))
  1035. {
  1036. int i;
  1037. for (i = GetKidCount(lpNode) -1; i >= 0; i--) {
  1038. OTRelease(GetNthKid(lpNode, i));
  1039. }
  1040. DPA_Destroy(lpNode->hdpaKids);
  1041. lpNode->hdpaKids = KIDSUNKNOWN;
  1042. OTInvalidateNode(lpNode);
  1043. SFCFreeNode(lpNode);
  1044. } else {
  1045. lpNode = NULL;
  1046. }
  1047. break;
  1048. case SHCNE_ASSOCCHANGED:
  1049. break;
  1050. case SHCNE_UPDATEIMAGE:
  1051. if (pidl) {
  1052. InvalidateImageIndices();
  1053. DoInvalidateAll(s_lpnRoot, *(int UNALIGNED *)((BYTE*)pidl + 2));
  1054. }
  1055. break;
  1056. }
  1057. #endif
  1058. }
  1059. #if 0
  1060. void _OnChangeNotify(NSC *pns, WPARAM wParam, LPARAM lParam)
  1061. {
  1062. LPITEMIDLIST *ppidl;
  1063. LONG lEvent;
  1064. LPSHChangeNotificationLock pshcnl;
  1065. pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);
  1066. if (pshcnl)
  1067. {
  1068. _DoChangeNotify(pns, lEvent, ppidl[0], ppidl[1]);
  1069. SHChangeNotification_Unlock(pshcnl);
  1070. }
  1071. }
  1072. #endif
  1073. LRESULT CALLBACK NameSpaceWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1074. {
  1075. NSC *pns = (NSC *)GetWindowLong(hwnd, 0);
  1076. switch (uMsg) {
  1077. case WM_NCCREATE:
  1078. Assert(pns == NULL);
  1079. return _OnNCCreate(hwnd, (LPCREATESTRUCT)lParam);
  1080. case WM_NCDESTROY:
  1081. if (pns)
  1082. _OnNCDestroy(pns);
  1083. break;
  1084. case WM_SIZE:
  1085. if (pns->hwndTree)
  1086. MoveWindow(pns->hwndTree, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
  1087. break;
  1088. case WM_STYLECHANGED:
  1089. if (pns && wParam == GWL_STYLE)
  1090. pns->style = ((LPSTYLESTRUCT)lParam)->styleNew;
  1091. break;
  1092. case WM_SETFOCUS:
  1093. if (pns && pns->hwndTree)
  1094. SetFocus(pns->hwndTree);
  1095. break;
  1096. case WM_SETFONT:
  1097. case WM_GETFONT:
  1098. if (pns && pns->hwndTree)
  1099. return SendMessage(pns->hwndTree, uMsg, wParam, lParam);
  1100. break;
  1101. case WM_NOTIFY:
  1102. Assert(((NMHDR *)lParam)->idFrom == ID_CONTROL);
  1103. switch (((NMHDR *)lParam)->code) {
  1104. // we track this through WM_CONTEXTMENU
  1105. // case NM_RCLICK:
  1106. case NM_RETURN:
  1107. case NM_DBLCLK:
  1108. return _DoDlbClick(pns);
  1109. case TVN_GETDISPINFO:
  1110. _OnGetDisplayInfo(pns, (TV_DISPINFO *)lParam);
  1111. break;
  1112. case TVN_ITEMEXPANDING:
  1113. SetCursor(LoadCursor(NULL, IDC_WAIT));
  1114. _OnItemExpanding(pns, (LPNM_TREEVIEW)lParam);
  1115. break;
  1116. case TVN_ITEMEXPANDED:
  1117. SetCursor(LoadCursor(NULL, IDC_ARROW));
  1118. break;
  1119. case TVN_DELETEITEM:
  1120. _OnDeleteItem(pns, (LPNM_TREEVIEW)lParam);
  1121. break;
  1122. case TVN_SELCHANGED:
  1123. _OnSelChanged(pns, (LPNM_TREEVIEW)lParam);
  1124. break;
  1125. case TVN_BEGINLABELEDIT:
  1126. return _OnBeginLabelEdit(pns, (TV_DISPINFO *)lParam);
  1127. case TVN_ENDLABELEDIT:
  1128. return _OnEndLabelEdit(pns, (TV_DISPINFO *)lParam);
  1129. case TVN_BEGINDRAG:
  1130. case TVN_BEGINRDRAG:
  1131. _OnBeginDrag(pns, (NM_TREEVIEW *)lParam);
  1132. break;
  1133. }
  1134. break;
  1135. case WM_CONTEXTMENU:
  1136. _ContextMenu(pns, (short)LOWORD(lParam), (short)HIWORD(lParam));
  1137. break;
  1138. case WM_INITMENUPOPUP:
  1139. case WM_DRAWITEM:
  1140. case WM_MEASUREITEM:
  1141. if (pns->pcm)
  1142. {
  1143. IContextMenu2 *pcm2;
  1144. if (SUCCEEDED(QueryInterface(pns->pcm, &IID_IContextMenu2, &pcm2)))
  1145. {
  1146. pcm2->lpVtbl->HandleMenuMsg(pcm2, uMsg, wParam, lParam);
  1147. Release(pcm2);
  1148. }
  1149. }
  1150. break;
  1151. case WM_CHANGENOTIFY:
  1152. #define ppidl ((LPITEMIDLIST *)wParam)
  1153. _DoChangeNotify(pns, (LONG)lParam, ppidl[0], ppidl[1]);
  1154. break;
  1155. case NSM_SETROOT:
  1156. return _OnSetRoot(pns, (NSC_SETROOT *)lParam);
  1157. case NSM_GETIDLIST:
  1158. if (wParam)
  1159. return (LRESULT)_GetFullIDList(pns->hwndTree, (HTREEITEM)lParam);
  1160. else
  1161. {
  1162. TV_ITEM tvi;
  1163. // now lets get the information about the item
  1164. tvi.mask = TVIF_PARAM | TVIF_HANDLE;
  1165. tvi.hItem = (HTREEITEM)lParam;
  1166. if (!TreeView_GetItem(pns->hwndTree, &tvi))
  1167. return 0;
  1168. return (LRESULT)tvi.lParam; // relative PIDL
  1169. }
  1170. break;
  1171. case NSM_DOVERB:
  1172. _DoVerb(pns, NULL, (LPCSTR)lParam);
  1173. return TRUE;
  1174. default:
  1175. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  1176. }
  1177. return 0;
  1178. }
  1179. const char c_szNameSpaceClass[] = NAME_SPACE_CLASS;
  1180. BOOL NameSpace_RegisterClass(HINSTANCE hinst)
  1181. {
  1182. WNDCLASS wc;
  1183. InitCommonControls();
  1184. if (!GetClassInfo(hinst, c_szNameSpaceClass, &wc))
  1185. {
  1186. wc.lpfnWndProc = NameSpaceWndProc;
  1187. wc.hCursor = NULL;
  1188. wc.hIcon = NULL;
  1189. wc.lpszMenuName = NULL;
  1190. wc.hInstance = hinst;
  1191. wc.lpszClassName = c_szNameSpaceClass;
  1192. wc.hbrBackground = NULL;
  1193. wc.style = 0;
  1194. wc.cbWndExtra = sizeof(NSC *);
  1195. wc.cbClsExtra = 0;
  1196. return RegisterClass(&wc);
  1197. }
  1198. return TRUE;
  1199. }