nsc.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 177k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. // todo:
  2. //
  3. //      delayed keyboard selection so keyboard navigation does not generate a sel change
  4. //
  5. //      Partial expanded nodes in the tree "Tree Down" (TVIS_EXPANDPARTIAL) for net cases
  6. //
  7. //      Programbility:
  8. //          notifies - sel changed, node expanded, verb executed, etc.
  9. //          cmds - do verb, get item, etc.
  10. //      review chrisny:  consolidate state managing stuff for tree control in all areas
  11. //      review chrisny:  more error handling stuff.
  12. #include "priv.h"
  13. #include <shlobjp.h>    
  14. #include "nsc.h"
  15. #include "resource.h"
  16. #include "subsmgr.h"
  17. #include "favorite.h" //for IsSubscribed()
  18. #include "chanmgr.h"
  19. #include "chanmgrp.h"
  20. #include <mstask.h>    // TASK_TRIGGER
  21. #include "dpastuff.h"
  22. #include "nicotask.h"
  23. #include "uemapp.h"
  24. #include <findhlp.h>
  25. #include <mluisupp.h>
  26. #define TF_NSC      0x00002000
  27. #define ID_NSC_SUBCLASS 359
  28. #define ID_NSCTREE  (DWORD)'NSC'
  29. #ifndef UNIX
  30. #define DEFAULT_PATHSTR "C:\"
  31. #else
  32. #define DEFAULT_PATHSTR "/"
  33. #endif
  34. #define LOGOGAP 2   // all kinds of things 
  35. #define DYITEM  17
  36. #define DXYFRAMESEL 1                             
  37. const DEFAULTORDERPOSITION = 32000;
  38. HRESULT CheckForExpandOnce( HWND hwndTree, HTREEITEM hti );
  39. // from util.cpp
  40. // same guid as in bandisf.cpp
  41. // {F47162A0-C18F-11d0-A3A5-00C04FD706EC}
  42. static const GUID TOID_ExtractImage = { 0xf47162a0, 0xc18f, 0x11d0, { 0xa3, 0xa5, 0x0, 0xc0, 0x4f, 0xd7, 0x6, 0xec } };
  43. //from nicotask.cpp
  44. EXTERN_C const GUID TASKID_IconExtraction; // = { 0xeb30900c, 0x1ac4, 0x11d2, { 0x83, 0x83, 0x0, 0xc0, 0x4f, 0xd9, 0x18, 0xd0 } };
  45. BOOL IsChannelFolder(LPCWSTR pwzPath, LPWSTR pwzChannelURL);
  46. COLORREF g_clrBk = 0;
  47. typedef struct
  48. {
  49.     DWORD   iIcon     : 12;
  50.     DWORD   iOpenIcon : 12;
  51.     DWORD   nFlags    : 4;
  52.     DWORD   nMagic    : 4;
  53. } NSC_ICONCALLBACKINFO;
  54. //if you don't remove the selection, treeview will expand everything below the current selection
  55. void TreeView_DeleteAllItemsQuickly(HWND hwnd)
  56. {
  57.     TreeView_SelectItem(hwnd, NULL);
  58.     TreeView_DeleteAllItems(hwnd);
  59. }
  60. BOOL IsParentOfItem(HWND hwnd, HTREEITEM htiParent, HTREEITEM htiChild)
  61. {
  62.     for (HTREEITEM hti = htiChild; (hti != TVI_ROOT) && (hti != NULL); hti = TreeView_GetParent(hwnd, hti))
  63.         if (hti == htiParent)
  64.             return TRUE;
  65.     return FALSE;
  66. }
  67. STDAPI CNscTree_CreateInstance(IUnknown * punkOuter, IUnknown ** ppunk, LPCOBJECTINFO poi)
  68. {
  69.     HRESULT hr;
  70.     CNscTree * pncst = new CNscTree();
  71.     if (pncst == NULL)
  72.     {
  73.         *ppunk = NULL;
  74.         hr = E_OUTOFMEMORY;
  75.     }
  76.     else
  77.     {
  78.         *ppunk = SAFECAST(pncst, INSCTree *);
  79.         hr = S_OK;
  80.     }
  81.     return hr;
  82. }
  83. INSCTree *CNscTree_CreateInstance(void)
  84. {
  85.     CNscTree * pncst = new CNscTree();
  86.     if (pncst)
  87.         return SAFECAST(pncst, INSCTree *);
  88.     return NULL;
  89. }
  90. //////////////////////////////////////////////////////////////////////////////
  91. CNscTree::CNscTree() : _cRef(1), _iDragSrc(-1), _iDragDest(-1), _fOnline(!SHIsGlobalOffline())
  92. {
  93.     // This object is a COM object so it will always be on the heap.
  94.     // ASSERT that our member variables were zero initialized.
  95.     ASSERT(!_fInitialized);
  96.     _ulSortCol = _ulDisplayCol = (ULONG)-1;
  97.     // Enable the notifications from wininet that tell us when to gray items 
  98.     // or update a pinned glyph
  99.     _inetNotify.Enable();
  100. }
  101. CNscTree::~CNscTree()
  102. {
  103.     Pidl_Set(&_pidlSelected, NULL);
  104.     ATOMICRELEASE(_pFilter);
  105.     // This needs to be destroyed or we leak the icon handle.
  106.     if (_hicoPinned) 
  107.     {
  108.         DestroyIcon(_hicoPinned);
  109.     }
  110. }
  111. HRESULT CNscTree::QueryInterface(REFIID riid, void **ppvObj)
  112. {
  113.     static const QITAB qit[] = {
  114.         QITABENT(CNscTree, IShellChangeNotify),         // IID_IShellChangeNotify
  115.         QITABENT(CNscTree, INSCTree),                   // IID_INSCTree
  116.         QITABENT(CNscTree, IShellFavoritesNameSpace),   // IID_IShellFavoritesNameSpace
  117.         QITABENT(CNscTree, IWinEventHandler),           // IID_IWinEventHandler
  118.         QITABENT(CNscTree, IDropTarget),                // IID_IDropTarget
  119.         QITABENT(CNscTree, IObjectWithSite),            // IID_IObjectWithSite
  120.         QITABENT(CNscTree, IShellBrowser),              // IID_IShellBrowser
  121.         QITABENT(CNscTree, IShellFolderFilterSite),     // IID_IShellFolderFilterSite
  122.         { 0 },
  123.     };
  124.     return QISearch(this, qit, riid, ppvObj);
  125. }
  126. ULONG CNscTree::AddRef()
  127. {
  128.     return InterlockedIncrement(&_cRef);
  129. }
  130. ULONG CNscTree::Release()
  131. {
  132.     if (InterlockedDecrement(&_cRef))
  133.         return _cRef;
  134.     delete this;
  135.     return 0;
  136. }
  137. void CNscTree::_ReleaseCachedShellFolder()
  138. {
  139.     ATOMICRELEASE(_psfCache);
  140.     ATOMICRELEASE(_psf2Cache);
  141.     _ulSortCol = _ulDisplayCol = (ULONG)-1;
  142.     _htiCache = NULL;
  143. }
  144. #ifdef DEBUG
  145. void CNscTree::TraceHTREE(HTREEITEM hti, LPCTSTR pszDebugMsg)
  146. {
  147.     TCHAR szDebug[MAX_PATH] = TEXT("Root");
  148.     if (hti != TVI_ROOT && hti)
  149.     {
  150.         TVITEM tvi;
  151.         tvi.mask = TVIF_TEXT | TVIF_HANDLE;
  152.         tvi.hItem = hti;
  153.         tvi.pszText = szDebug;
  154.         tvi.cchTextMax = MAX_PATH;
  155.         TreeView_GetItem(_hwndTree, &tvi);
  156.     }
  157.     TraceMsg(TF_NSC, "NSCBand: %s - %s", pszDebugMsg, szDebug);
  158. }
  159. void CNscTree::TracePIDL(LPCITEMIDLIST pidl, LPCTSTR pszDebugMsg)
  160. {
  161.     TCHAR szDebugName[MAX_URL_STRING] = TEXT("Desktop");
  162.     STRRET str;
  163.     if (_psfCache &&
  164.         SUCCEEDED(_psfCache->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str)))
  165.     {
  166.         StrRetToBuf(&str, pidl, szDebugName, ARRAYSIZE(szDebugName));
  167.     }
  168.     TraceMsg(TF_NSC, "NSCBand: %s - %s", pszDebugMsg, szDebugName);
  169. }
  170. void CNscTree::TracePIDLAbs(LPCITEMIDLIST pidl, LPCTSTR pszDebugMsg)
  171. {
  172.     TCHAR szDebugName[MAX_URL_STRING] = TEXT("Desktop");
  173.     IEGetDisplayName(pidl, szDebugName, SHGDN_FORPARSING);
  174.     TraceMsg(TF_NSC, "NSCBand: %s - %s", pszDebugMsg, szDebugName);
  175. }
  176. #endif
  177. /*****************************************************
  178.     DESCRIPTION:
  179.         We want to unsubclass/subclass everytime we
  180.     change roots so we get the correct notifications
  181.     for everything in that subtree of the shell
  182.     name space.
  183. *****************************************************/
  184. void CNscTree::_SubClass(LPCITEMIDLIST pidlRoot)
  185. {
  186.     LPITEMIDLIST pidlToFree = NULL;
  187.     
  188.     if (NULL == pidlRoot)       // (NULL == CSIDL_DESKTOP)
  189.     {
  190.         SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, (LPITEMIDLIST *) &pidlRoot);
  191.         pidlToFree = (LPITEMIDLIST) pidlRoot;
  192.     }
  193.         
  194.     // It's necessary 
  195.     if (EVAL(!_fSubClassed && pidlRoot))
  196.     {
  197.         if (_SubclassWindow(_hwndTree))
  198.         {
  199.             _RegisterWindow(_hwndTree, pidlRoot,
  200.                 SHCNE_DRIVEADD|SHCNE_CREATE|SHCNE_MKDIR|SHCNE_DRIVEREMOVED|
  201.                 SHCNE_DELETE|SHCNE_RMDIR|SHCNE_RENAMEITEM|SHCNE_RENAMEFOLDER|
  202.                 SHCNE_MEDIAINSERTED|SHCNE_MEDIAREMOVED|SHCNE_NETUNSHARE|SHCNE_NETSHARE|
  203.                 SHCNE_UPDATEITEM|SHCNE_UPDATEIMAGE|SHCNE_ASSOCCHANGED|
  204.                 SHCNE_UPDATEDIR | SHCNE_EXTENDED_EVENT,
  205.                 ((_mode & MODE_HISTORY) ? SHCNRF_ShellLevel : SHCNRF_ShellLevel | SHCNRF_InterruptLevel));
  206.         }
  207.         ASSERT(_hwndTree);
  208.         _fSubClassed = SetWindowSubclass(_hwndTree, _SubClassTreeWndProc, 
  209.             ID_NSCTREE, (DWORD_PTR)this);
  210.     }
  211.     if (pidlToFree) // Did we have to alloc our own pidl?
  212.         ILFree(pidlToFree); // Yes.
  213. }
  214. /*****************************************************
  215.     DESCRIPTION:
  216.         We want to unsubclass/subclass everytime we
  217.     change roots so we get the correct notifications
  218.     for everything in that subtree of the shell
  219.     name space.
  220. *****************************************************/
  221. void CNscTree::_UnSubClass(void)
  222. {
  223.     if (_fSubClassed)
  224.     {
  225.         _fSubClassed = FALSE;
  226.         RemoveWindowSubclass(_hwndTree, _SubClassTreeWndProc, ID_NSCTREE);
  227.         _UnregisterWindow(_hwndTree);
  228.         _UnsubclassWindow(_hwndTree);
  229.     }
  230. }
  231. void CNscTree::_ReleasePidls(void)
  232. {
  233.     Pidl_Set(&_pidlRoot, NULL);
  234. }
  235. HRESULT CNscTree::ShowWindow(BOOL fShow)
  236. {
  237.     if (fShow)
  238.         _TvOnShow();
  239.     else
  240.         _TvOnHide();
  241.     return S_OK;
  242. }
  243. HRESULT CNscTree::SetSite(IUnknown *punkSite)
  244. {
  245.     if (!punkSite)
  246.     {
  247.         // We need to prepare to go away and squirel
  248.         // away the currently selected pidl(s) because
  249.         // the caller may call INSCTree::GetSelectedItem()
  250.         // after the tree is gone.
  251.         _OnWindowCleanup();
  252.     }
  253.     return CObjectWithSite::SetSite(punkSite);
  254. }
  255. EXTERN_C static const GUID TASKID_IconExtraction;
  256. HRESULT CNscTree::_OnWindowCleanup(void)
  257. {
  258.     // Squirel away the selected pidl in case the caller asks for it after the
  259.     // treeview is gone.
  260.     if (!_fIsSelectionCached)
  261.     {
  262.         _fIsSelectionCached = TRUE;
  263.         Pidl_Set(&_pidlSelected, NULL);
  264.         GetSelectedItem(&_pidlSelected, 0);
  265.     }
  266.     _fClosing = TRUE;
  267.     if (_pTaskScheduler)
  268.         _pTaskScheduler->RemoveTasks(TASKID_IconExtraction, ITSAT_DEFAULT_LPARAM, FALSE);
  269.     ATOMICRELEASE(_pTaskScheduler);
  270.     _TvOnHide();
  271.     ASSERT(IsWindow(_hwndTree));      // window not valid, we need to know about this
  272.     SendMessage(_hwndTree, WM_SETREDRAW, FALSE, 0L);
  273.     TreeView_DeleteAllItemsQuickly(_hwndTree);
  274.     _UnSubClass();
  275.     _ReleasePidls();
  276.     ASSERT(_pidlRoot == NULL);
  277.     _ReleaseCachedShellFolder();
  278.     return S_OK;
  279. }
  280. ITEMINFO *CNscTree::_GetTreeItemInfo(HTREEITEM hti)
  281. {
  282.     TV_ITEM tvi;
  283.     
  284.     tvi.mask = TVIF_PARAM | TVIF_HANDLE;
  285.     tvi.hItem = hti;
  286.     if (!TreeView_GetItem(_hwndTree, &tvi))
  287.         return NULL;
  288.     return (ITEMINFO *)tvi.lParam;
  289. }
  290. PORDERITEM CNscTree::_GetTreeOrderItem(HTREEITEM hti)
  291. {
  292.     ITEMINFO *pii = _GetTreeItemInfo(hti);
  293.     return pii ? pii->poi : NULL;
  294. }
  295. // builds a fully qualified IDLIST from a given tree node by walking up the tree
  296. // be sure to free this when you are done!
  297. LPITEMIDLIST CNscTree::_GetFullIDList(HTREEITEM hti)
  298. {
  299.     LPITEMIDLIST pidl, pidlT = NULL;
  300.     if ((hti == TVI_ROOT) || (hti == NULL)) // evil root
  301.     {
  302.         pidlT = ILClone(_pidlRoot);
  303.         return pidlT;
  304.     }
  305.     // now lets get the information about the item
  306.     PORDERITEM poi = _GetTreeOrderItem(hti);
  307.     if (!poi)
  308.     {
  309.         return NULL;
  310.     }
  311.     
  312.     pidl = ILClone(poi->pidl);
  313.     if (pidl && _pidlRoot)
  314.     {
  315.         while ((hti = TreeView_GetParent(_hwndTree, hti)))
  316.         {
  317.             poi = _GetTreeOrderItem(hti);
  318.             if (!poi)
  319.                 return pidl;   // will assume I screwed up...
  320.             
  321.             if (poi->pidl)
  322.                 pidlT = ILCombine(poi->pidl, pidl);
  323.             else 
  324.                 pidlT = NULL;
  325.             
  326.             ILFree(pidl);
  327.             pidl = pidlT;
  328.             if (pidl == NULL)
  329.                 break;          // outta memory
  330.         }
  331.         if (pidl) 
  332.         {
  333.             // MODE_NORMAL has the pidl root in the tree
  334.             if (_mode != MODE_NORMAL)
  335.             {
  336.                 pidlT = ILCombine(_pidlRoot, pidl);    // gotta get the silent root
  337.                 ILFree(pidl);
  338.             }
  339.             else
  340.                 pidlT = pidl;
  341.         }
  342.     }
  343.     return pidlT;
  344. }
  345. BOOL _IsItemFileSystem(IShellFolder *psf, LPCITEMIDLIST pidl)
  346. {
  347.     DWORD dwAttributes = SFGAO_FOLDER | SFGAO_FILESYSTEM;
  348.     HRESULT hr = psf->GetAttributesOf(1, &pidl, &dwAttributes);
  349.     return SUCCEEDED(hr) && ((dwAttributes & (SFGAO_FOLDER | SFGAO_FILESYSTEM)) == (SFGAO_FOLDER | SFGAO_FILESYSTEM));
  350. }
  351. // NOTE: takes ownership of pidl 
  352. HTREEITEM CNscTree::_AddItemToTree(HTREEITEM htiParent, LPCITEMIDLIST pidl, 
  353.                                    int cChildren, int iPos, HTREEITEM htiAfter, /* = TVI_LAST*/
  354.                                    BOOL fCheckForDups, /* = TRUE */ BOOL fMarked /*= FALSE */)
  355. {
  356.     HTREEITEM htiRet = NULL;
  357.     BOOL fCached;
  358.     // So we need to cached the shell folder of the parent item. But, this is a little interesting:
  359.     if (_mode == MODE_NORMAL && htiParent == TVI_ROOT)
  360.     {
  361.         // In "Normal" mode, or "Display root in NSC" mode, there is only 1 item that is parented to
  362.         // TVI_ROOT. So when we do an _AddItemToTree, we need the shell folder that contains _pidlRoot or
  363.         // the Parent of TVI_ROOT.
  364.         fCached = (NULL != _CacheParentShellFolder(htiParent, NULL));
  365.     }
  366.     else
  367.     {
  368.         // But, in the "Favorites, Control or History" if htiParent is TVI_ROOT, then we are not adding _pidlRoot,
  369.         // so we actually need the folder that IS TVI_ROOT.
  370.         fCached = _CacheShellFolder(htiParent);
  371.     }
  372.     
  373.     if (fCached)
  374.     {
  375.         PORDERITEM poi = OrderItem_Create((LPITEMIDLIST)pidl, iPos);
  376.         if (poi)
  377.         {
  378.             ITEMINFO *pii = (ITEMINFO *)LocalAlloc(LPTR, sizeof(*pii));
  379.             if (pii)
  380.             {
  381.                 pii->poi = poi;
  382.                 // For the normal case, we need a relative pidl for this add, but the lParam needs to have a full
  383.                 // pidl (This is so that arbitrary mounting works, as well as desktop case).
  384.                 if (_mode == MODE_NORMAL && htiParent == TVI_ROOT)
  385.                 {
  386.                     pidl = ILFindLastID(pidl);
  387.                 }
  388.                 if (!fCheckForDups || (NULL == (htiRet = _FindChild(_psfCache, htiParent, pidl))))
  389.                 {
  390.                     TV_INSERTSTRUCT tii;
  391.                     // Initialize item to add with callback for everything
  392.                     tii.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_CHILDREN;
  393.                     tii.hParent = htiParent;
  394.                     tii.hInsertAfter = htiAfter;
  395.                     tii.item.iImage = I_IMAGECALLBACK;
  396.                     tii.item.iSelectedImage = I_IMAGECALLBACK;
  397.                     tii.item.pszText = LPSTR_TEXTCALLBACK;
  398.                     tii.item.cChildren = cChildren;
  399.                     tii.item.lParam = (LPARAM)pii;
  400.                     tii.item.stateMask = TVIS_STATEIMAGEMASK;
  401.                     tii.item.state = (fMarked ? NSC_TVIS_MARKED : 0);
  402. #ifdef DEBUG
  403.                     TracePIDL(pidl, TEXT("Inserting"));
  404.                     TraceMsg(TF_NSC, "_AddItemToTree(htiParent=%#08lx, htiAfter=%#08lx, fCheckForDups=%d, _psfCache=%#08lx)", 
  405.                                 htiParent, htiAfter, fCheckForDups, _psfCache);
  406.                     
  407. #endif // DEBUG
  408.                     pii->fNavigable = !_IsItemFileSystem(_psfCache, pidl);
  409.     
  410.                     htiRet = TreeView_InsertItem(_hwndTree, &tii);
  411.                     if (htiRet)
  412.                     {
  413.                         pii = NULL;     // don't free
  414.                         poi = NULL;     // don't free
  415.                         pidl = NULL;    // don't free
  416.                     }
  417.                 }
  418.                 if (pii)
  419.                     LocalFree(pii);
  420.             }
  421.             if (poi)
  422.                 OrderItem_Free(poi, FALSE);
  423.         }
  424.     }
  425.     if (pidl)
  426.         ILFree((LPITEMIDLIST)pidl);
  427.     return htiRet;
  428. }
  429. HRESULT CNscTree::CreateTree(HWND hwndParent, DWORD dwStyles, HWND *phwnd)
  430. {
  431.     _fIsSelectionCached = FALSE;
  432.     if (*phwnd)
  433.         return S_OK;                                
  434.     
  435.     _style |= (WS_CHILD | TVS_INFOTIP | TVS_FULLROWSELECT | TVS_EDITLABELS
  436.         | TVS_SHOWSELALWAYS | TVS_NONEVENHEIGHT | TVS_NOHSCROLL | dwStyles);
  437.     if (TVS_HASLINES & _style)
  438.         _style &= ~TVS_FULLROWSELECT;       // If it has TVS_HASLINES, it can't have TVS_FULLROWSELECT
  439.     if (_mode != MODE_NORMAL)
  440.     {
  441.         // We don't want track select (underline and blue) for the folder or tree view.
  442.         _style |= TVS_TRACKSELECT;
  443.         //get single expand setting from registry
  444.         DWORD dwValue;
  445.         DWORD dwSize = SIZEOF(dwValue);
  446.         BOOL  fDefault = TRUE;
  447.         SHRegGetUSValue(L"Software\Microsoft\Internet Explorer\Main",
  448.                         L"NscSingleExpand", NULL, (LPBYTE)&dwValue, &dwSize, FALSE,
  449.                         (void *) &fDefault, SIZEOF(fDefault));
  450.         if (dwValue)
  451.         {
  452.             _style |= TVS_SINGLEEXPAND;
  453.             _fSingleExpand = TRUE;
  454.         }
  455.     }
  456.     else
  457.     {
  458.         // According to Bug#241601, Tooltips display too quickly. The problem is
  459.         // the original designer of the InfoTips in the Treeview merged the "InfoTip" tooltip and
  460.         // the "I'm too small to display correctly" tooltips. This is really unfortunate because you
  461.         // cannot control the display of these tooltips independantly. Therefore we are turning off
  462.         // infotips in normal mode. (lamadio) 4.7.99
  463.         _style &= ~TVS_INFOTIP;
  464.     }
  465.     
  466.     _hwndParent = hwndParent;
  467.     *phwnd = _CreateTreeview();
  468.     if (*phwnd == NULL)
  469.     {
  470.         ASSERT(FALSE);
  471.         return E_OUTOFMEMORY;
  472.     }
  473.     ::ShowWindow(_hwndTree, SW_SHOW);
  474.     return S_OK;
  475. }
  476. HWND CNscTree::_CreateTreeview()
  477. {
  478.     ASSERT(_hwndTree == NULL);
  479.     
  480.     if (!_hwndParent)
  481.     {
  482.         TraceMsg(TF_WARNING, "CNscTree::_CreateTreeview has no parent window");
  483.     }
  484.     
  485.     DWORD dwExStyle = 0;
  486.     
  487.     RECT rcParent;
  488.     GetClientRect(_hwndParent, &rcParent);
  489.     
  490.     _style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP;
  491.     _hwndTree = CreateWindowEx(dwExStyle, WC_TREEVIEW, NULL, _style,
  492.         0, 0, rcParent.right, rcParent.bottom, _hwndParent, (HMENU)ID_CONTROL, HINST_THISDLL, NULL);
  493.     
  494.     if (_hwndTree)
  495.     {
  496.         SendMessage(_hwndTree, TVM_SETSCROLLTIME, 100, 0);
  497.         SendMessage(_hwndTree, CCM_SETUNICODEFORMAT, DLL_IS_UNICODE, 0);
  498.     }
  499.     else
  500.     {
  501.         TraceMsg(TF_ERROR, "_hwndTree failed");
  502.     }
  503.     return _hwndTree;
  504. void CNscTree::_TvOnHide()
  505. {
  506.     _DtRevoke();
  507. }
  508. void CNscTree::_TvOnShow()
  509. {
  510.     _DtRegister();
  511. }
  512. HRESULT CNscTree::_HandleWinIniChange()
  513. {
  514.     g_clrBk = GetSysColor(COLOR_WINDOW);
  515.     if (_mode != MODE_NORMAL)
  516.     {
  517.         // make things a bit more spaced out
  518.         int cyItem = TreeView_GetItemHeight(_hwndTree);
  519.         cyItem += LOGOGAP + 1;
  520.         TreeView_SetItemHeight(_hwndTree, cyItem);
  521.     }
  522.     return S_OK;
  523. }
  524. HRESULT CNscTree::Initialize(LPCITEMIDLIST pidlRoot, DWORD grfFlags, DWORD dwFlags)
  525. {
  526.     HRESULT     hres;
  527.     _grfFlags = grfFlags;                          // Filter flags.
  528.     _dwFlags = dwFlags;                            // Behavior Flags
  529.     if (!_fInitialized)
  530.     {
  531.         _fInitialized = TRUE;
  532.     
  533.         SHFILEINFO sfi;
  534.         HIMAGELIST himl = (HIMAGELIST)SHGetFileInfo(TEXT(DEFAULT_PATHSTR), 0, &sfi
  535.             , sizeof(SHFILEINFO),  SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
  536.     
  537.         TreeView_SetImageList(_hwndTree, himl, TVSIL_NORMAL);
  538.         _DtRegister();
  539.     
  540.         hres = Init();  // init lock and scroll handles for CDelegateDropTarget
  541.     
  542.         ASSERT(SUCCEEDED(hres));
  543.     
  544.         if (_mode != MODE_NORMAL)
  545.         {
  546.             // set borders and space out for all, much cleaner.
  547.             TreeView_SetBorder(_hwndTree, TVSBF_XBORDER, 2*LOGOGAP, 0);   
  548.         }
  549.     
  550.         //init g_clrBk
  551.         _HandleWinIniChange();
  552.         // pidlRoot may equal NULL because that is equal to CSIDL_DESKTOP.
  553.         if ((LPITEMIDLIST)INVALID_HANDLE_VALUE != pidlRoot)
  554.         {
  555.             _UnSubClass();
  556.             _SetRoot(pidlRoot, 1, NULL, NSSR_CREATEPIDL);
  557.             _SubClass(pidlRoot);
  558.         }
  559.     
  560.         // need top level frame available for D&D if possible.
  561.         IOleWindow *pOleWindow;
  562.     
  563.         _hwndDD = GetParent(_hwndTree);
  564.         if (_punkSite)
  565.         {
  566.             HRESULT hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_IOleWindow, (void **)&pOleWindow);
  567.             if (SUCCEEDED(hr))
  568.             { 
  569.                 ASSERT(pOleWindow);
  570.                 pOleWindow->GetWindow(&_hwndDD);
  571.                 pOleWindow->Release();
  572.             }
  573.         }
  574.         //this is a non-ML resource
  575.         _hicoPinned = (HICON)LoadImage(HINST_THISDLL, MAKEINTRESOURCE(IDI_PINNED), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
  576.         ASSERT(_hicoPinned);
  577.         //failure ignored intentionally
  578.         THR(CoCreateInstance(CLSID_ShellTaskScheduler, NULL, CLSCTX_INPROC,
  579.                              IID_IShellTaskScheduler, (void **)&_pTaskScheduler));
  580.     }
  581.     else
  582.         hres = _ChangePidlRoot(pidlRoot);
  583.     return hres;
  584. }
  585. // BUGBUG: Move to shlwapiutil.cpp
  586. HRESULT IUnknown_UIActivateIO(IUnknown * punk, BOOL fActivate, LPMSG pMsg)
  587. {
  588.     HRESULT hr = E_INVALIDARG;
  589.     
  590.     if (punk)
  591.     {
  592.         IInputObject * pio;
  593.         hr = punk->QueryInterface(IID_IInputObject, (void **) &pio);
  594.         if (SUCCEEDED(hr))
  595.         {
  596.             hr = pio->UIActivateIO(fActivate, pMsg);
  597.             pio->Release();
  598.         }
  599.     }
  600.     return hr;
  601. }
  602. // set the root of the name space control.
  603. //
  604. // in:
  605. //  pidlRoot    NULL means the desktop
  606. //    HIWORD 0 -> LOWORD == ID of special folder (CSIDL_* values)
  607. //
  608. //  flags,
  609. //  pidlRoot,       PIDL, NULL for desktop, or CSIDL for shell special folder
  610. //  iExpandDepth,   how many levels to expand the tree
  611. //  pidlExpandTo    NULL, or PIDL to expand to
  612. //
  613. BOOL CNscTree::_SetRoot(LPCITEMIDLIST pidlRoot, int iExpandDepth, LPCITEMIDLIST pidlExpandTo, NSSR_FLAGS flags)
  614. {
  615.     _ReleasePidls();
  616.     // review chrisny:  clean up this psr stuff.
  617.     // HIWORD/LOWORD stuff is to support pidl IDs instead of full pidl here
  618.     if (HIWORD(pidlRoot))
  619.         _pidlRoot = ILClone(pidlRoot);
  620.     else
  621.     {
  622.         SHGetSpecialFolderLocation(NULL, LOWORD(pidlRoot) 
  623.             ? LOWORD(pidlRoot) 
  624.             : CSIDL_DESKTOP, &_pidlRoot);
  625.     }
  626.     if (_pidlRoot)
  627.     {
  628.         HTREEITEM htiRoot = TVI_ROOT;
  629.         if (_mode == MODE_NORMAL)
  630.         {
  631.             // Since we'll be adding this into the tree, we need
  632.             // to clone it: We have a copy for the class, and we
  633.             // have one for the tree itself (Makes life easier so
  634.             // we don't have to special case TVI_ROOT).
  635.             LPITEMIDLIST pidlRoot = ILClone(_pidlRoot);
  636.             if (pidlRoot)
  637.             {
  638.                 htiRoot = _AddItemToTree(TVI_ROOT, pidlRoot, 1, 0);
  639.                 if (htiRoot)
  640.                 {
  641.                     TraceMsg(TF_NSC, "NSCBand: Setting Root to "Desktop"");
  642.                     TreeView_Expand(_hwndTree, htiRoot, TVE_EXPAND);
  643.                     TreeView_SelectItem(_hwndTree, htiRoot);
  644.                     return TRUE;
  645.                 }
  646.                 else
  647.                 {
  648.                     ILFree(pidlRoot);
  649.                     htiRoot = TVI_ROOT;
  650.                 }
  651.             }
  652.         }
  653.         int cAdded;
  654.         BOOL fOrdered = _fOrdered;
  655.         _LoadSF(htiRoot, _pidlRoot, TRUE, &cAdded, &fOrdered);   // load the roots (actual children of _pidlRoot.
  656.         _fOrdered = BOOLIFY(fOrdered);
  657. #ifdef DEBUG
  658.         TracePIDLAbs(_pidlRoot, TEXT("Setting Root to"));
  659. #endif // DEBUG
  660.         // in organize favorites, select the first item by default
  661.         if (_mode & MODE_CONTROL)
  662.         {
  663.             //yes, this is really necessary. the selectitem scrolls the list down so the 
  664.             //first item is not visible. doing just the select has no effect!
  665.             HTREEITEM htiFirst = TreeView_GetFirstVisible(_hwndTree);
  666.             TreeView_SelectItem(_hwndTree, htiFirst);
  667.             TreeView_Expand(_hwndTree, htiFirst, TVE_COLLAPSE); //just in case it expanded
  668.             TreeView_Select(_hwndTree, htiFirst, TVGN_FIRSTVISIBLE);
  669.         }
  670.         return TRUE;
  671.     }
  672.     TraceMsg(DM_ERROR, "set root failed");
  673.     _ReleasePidls();
  674.     return FALSE;
  675. }
  676. // cache the shell folder for a given tree item
  677. // in:
  678. //  hti tree node to cache shell folder for. this my be
  679. //      NULL indicating the root item.
  680. //
  681. BOOL CNscTree::_CacheShellFolder(HTREEITEM hti)
  682. {
  683.     // in the cache?
  684.     if ((hti != _htiCache) || (_psfCache == NULL))
  685.     {
  686.         // cache miss, do the work
  687.         LPITEMIDLIST pidl;
  688.         BOOL fRet = FALSE;
  689.         
  690.         _fpsfCacheIsTopLevel = FALSE;
  691.         _ReleaseCachedShellFolder();
  692.         
  693.         if ((hti == NULL) || (hti == TVI_ROOT))
  694.         {
  695.             pidl = ILClone(_pidlRoot);
  696.         }
  697.         else
  698.             pidl = _GetFullIDList(hti);
  699.             
  700.         if (pidl)
  701.         {
  702.             if (SUCCEEDED(IEBindToObject(pidl, &_psfCache)))
  703.             {
  704.                 ASSERT(_psfCache);
  705.                 _htiCache = hti;    // this is for the cache match
  706.                 _fpsfCacheIsTopLevel = ( hti == TVI_ROOT || hti == NULL );
  707.                 fRet = TRUE;
  708.             }      
  709.             
  710.             ILFree(pidl);
  711.         }
  712.         
  713.         return fRet;
  714.     }
  715.     return TRUE;
  716. }
  717. #define TVI_ROOTPARENT ((HTREEITEM)(ULONG_PTR)-0xF000)
  718. // pidlItem is typically a relative pidl, except in the case of the root where
  719. // it can be a fully qualified pidl
  720. LPITEMIDLIST CNscTree::_CacheParentShellFolder(HTREEITEM hti, LPITEMIDLIST pidl)
  721. {
  722.     // need parent shell folder of TVI_ROOT, special case for drop insert into root level of tree.
  723.     if (hti == TVI_ROOT || 
  724.         hti == NULL || 
  725.         (_mode == MODE_NORMAL &&
  726.         TreeView_GetParent(_hwndTree, hti) == NULL))    // If we have a null parent and we're a normal, 
  727.                                                         // than that's the same as root.
  728.     {
  729.         if (_htiCache != TVI_ROOTPARENT) 
  730.         {
  731.             _ReleaseCachedShellFolder();
  732.             IEBindToParentFolder(_pidlRoot, &_psfCache, NULL);
  733.             if (!ILIsEmpty(_pidlRoot))
  734.                 _htiCache = TVI_ROOTPARENT;
  735.         }
  736.         return ILFindLastID(_pidlRoot);
  737.     }
  738.     if (_CacheShellFolder(TreeView_GetParent(_hwndTree, hti)))
  739.     {
  740.         if (pidl == NULL)
  741.         {
  742.             PORDERITEM poi = _GetTreeOrderItem(hti);
  743.             if (!poi)
  744.                 return NULL;
  745.             pidl = poi->pidl;
  746.         }
  747.         
  748.         return ILFindLastID(pidl);
  749.     }
  750.     
  751.     return NULL;
  752. }
  753. typedef struct _SORTPARAMS
  754. {
  755.     CNscTree *pnsc;
  756.     IShellFolder *psf;
  757. } SORTPARAMS;
  758. int CALLBACK CNscTree::_TreeCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  759. {
  760.     SORTPARAMS *pSortParams = (SORTPARAMS *)lParamSort;
  761.     PORDERITEM poi1 = GetPoi(lParam1), poi2 = GetPoi(lParam2);
  762.     
  763.     HRESULT hres = pSortParams->pnsc->_CompareIDs(pSortParams->psf, poi1->pidl, poi2->pidl);
  764.     return (short)SCODE_CODE(hres);
  765. }
  766. int CALLBACK CNscTree::_TreeOrder(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  767. {
  768.     HRESULT hres;
  769.     PORDERITEM poi1 = GetPoi(lParam1), poi2 = GetPoi(lParam2);
  770.     
  771.     ASSERT((poi1 != NULL) && (poi1 != NULL));
  772.     if (poi1->nOrder == poi2->nOrder)
  773.         hres = 0;
  774.     else
  775.         // do unsigned compare so -1 goes to end of list
  776.         hres = (poi1->nOrder < poi2->nOrder ? -1 : 1);
  777.     
  778.     return (short)SCODE_CODE(hres);
  779. }
  780. // review chrisny:  instead of sort, insert items on the fly.
  781. void CNscTree::_Sort(HTREEITEM hti, IShellFolder *psf)
  782. {
  783.     TV_SORTCB   scb;
  784.     SORTPARAMS  SortParams = {this, psf};
  785.     BOOL        fOrdering = _IsOrdered(hti);
  786. #ifdef DEBUG
  787.         TraceHTREE(hti, TEXT("Sorting"));
  788. #endif
  789.     
  790.     scb.hParent = hti;
  791.     scb.lpfnCompare = !fOrdering ? _TreeCompare : _TreeOrder;
  792.     
  793.     scb.lParam = (LPARAM)&SortParams;
  794.     TreeView_SortChildrenCB(_hwndTree, &scb, FALSE);
  795. }
  796. BOOL CNscTree::_IsOrdered(HTREEITEM htiRoot)
  797. {
  798.     if ( (htiRoot == TVI_ROOT) || (htiRoot == NULL) )
  799.         return _fOrdered;
  800.     else
  801.     {
  802.         PORDERITEM poi = _GetTreeOrderItem(htiRoot);
  803.         if (poi)
  804.         {
  805.             // LParam Is a Boolean: 
  806.             // TRUE: It has an order.
  807.             // FALSE: It does not have an order.
  808.             // Question: Where is that order stored? _hdpaOrder?
  809.             return poi->lParam;
  810.         }
  811.     }
  812.     return FALSE;
  813. }
  814. //helper function to init _hdpaOrd
  815. //MUST be followed by a call to _FreeOrderList
  816. HRESULT CNscTree::_PopulateOrderList(HTREEITEM htiRoot)
  817. {
  818.     int        i = 0;
  819.     HTREEITEM  hti = NULL;
  820. #ifdef DEBUG
  821.     TraceHTREE(htiRoot, TEXT("Populating Order List from tree node"));
  822. #endif
  823.     
  824.     if (_hdpaOrd)
  825.         DPA_Destroy(_hdpaOrd);
  826.     
  827.     _hdpaOrd = DPA_Create(4);
  828.     if (_hdpaOrd == NULL)
  829.         return E_FAIL;
  830.     
  831.     for (hti = TreeView_GetChild(_hwndTree, htiRoot); hti;
  832.     hti = TreeView_GetNextSibling(_hwndTree, hti))
  833.     {
  834.         PORDERITEM poi = _GetTreeOrderItem(hti);
  835.         if (poi)
  836.         {
  837.             poi->nOrder = i;        // reset the positions of the nodes.
  838.             DPA_SetPtr(_hdpaOrd, i++, (void *)poi);
  839.         }
  840.     }
  841.     
  842.     //set the root's ordered flag
  843.     if (htiRoot == TVI_ROOT)
  844.         _fOrdered = TRUE;
  845.     else
  846.     {
  847.         PORDERITEM poi = _GetTreeOrderItem(htiRoot);
  848.         if (poi)
  849.         {
  850.             poi->lParam = TRUE;
  851.         }
  852.     }
  853.     
  854.     return S_OK;
  855. }
  856. //helper function to free _hdpaOrd
  857. //MUST be preceded by a call to _PopulateOrderList
  858. void CNscTree::_FreeOrderList(HTREEITEM htiRoot)
  859. {
  860.     ASSERT(_hdpaOrd);
  861. #ifdef DEBUG
  862.     TraceHTREE(htiRoot, TEXT("Freeing OrderList"));
  863. #endif
  864.     _ReleaseCachedShellFolder();
  865.     
  866.     // Persist the new order out to the registry
  867.     LPITEMIDLIST pidl = _GetFullIDList(htiRoot);
  868.     if (pidl)
  869.     {
  870.         IStream* pstm = GetOrderStream(pidl, STGM_WRITE | STGM_CREATE);
  871.         if (pstm)
  872.         {
  873.             if (_CacheShellFolder(htiRoot))
  874.             {
  875.                 OrderList_SaveToStream(pstm, _hdpaOrd, _psfCache);
  876.                 pstm->Release();
  877.                 
  878.                 // Notify everyone that the order changed
  879.                 SHSendChangeMenuNotify(this, SHCNEE_ORDERCHANGED, SHCNF_FLUSH, _pidlRoot);
  880.                 TraceMsg(TF_NSC, "NSCBand: Sent SHCNE_EXTENDED_EVENT : SHCNEE_ORDERCHANGED");
  881.                 
  882.                 // Remove this notify message immediately (so _fDropping is set
  883.                 // and we'll ignore this event in above OnChange method)
  884.                 //
  885.                 // _FlushNotifyMessages(_hwndTree);
  886.             }
  887.             else
  888.                 pstm->Release();
  889.         }
  890.         ILFree(pidl);
  891.     }
  892.     
  893.     DPA_Destroy(_hdpaOrd);
  894.     _hdpaOrd = NULL;
  895. }
  896. //removes any order the user has set and goes back to alphabetical sort
  897. HRESULT CNscTree::ResetSort(void)
  898. {
  899.     HRESULT hr = S_OK;
  900. #ifdef UNUSED
  901.     ASSERT(_psfCache);
  902.     ASSERT(_pidlRoot);
  903.     
  904.     int cAdded = 0;
  905.     IStream* pstm = NULL;
  906.     
  907.     _fWeChangedOrder = TRUE;
  908.     if (FAILED(hr = _PopulateOrderList(TVI_ROOT)))
  909.         return hr;
  910.     
  911.     pstm = OpenPidlOrderStream((LPCITEMIDLIST)CSIDL_FAVORITES, _pidlRoot, REG_SUBKEY_FAVORITESA, STGM_CREATE | STGM_WRITE);
  912.     
  913.     _CacheShellFolder(TVI_ROOT);
  914.     
  915.     if (pstm == NULL || _psfCache == NULL)
  916.     {
  917.         ATOMICRELEASE(pstm);
  918.         _FreeOrderList(TVI_ROOT);
  919.         return S_OK;
  920.     }
  921.     _fOrdered = FALSE;
  922.     
  923.     ORDERINFO   oinfo;
  924.     oinfo.psf = _psfCache;
  925.     (oinfo.psf)->AddRef();
  926.     oinfo.dwSortBy = OI_SORTBYNAME;
  927.     DPA_Sort(_hdpaOrd, OrderItem_Compare,(LPARAM)&oinfo);
  928.     ATOMICRELEASE(oinfo.psf);
  929.     
  930.     OrderList_Reorder(_hdpaOrd);
  931.     
  932.     OrderList_SaveToStream(pstm, _hdpaOrd, _psfCache);
  933.     ATOMICRELEASE(pstm);
  934.     
  935.     _FreeOrderList(TVI_ROOT);
  936.     Refresh();
  937.     
  938.     _fWeChangedOrder = FALSE;
  939. #endif
  940.     return hr;
  941. }
  942. void CNscTree::MoveItemUpOrDown(BOOL fUp)
  943. {
  944.     HTREEITEM   htiSelected, htiToSwap, htiParent;
  945.     PORDERITEM  poiSelected, poiToSwap;
  946.     
  947.     htiSelected = TreeView_GetSelection(_hwndTree);
  948.     htiToSwap = (fUp) ? TreeView_GetPrevSibling(_hwndTree, htiSelected) : 
  949.                         TreeView_GetNextSibling(_hwndTree, htiSelected);
  950.     htiParent = TreeView_GetParent(_hwndTree, htiSelected);
  951.     if (htiParent == NULL)
  952.         htiParent = TVI_ROOT;
  953.     ASSERT(htiSelected);
  954.     
  955.     _fWeChangedOrder = TRUE;
  956.     if (FAILED(_PopulateOrderList(htiParent)))
  957.         return;
  958.     
  959.     if ( (htiSelected) && (htiToSwap) )
  960.     {
  961.         if ((poiSelected = _GetTreeOrderItem(htiSelected)) &&
  962.             (poiToSwap   = _GetTreeOrderItem(htiToSwap)))
  963.         {
  964.             int iOrder = 0;
  965.             
  966.             iOrder = poiSelected->nOrder;
  967.             poiSelected->nOrder = poiToSwap->nOrder;
  968.             poiToSwap->nOrder   = iOrder;
  969.         }
  970.         
  971.         _CacheShellFolder(htiParent);
  972.         
  973.         if (_psfCache)
  974.             _Sort(htiParent, _psfCache);
  975.     }
  976.     TreeView_SelectItem(_hwndTree, htiSelected);
  977.     
  978.     _FreeOrderList(htiParent);
  979.     _fWeChangedOrder = FALSE;
  980. }
  981. // filter function... let clients filter what gets added here
  982. BOOL CNscTree::_ShouldAdd(LPCITEMIDLIST pidl)
  983. {
  984.     // send notify up to parent to let them filter
  985.     return TRUE;
  986. }
  987. BOOL CNscTree::_OnItemExpandingMsg(NM_TREEVIEW *pnm)
  988. {
  989.     HCURSOR hCursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
  990.     
  991.     BOOL bRet = _OnItemExpanding(pnm->itemNew.hItem, pnm->action, (pnm->itemNew.state & TVIS_EXPANDEDONCE));
  992.     SetCursor(hCursorOld);
  993.     return bRet;
  994. }
  995. //
  996. //  The NSC item is expandable if it is a regular folder and it's not one
  997. //  of those funky non-expandable channel folders.
  998. //
  999. BOOL CNscTree::_IsExpandable(HTREEITEM hti)
  1000. {
  1001.     BOOL fExpandable = FALSE;
  1002.     LPCITEMIDLIST pidlItem = _CacheParentShellFolder(hti, NULL);
  1003.     if (pidlItem)
  1004.     {
  1005.         // make sure item is actually a folder and not a non-expandable channel folder
  1006.         // except: in org favs, never expand channel folders
  1007.         ULONG ulAttr = SFGAO_FOLDER;
  1008.         LPITEMIDLIST pidlTarget = NULL;
  1009.         if (SUCCEEDED(_psfCache->GetAttributesOf(1, &pidlItem, &ulAttr)) &&
  1010.             (ulAttr & SFGAO_FOLDER) &&
  1011.             !(SUCCEEDED(SHGetNavigateTarget(_psfCache, pidlItem, &pidlTarget, &ulAttr)) &&
  1012.                   ((_mode & MODE_CONTROL) ?
  1013.                         TRUE :
  1014.                         !IsExpandableChannelFolder(_psfCache, pidlItem))) )
  1015.         {
  1016.             fExpandable = TRUE;
  1017.         }
  1018.         ILFree(pidlTarget);
  1019.     }
  1020.     return fExpandable;
  1021. }
  1022. BOOL CNscTree::_OnItemExpanding(HTREEITEM htiToActivate, UINT action, BOOL fExpandedOnce)
  1023. {
  1024.     int cAdded = 0;
  1025.     
  1026.     if (action != TVE_EXPAND)
  1027.     {
  1028.         htiToActivate = TreeView_GetParent(_hwndTree, htiToActivate);
  1029.     } 
  1030.     else if (fExpandedOnce)
  1031.     {
  1032.         ; //item has already been expanded, don't add items
  1033.     }
  1034.     else
  1035.     {
  1036.         if (_IsExpandable(htiToActivate))
  1037.         {
  1038.             LPITEMIDLIST pidlParent = _GetFullIDList(htiToActivate);
  1039.             if (pidlParent)
  1040.             {
  1041.                 BOOL fOrdered;
  1042.                 // if we're refreshing, fAddingOnly should false
  1043.                 _LoadSF(htiToActivate, pidlParent, !_fRefreshing, &cAdded, &fOrdered);
  1044.                 ILFree(pidlParent);
  1045.             }
  1046.         }
  1047.         // If we did not add anything we should update this item to let
  1048.         // the user know something happened.
  1049.         //
  1050.         if (cAdded == 0)
  1051.         {
  1052.             TV_ITEM tvi;
  1053.             tvi.mask = TVIF_CHILDREN | TVIF_HANDLE;   // only change the number of children
  1054.             tvi.hItem = htiToActivate;
  1055.             tvi.cChildren = 0;
  1056.             
  1057.             TreeView_SetItem(_hwndTree, &tvi);
  1058.         }
  1059.     }
  1060.     
  1061.     _UpdateActiveBorder(htiToActivate);
  1062.     return TRUE;
  1063. }
  1064. HTREEITEM CNscTree::_FindFromRoot(HTREEITEM htiRoot, LPCITEMIDLIST pidl)
  1065. {
  1066.     HTREEITEM    htiRet = NULL;
  1067.     LPITEMIDLIST pidlParent, pidlChild;
  1068.     BOOL         fFreePidlParent = FALSE;
  1069. #ifdef DEBUG
  1070.     TracePIDLAbs(pidl, TEXT("Finding this pidl"));
  1071.     TraceHTREE(htiRoot, TEXT("from this root"));
  1072. #endif
  1073.     
  1074.     if (!htiRoot) 
  1075.     {
  1076.         // When in "Normal" mode, we need to use the first child, not the root
  1077.         // in order to calculate, because there is no "Invisible" root. On the
  1078.         // other hand, History and Favorites have an invisible root: Their
  1079.         // parent folder, so they need this fudge.
  1080.         htiRoot = (MODE_NORMAL == _mode)?TreeView_GetChild(_hwndTree, 0) : TVI_ROOT;
  1081.         pidlParent = _pidlRoot;    // the invisible root.
  1082.     }
  1083.     else 
  1084.     {
  1085.         pidlParent      = _GetFullIDList(htiRoot);
  1086.         fFreePidlParent = TRUE;
  1087.     }
  1088.     
  1089.     if (pidlParent == NULL)
  1090.         return NULL;
  1091.     
  1092.     if (ILIsEqual(pidlParent, pidl)) 
  1093.     {
  1094.         if (fFreePidlParent)
  1095.             ILFree(pidlParent);
  1096.         return htiRoot;
  1097.     }
  1098.     
  1099.     pidlChild = ILFindChild(pidlParent, pidl);
  1100.     if (pidlChild == NULL) 
  1101.     {
  1102.         if (fFreePidlParent)
  1103.             ILFree(pidlParent);
  1104.         return NULL;    // not root match, no hti
  1105.     }
  1106.     
  1107.     // root match, carry on . . .
  1108.     
  1109.     // Are we rooted under the Desktop (i.e. Empty pidl or ILIsEmpty(_pidlRoot))
  1110.     IShellFolder *psf = NULL;
  1111.     HRESULT hres = IEBindToObject(pidlParent, &psf);
  1112.     if (FAILED(hres))
  1113.     {
  1114.         if (fFreePidlParent)
  1115.             ILFree(pidlParent);
  1116.         return htiRet;
  1117.     }
  1118.     
  1119.     while (htiRoot && psf)
  1120.     {
  1121.         LPITEMIDLIST pidlItem = ILCloneFirst(pidlChild);
  1122.         if (!pidlItem)
  1123.             break;
  1124.         
  1125.         htiRoot = _FindChild(psf, htiRoot, pidlItem);
  1126.         IShellFolder *psfNext = NULL;
  1127.         hres = psf->BindToObject(pidlItem, NULL, IID_IShellFolder, (void **)&psfNext);
  1128.         ILFree(pidlItem);
  1129.         if (!htiRoot)
  1130.         {
  1131.             ATOMICRELEASE(psfNext);
  1132.             break;
  1133.         }
  1134.         psf->Release();
  1135.         psf = psfNext;
  1136.         pidlChild = _ILNext(pidlChild);
  1137.         // if we're down to an empty pidl, we've found it!
  1138.         if (ILIsEmpty(pidlChild)) 
  1139.         {
  1140.             htiRet = htiRoot;
  1141.             break;
  1142.         }
  1143.         if (FAILED(hres))
  1144.         {
  1145.             ASSERT(psfNext == NULL);
  1146.             break;
  1147.         }
  1148.     }
  1149.     if (psf) 
  1150.         psf->Release();
  1151.     if (fFreePidlParent)
  1152.         ILFree(pidlParent);
  1153. #ifdef DEBUG
  1154.     TraceHTREE(htiRet, TEXT("Found at"));
  1155. #endif
  1156.     return htiRet;
  1157. }
  1158. BOOL CNscTree::_FIsItem(IShellFolder * psf, LPCITEMIDLIST pidl, HTREEITEM hti)
  1159. {
  1160.     PORDERITEM poi = _GetTreeOrderItem(hti);
  1161.     if (poi)
  1162.     {
  1163.         if (poi->pidl && psf->CompareIDs(0, poi->pidl, pidl) == 0)
  1164.             return TRUE;
  1165.     }
  1166.     return FALSE;
  1167. }
  1168. HRESULT CNscTree::_OnSHNotifyDelete(LPCITEMIDLIST pidl, int *piPosDeleted, HTREEITEM *phtiParent)
  1169. {
  1170.     HRESULT     hres = S_FALSE;
  1171.     HTREEITEM   hti = _FindFromRoot(NULL, pidl);
  1172.     
  1173.     if (hti == TVI_ROOT)
  1174.         return E_INVALIDARG;        // invalid arg, DELETION OF TVI_ROOT
  1175.     // need to clear _pidlDrag if the one being deleted is _pidlDrag.
  1176.     // handles case where dragging into another folder from within or dragging out.
  1177.     if (_pidlDrag)
  1178.     {
  1179.         LPCITEMIDLIST pidltst = _CacheParentShellFolder(hti, NULL);
  1180.         if (pidltst)
  1181.         {
  1182.             if (!_psfCache->CompareIDs(0, pidltst, _pidlDrag))
  1183.                 _pidlDrag = NULL;
  1184.         }
  1185.     }
  1186.     if (pidl && (hti != NULL))
  1187.     {
  1188.         _fIgnoreNextItemExpanding = TRUE;
  1189.         HTREEITEM htiParent = TreeView_GetParent(_hwndTree, hti);
  1190.         
  1191.         if (phtiParent)
  1192.             *phtiParent = htiParent;
  1193.         //if caller wants the position of the deleted item, don't reorder the other items
  1194.         if (piPosDeleted)
  1195.         {
  1196.             PORDERITEM poi = _GetTreeOrderItem(hti);
  1197.             if (poi)
  1198.             {
  1199.                 *piPosDeleted = poi->nOrder;
  1200.                 hres = S_OK;
  1201.             }
  1202.             TreeView_DeleteItem(_hwndTree, hti);
  1203.         }
  1204.         else
  1205.         {
  1206.             if (htiParent == NULL)
  1207.                 htiParent = TVI_ROOT;
  1208.             if (TreeView_DeleteItem(_hwndTree, hti))
  1209.             {
  1210.                 _ReorderChildren(htiParent);
  1211.                 hres = S_OK;
  1212.             }
  1213.         }
  1214.         // Update the + next to the parent folder. Note that History and Favorites
  1215.         // set ALL of their items to be Folder items, so this is not needed for
  1216.         // favorites.
  1217.         if (_mode == MODE_NORMAL)
  1218.         {
  1219.             LPCITEMIDLIST pidl = _CacheParentShellFolder(htiParent, NULL);
  1220.             if (pidl && !ILIsEmpty(pidl))
  1221.             {
  1222.                 DWORD dwAttrib = SFGAO_HASSUBFOLDER;
  1223.                 if (SUCCEEDED(_psfCache->GetAttributesOf(1, &pidl, &dwAttrib)) &&
  1224.                     !(dwAttrib & SFGAO_HASSUBFOLDER))
  1225.                 {
  1226.                     TV_ITEM tvi;
  1227.                     tvi.mask = TVIF_CHILDREN | TVIF_HANDLE;
  1228.                     tvi.hItem = htiParent;
  1229.                     tvi.cChildren = 0;
  1230.                     TreeView_SetItem(_hwndTree, &tvi);
  1231.                 }
  1232.             }
  1233.         }
  1234.         _fIgnoreNextItemExpanding = FALSE;
  1235.         if (hti == _htiCut)
  1236.         {
  1237.             _htiCut = NULL;
  1238.             _TreeNukeCutState();
  1239.         }
  1240.     }
  1241.     return hres;
  1242. }
  1243. //
  1244. //  Attempt to perform a rename-in-place.  Returns
  1245. //
  1246. //  S_OK - rename succeeded
  1247. //  S_FALSE - original object not found
  1248. //  error - rename failed
  1249. //
  1250. HRESULT CNscTree::_OnSHNotifyRename(LPCITEMIDLIST pidl, LPCITEMIDLIST pidlNew)
  1251. {
  1252.     HTREEITEM   hti, htiParent = NULL;
  1253.     int         iPosDeleted = DEFAULTORDERPOSITION;
  1254.     HRESULT     hres;
  1255.     //
  1256.     //  If the source and destination belong to the same folder, then
  1257.     //  it's an in-folder rename.
  1258.     //
  1259.     LPITEMIDLIST pidlParent = ILCloneParent(pidl);
  1260.     LPITEMIDLIST pidlNewParent = ILCloneParent(pidlNew);
  1261.     LPCITEMIDLIST pidlNewChild;
  1262.     LPITEMIDLIST pidlRealChild;
  1263.     IShellFolder *psf = NULL;
  1264.     PORDERITEM poi;
  1265.     if (pidlParent && pidlNewParent &&
  1266.         IEILIsEqual(pidlParent, pidlNewParent, TRUE) &&
  1267.         (hti = _FindFromRoot(NULL, pidl)) &&
  1268.         (poi = _GetTreeOrderItem(hti)) &&
  1269.         SUCCEEDED(_ParentFromItem(pidlNew, &psf, &pidlNewChild)) &&
  1270.         SUCCEEDED(_IdlRealFromIdlSimple(psf, pidlNewChild, &pidlRealChild)))
  1271.     {
  1272.         // Just substitute the final pidl component
  1273.         ILFree(poi->pidl);
  1274.         poi->pidl = pidlRealChild;
  1275.         _TreeInvalidateItemInfo(hti, TVIF_TEXT);
  1276.         // BUGBUG If we renamed the item the user is sitting on,
  1277.         // SHBrowseForFolder doesn't realize it and doesn't update the
  1278.         // edit control.
  1279.         hres = S_OK;
  1280.     }
  1281.     else
  1282.     // rename can be a move, so do not depend on the delete happening successfully.
  1283.     if ((_OnSHNotifyDelete(pidl, &iPosDeleted, &htiParent) != E_INVALIDARG)   // invalid arg indication of bogus rename, do not continue.
  1284.         && ((hti = _FindFromRoot(NULL, pidlNew)) == NULL) 
  1285.         && (_OnSHNotifyCreate(pidlNew, iPosDeleted, htiParent) == S_OK))
  1286.     {
  1287.         hres = S_OK;
  1288.     }
  1289.     else
  1290.     {
  1291.         hres = S_FALSE;
  1292.     }
  1293.     ILFree(pidlParent);
  1294.     ILFree(pidlNewParent);
  1295.     ATOMICRELEASE(psf);
  1296.     return hres;
  1297.     
  1298. }
  1299. //
  1300. //  To update an item, just find it and invalidate it.
  1301. //
  1302. void CNscTree::_OnSHNotifyUpdateItem(LPCITEMIDLIST pidl)
  1303. {
  1304.     HTREEITEM hti = _FindFromRoot(NULL, pidl);
  1305.     if (hti)
  1306.         _TreeInvalidateItemInfo(hti, TVIF_TEXT);
  1307. }
  1308. HRESULT CNscTree::_OnSHNotifyUpdateDir(LPCITEMIDLIST pidl)
  1309. {
  1310.     HRESULT         hres = S_FALSE;
  1311.     HTREEITEM       hti;
  1312.     if (((hti = _FindFromRoot(NULL, pidl)) != NULL))
  1313.     {   // folder exists in tree refresh folder now if had been loaded by expansion.
  1314.         TV_ITEM tvi;
  1315.         tvi.mask = TVIF_STATE;
  1316.         tvi.stateMask = (TVIS_EXPANDEDONCE | TVIS_EXPANDED | TVIS_EXPANDPARTIAL);
  1317.         tvi.hItem = (HTREEITEM)hti;
  1318.         if ((hti == TVI_ROOT) || (TreeView_GetItem(_hwndTree, &tvi) && (tvi.state & TVIS_EXPANDEDONCE)))
  1319.             hres = _UpdateDir(hti);
  1320.         else if (!(tvi.state & TVIS_EXPANDEDONCE))
  1321.         {
  1322.             TV_ITEM     tvi;
  1323.             tvi.mask = TVIF_CHILDREN | TVIF_HANDLE;   // only change the number of children so expand below will work.
  1324.             tvi.hItem = hti;
  1325.             tvi.cChildren = 1;
  1326.             TreeView_SetItem(_hwndTree, &tvi);
  1327.         }
  1328.     }
  1329.     return hres;
  1330. }
  1331. HRESULT CNscTree::_GetEnum(IShellFolder *psf, LPCITEMIDLIST pidlFolder, IEnumIDList **ppenum)
  1332. {
  1333.     HWND hwnd = NULL;
  1334.     DWORD grfFlags = _grfFlags;
  1335.     if (_pFilter)
  1336.     {
  1337.         LPITEMIDLIST pidlFree = NULL;
  1338.         if (pidlFolder == NULL)
  1339.         {
  1340.             SHGetIDListFromUnk(psf, &pidlFree);
  1341.             pidlFolder = pidlFree;
  1342.         }
  1343.         _pFilter->GetEnumFlags(psf, pidlFolder, &hwnd, &grfFlags);
  1344.         if (pidlFree)
  1345.             ILFree(pidlFree);
  1346.     }
  1347.     // get the enumerator and add the child items for any given pidl
  1348.     // BUGBUG right now, we don't detect if we actually are dealing with a folder (shell32.dll
  1349.     // BUGBUG allows you to create an IShellfolder to a non folder object, so we get stupid
  1350.     // BUGUBG dialogs, by not passing the hwnd, we don't get the dialogs. we should fix this better. by caching
  1351.     // BUGBUG in the tree whether it is a folder or not.
  1352.     return psf->EnumObjects(/* _fAutoExpanding ?*/ hwnd, grfFlags, ppenum);
  1353. }
  1354. BOOL CNscTree::_ShouldShow(IShellFolder* psf, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem)
  1355. {
  1356.     BOOL bRet = TRUE;
  1357.     if (_pFilter)
  1358.     {
  1359.         LPITEMIDLIST pidlFree = NULL;
  1360.         if (pidlFolder == NULL)
  1361.         {
  1362.             SHGetIDListFromUnk(psf, &pidlFree);
  1363.             pidlFolder = pidlFree;
  1364.         }
  1365.         bRet = (S_OK == _pFilter->ShouldShow(psf, pidlFolder, pidlItem));
  1366.         if (pidlFree)
  1367.             ILFree(pidlFree);
  1368.     }
  1369.     return bRet;
  1370. }
  1371. // review chrisny:  threadize this 
  1372. // updates existing dir only.  Not new load.
  1373. HRESULT CNscTree::_UpdateDir(HTREEITEM hti)
  1374. {
  1375.     HTREEITEM       htiTemp;
  1376. #ifdef DEBUG
  1377. //    TraceHTREE(hti, TEXT("UpdateDIR"));
  1378. #endif
  1379.     
  1380.     // we now know the true state of the folder represented by input hti
  1381.     // , so recursively updatedir on all it's children. Nasty. . .
  1382.     // review chrisny:  perf on this.
  1383.     for (htiTemp = TreeView_GetChild(_hwndTree, hti); htiTemp
  1384.         ; htiTemp = TreeView_GetNextSibling(_hwndTree, htiTemp)) 
  1385.         _UpdateDir(htiTemp);
  1386.     if (!_CacheShellFolder(hti))          // immediate failure if not folder causing return.
  1387.         return S_FALSE;
  1388.     //if this item hasn't been expanded yet (and filled with items), ignore it
  1389.     TV_ITEM tvi;
  1390.     tvi.mask = TVIF_STATE;
  1391.     tvi.stateMask = (TVIS_EXPANDEDONCE | TVIS_EXPANDED);
  1392.     tvi.hItem = (HTREEITEM)hti;
  1393.     if ((hti != TVI_ROOT) && (TreeView_GetItem(_hwndTree, &tvi) && !(tvi.state & TVIS_EXPANDEDONCE)))
  1394.         return S_OK;
  1395.     HDPA hdpa = DPA_Create(4);
  1396.     if (!hdpa)
  1397.         return E_OUTOFMEMORY;
  1398.     
  1399.     LPITEMIDLIST pidlParent = _GetFullIDList(hti);
  1400.     if (pidlParent == NULL)
  1401.     {
  1402.         DPA_Destroy(hdpa);
  1403.         return S_FALSE;
  1404.     }
  1405.     IEnumIDList *penum; 
  1406.     if (S_OK == _GetEnum(_psfCache, pidlParent, &penum))
  1407.     {
  1408.         LPITEMIDLIST pidlChild;
  1409.         ULONG celt, i = 0;
  1410.         
  1411.         while (penum->Next(1, &pidlChild, &celt) == S_OK && celt == 1)
  1412.         {
  1413.             if (_ShouldShow(_psfCache, pidlParent, pidlChild))
  1414.             {
  1415.                 DPA_SetPtr(hdpa, i++, pidlChild);
  1416.             }
  1417.             else
  1418.                 ILFree(pidlChild);
  1419.         }
  1420.         
  1421.         penum->Release();
  1422.         
  1423.         // set pointers to be kept to NULL in DPA and delete item from tree
  1424.         celt = i;
  1425.         if (celt && (hti != TVI_ROOT))
  1426.         {
  1427.             TV_ITEM tvi;
  1428.             tvi.mask = TVIF_CHILDREN | TVIF_HANDLE;   // only change the number of children so expand below will work.
  1429.             tvi.hItem = hti;
  1430.             tvi.cChildren = celt;
  1431.             TreeView_SetItem(_hwndTree, &tvi);
  1432.         }
  1433.         
  1434.         HDPA hDel = DPA_Create(4);
  1435.         if (!hDel) 
  1436.             return E_FAIL;
  1437.         
  1438.         i = 0;
  1439.         for (htiTemp = TreeView_GetChild(_hwndTree, hti); htiTemp; ) 
  1440.         {
  1441.             HTREEITEM htiNextChild = TreeView_GetNextSibling(_hwndTree, htiTemp);
  1442.             // must delete in this way or break the linkage of tree.
  1443.             if (!_FInTree(hdpa, celt, htiTemp)) 
  1444.             {
  1445.                 DPA_SetPtr( hDel, i++, htiTemp );
  1446.                 //if (!TreeView_DeleteItem(_hwndTree, htiTemp))
  1447.                 //    ASSERT(FALSE);       // somethings hosed in the tree.
  1448.             }
  1449.             htiTemp = htiNextChild;
  1450.         }
  1451.         
  1452.         ULONG cDel = DPA_GetPtrCount( hDel );
  1453.         for(i = 0;i < cDel; i++ )
  1454.         {
  1455.             if (!TreeView_DeleteItem(_hwndTree, (HTREEITEM)DPA_FastGetPtr(hDel,i)))
  1456.             {
  1457.                 ASSERT(FALSE);       // somethings hosed in the tree.
  1458.             }
  1459.         }
  1460.         
  1461.         DPA_Destroy(hDel);
  1462.         
  1463.         for (i = 0; i < celt; i++)
  1464.         {
  1465.             pidlChild = (LPITEMIDLIST)DPA_GetPtr(hdpa, i);
  1466.             if (pidlChild)
  1467.             {
  1468.                 LPITEMIDLIST pidlFull = ILCombine(pidlParent, pidlChild);
  1469.                 if (pidlFull)
  1470.                 {
  1471.                     htiTemp = _FindFromRoot(hti, pidlFull);
  1472.                     // if we DON'T FIND IT add it to the tree . . .
  1473.                     if (!htiTemp)
  1474.                     {
  1475.                         if (_AddItemToTree(hti, pidlChild, 1, 32000, TVI_LAST, TRUE, _IsMarked(hti)))
  1476.                             DPA_SetPtr(hdpa, i, NULL);  // so we don't free below
  1477.                     }
  1478.                     ILFree(pidlFull);
  1479.                 }
  1480.             }
  1481.         }
  1482.         // pidls away . . .
  1483.         for (i = 0; i < celt; i++)
  1484.         {
  1485.             LPITEMIDLIST pidl = (LPITEMIDLIST)DPA_GetPtr(hdpa, i);
  1486.             if (pidl)
  1487.                 ILFree(pidl);
  1488.         }
  1489.         EVAL(DPA_Destroy(hdpa));
  1490.         // Handle Ordering information.
  1491.         HDPA hdpaOrder;
  1492.         if (_LoadOrder(hti, pidlParent, _psfCache, &hdpaOrder))
  1493.         {
  1494.             HTREEITEM htiChild = TreeView_GetChild(_hwndTree, hti);
  1495.             // We have to do a manual merge with the items in the listview.
  1496.             // Exercise left for the student: Make this fast.
  1497.             for (; htiChild; htiChild = TreeView_GetNextSibling(_hwndTree, htiChild)) 
  1498.             {
  1499.                 PORDERITEM poi = _GetTreeOrderItem(htiChild);
  1500.                 if (poi)
  1501.                 {
  1502.                     int cOrder = DPA_GetPtrCount(hdpaOrder);
  1503.                     for (int i = 0; i < cOrder; i++)
  1504.                     {
  1505.                         PORDERITEM poiOrder = (PORDERITEM)DPA_FastGetPtr(hdpaOrder, i);
  1506.                         if (_psfCache->CompareIDs(0, poi->pidl, poiOrder->pidl) == 0)
  1507.                         {
  1508.                             poi->nOrder = poiOrder->nOrder;
  1509.                             OrderItem_Free(poiOrder);
  1510.                             DPA_DeletePtr(hdpaOrder, i);
  1511.                             break;
  1512.                         }
  1513.                     }
  1514.                 }
  1515.             }
  1516.             OrderList_Destroy(&hdpaOrder, FALSE);
  1517.         }
  1518.         
  1519.         _Sort(hti, _psfCache);
  1520.     }
  1521.     ILFree(pidlParent);
  1522.     return S_OK;
  1523. }
  1524. BOOL CNscTree::_FInTree(HDPA hdpa, int celt, HTREEITEM hti)
  1525. {
  1526.     ASSERT(hti);
  1527.     PORDERITEM poi = _GetTreeOrderItem(hti);
  1528.     if (poi)
  1529.     {
  1530.         for (int i = 0; i < celt; i++)
  1531.         {
  1532.             LPITEMIDLIST    pidl;
  1533.             if ((pidl = (LPITEMIDLIST)DPA_GetPtr(hdpa, i)) == NULL)
  1534.                 continue;
  1535.             if (_psfCache->CompareIDs(0, poi->pidl
  1536.                 , pidl) == 0)
  1537.             {
  1538.                 ILFree(pidl);
  1539.                 DPA_SetPtr(hdpa, i++, NULL);
  1540.                 return TRUE;
  1541.             }
  1542.         }
  1543.     }
  1544.     return FALSE;
  1545. }
  1546. HTREEITEM CNscTree::_FindChild(IShellFolder *psf, HTREEITEM htiParent, LPCITEMIDLIST pidlChild)
  1547. {
  1548.      HTREEITEM hti;
  1549.     
  1550.     for (hti = TreeView_GetChild(_hwndTree, htiParent); hti
  1551.         ; hti = TreeView_GetNextSibling(_hwndTree, hti))
  1552.     {
  1553.         if (_FIsItem(psf, pidlChild, hti))
  1554.             break;
  1555.     }
  1556.     return hti;
  1557. }
  1558. void CNscTree::_ReorderChildren(HTREEITEM htiParent)
  1559. {
  1560.     int i = 0;
  1561.     HTREEITEM hti;
  1562.     for (hti = TreeView_GetChild(_hwndTree, htiParent); hti
  1563.         ; hti = TreeView_GetNextSibling(_hwndTree, hti))
  1564.     {
  1565.         PORDERITEM poi = _GetTreeOrderItem(hti);
  1566.         if (poi)
  1567.         {
  1568.             poi->nOrder = i++;        // reset the positions of the nodes.
  1569.         }
  1570.     }
  1571. }
  1572. HRESULT CNscTree::_InsertChild(HTREEITEM htiParent, IShellFolder *psfParent, LPCITEMIDLIST pidlChild, BOOL fExpand, int iPosition)
  1573. {
  1574.     LPITEMIDLIST pidlReal;
  1575.     HRESULT hres = S_OK;
  1576.     
  1577.     // review chrisny:  no sort here, use compareitems to insert item instead.
  1578.     if (SUCCEEDED(_IdlRealFromIdlSimple(psfParent, pidlChild, &pidlReal)))
  1579.     {
  1580.         HTREEITEM htiAfter = TVI_LAST;
  1581.         if (iPosition != DEFAULTORDERPOSITION)
  1582.         {
  1583.             if (iPosition == 0)
  1584.                 htiAfter = htiParent;
  1585.             else
  1586.             {
  1587.                 for (HTREEITEM hti = TreeView_GetChild(_hwndTree, htiParent); hti
  1588.                     ; hti = TreeView_GetNextSibling(_hwndTree, hti))
  1589.                 {
  1590.                     PORDERITEM poi = _GetTreeOrderItem(hti);
  1591.                     if (poi)
  1592.                     {
  1593.                         if (poi->nOrder == iPosition-1)
  1594.                         {
  1595.                             htiAfter = hti;
  1596. #ifdef DEBUG
  1597.                             TraceHTREE(htiAfter, TEXT("Inserting After"));
  1598. #endif
  1599.                             break;
  1600.                         }
  1601.                     }
  1602.                 }
  1603.             }
  1604.         }
  1605.         HTREEITEM   htiNew;
  1606.         DWORD       dwAttrib = SFGAO_HASSUBFOLDER;
  1607.         if ((_FindChild(psfParent, htiParent, pidlReal) == NULL) && 
  1608.             SUCCEEDED(psfParent->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlReal, &dwAttrib)) &&
  1609.             ((htiNew = _AddItemToTree(htiParent, pidlReal, 
  1610.                                       (MODE_NORMAL != _mode || dwAttrib & SFGAO_HASSUBFOLDER)? 1 : 0, 
  1611.                                       iPosition, htiAfter)) != NULL))
  1612.         {
  1613.             // if added at the default position OR anywhere when sorted by name,
  1614.             //  refresh the order information for the items including the newly added item.
  1615.             if ( (iPosition == DEFAULTORDERPOSITION) || !_IsOrdered(htiParent) )
  1616.             {
  1617.                 _ReorderChildren(htiParent);
  1618.                 _Sort(htiParent, psfParent);
  1619.             }
  1620.             
  1621.             if (fExpand ) 
  1622.                 TreeView_Expand(_hwndTree, htiParent, TVE_EXPAND);    // force expansion to show new item.
  1623.             
  1624.             //ensure the item is visible after a rename (or external drop, but that should always be a noop)
  1625.             if (iPosition != DEFAULTORDERPOSITION)
  1626.                 TreeView_EnsureVisible(_hwndTree, htiNew);
  1627.             hres = S_OK;
  1628.         }
  1629.         else
  1630.         {
  1631.             ILFree(pidlReal);
  1632.             hres = S_FALSE;
  1633.         }       
  1634.     }
  1635.     return hres;
  1636. }
  1637. HRESULT CheckForExpandOnce(HWND hwndTree, HTREEITEM hti)
  1638. {
  1639.     HRESULT  hres = S_OK;
  1640.     TV_ITEM tvi;
  1641.     
  1642.     // Root node always expanded.
  1643.     if( hti == TVI_ROOT )
  1644.         return hres;
  1645.     
  1646.     tvi.mask = TVIF_STATE | TVIF_CHILDREN;
  1647.     tvi.stateMask = (TVIS_EXPANDEDONCE | TVIS_EXPANDED | TVIS_EXPANDPARTIAL);
  1648.     tvi.hItem = (HTREEITEM)hti;
  1649.     
  1650.     if (TreeView_GetItem(hwndTree, &tvi))
  1651.     {
  1652.         if( !(tvi.state & TVIS_EXPANDEDONCE) && (tvi.cChildren == 0) )
  1653.         {
  1654.             tvi.mask = TVIF_CHILDREN | TVIF_HANDLE;
  1655.             tvi.hItem = hti;
  1656.             tvi.cChildren = 1;
  1657.             TreeView_SetItem(hwndTree, &tvi);
  1658.         }
  1659.     }
  1660.     
  1661.     return hres;
  1662. }
  1663. HRESULT _InvokeCommandThunk(IContextMenu * pcm, HWND hwndParent)
  1664. {
  1665.     HRESULT hr;
  1666.     if (g_fRunningOnNT)
  1667.     {
  1668.         CMINVOKECOMMANDINFOEX ici = {0};
  1669.         ici.cbSize = sizeof(ici);
  1670.         ici.hwnd = hwndParent;
  1671.         ici.nShow = SW_NORMAL;
  1672.         ici.lpVerb = CMDSTR_NEWFOLDERA;
  1673.         ici.fMask = CMIC_MASK_UNICODE;
  1674.         ici.lpVerbW = CMDSTR_NEWFOLDERW;
  1675.         hr = pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)(&ici));
  1676.     }
  1677.     else
  1678.     {
  1679.         CMINVOKECOMMANDINFO ici = {0};
  1680.         ici.cbSize = sizeof(ici);
  1681.         ici.hwnd = hwndParent;
  1682.         ici.nShow = SW_NORMAL;
  1683.         ici.lpVerb = CMDSTR_NEWFOLDERA;
  1684.         // Win95 doesn't work with CMIC_MASK_UNICODE & CMDSTR_NEWFOLDERW
  1685.         hr = pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)(&ici));
  1686.     }
  1687.     return hr;
  1688. }
  1689. // We want to create a new folder in the current directory, so
  1690. // find it.
  1691. HRESULT CNscTree::NewFolder(void)
  1692. {
  1693.     return CreateNewFolder(TreeView_GetSelection(_hwndTree));
  1694. }
  1695. BOOL CNscTree::_IsItemExpanded(HTREEITEM hti)
  1696. {
  1697.     // if it's not open, then use it's parent
  1698.     TV_ITEM tvi;
  1699.     tvi.mask = TVIF_STATE;
  1700.     tvi.stateMask = TVIS_EXPANDED;
  1701.     tvi.hItem = (HTREEITEM)hti;
  1702.     
  1703.     return (TreeView_GetItem(_hwndTree, &tvi) && (tvi.state & TVIS_EXPANDED));
  1704. }
  1705. HRESULT CNscTree::CreateNewFolder(HTREEITEM hti)
  1706. {
  1707.     HRESULT hr = E_FAIL;
  1708.     if (hti)
  1709.     {
  1710.         // If the user selected a folder item (file), we need
  1711.         // to bind set the cache to the parent folder.
  1712.         LPITEMIDLIST pidl = _GetFullIDList(hti);
  1713.         if (EVAL(pidl))
  1714.         {
  1715.             ULONG ulAttr = SFGAO_FOLDER;    // make sure item is actually a folder
  1716.             if (EVAL(SUCCEEDED(IEGetAttributesOf(pidl, &ulAttr))))
  1717.             {
  1718.                 // Is it a folder?
  1719.                 if (ulAttr & SFGAO_FOLDER)
  1720.                 {
  1721.                     // non-Normal modes (!MODE_NORMAL) wants the new folder to be created as
  1722.                     // a sibling instead of as a child of the selected folder if it's
  1723.                     // closed.  I assume their reasoning is that closed folders are often
  1724.                     // selected by accident/default because these views are mostly 1 level.
  1725.                     // We don't want this functionality for the normal mode.
  1726.                     if ((MODE_NORMAL != _mode) && !_IsItemExpanded(hti))
  1727.                         _CacheShellFolder(TreeView_GetParent(_hwndTree, hti));
  1728.                     else                
  1729.                         _CacheShellFolder(hti);     // Yes, so fine.
  1730.                 }
  1731.                 else
  1732.                     _CacheShellFolder(TreeView_GetParent(_hwndTree, hti));  // No, so bind to the parent.
  1733.             }
  1734.             ILFree(pidl);
  1735.         }
  1736.     }
  1737.     // If no item is selected, we should still create a folder in whatever
  1738.     // the user most recently dinked with.  This is important if the
  1739.     // Favorites folder is completely empty.
  1740.     if (_psfCache)
  1741.     {
  1742.         // On Win95 it was only the view background menu that supported NewFolder,
  1743.         // On IE4 Shell+, it's the folder background object itself, but the view
  1744.         // still works.
  1745.         //
  1746.         IShellView * psv;
  1747.         hr = _psfCache->CreateViewObject(_hwndTree, IID_IShellView, (LPVOID *)&psv);
  1748.         if (SUCCEEDED(hr))
  1749.         {
  1750.             IContextMenu * pcm;
  1751.             if (SUCCEEDED(psv->GetItemObject(SVGIO_BACKGROUND, IID_IContextMenu, (LPVOID *)&pcm)))
  1752.             {
  1753.                 IUnknown_SetSite(pcm, psv);
  1754.                 HMENU hmContext = CreatePopupMenu();
  1755.                 hr = pcm->QueryContextMenu(hmContext, 0, 1, 256, 0);
  1756.                 if (SUCCEEDED(hr))
  1757.                 {
  1758.                     _pidlNewFolderParent = _GetFullIDList(_htiCache);
  1759.                     hr = _InvokeCommandThunk(pcm, _hwndParent);
  1760.                     SHChangeNotifyHandleEvents(); // Flush the events to it doesn't take forever to shift into edit mode
  1761.                     ILFree(_pidlNewFolderParent);
  1762.                     _pidlNewFolderParent = NULL;
  1763.                 }
  1764.                 IUnknown_SetSite(pcm, NULL);
  1765.                 DestroyMenu(hmContext);
  1766.                 pcm->Release();
  1767.             }
  1768.             psv->Release();
  1769.         }
  1770.     }
  1771.     return hr;
  1772. }
  1773. HRESULT CNscTree::_EnterNewFolderEditMode(LPCITEMIDLIST pidlNewFolder)
  1774. {
  1775.     HRESULT hres = S_OK;
  1776.     HTREEITEM htiNewFolder = _FindFromRoot(NULL, pidlNewFolder);
  1777.     LPITEMIDLIST pidlParent = NULL;
  1778.     
  1779.     // 1. Flush all the notifications.
  1780.     // 2. Find the new dir in the tree.
  1781.     //    Expand the parent if needed.
  1782.     // 3. Put it into the rename mode.     
  1783.     EVAL(SUCCEEDED(SetSelectedItem(pidlNewFolder, FALSE, FALSE, 0)));
  1784.     if (htiNewFolder == NULL) 
  1785.     {
  1786.         pidlParent = ILClone(pidlNewFolder);
  1787.         ILRemoveLastID(pidlParent);
  1788.         HTREEITEM htiParent = _FindFromRoot(NULL, pidlParent);
  1789.         // We are looking for the parent folder. If this is NOT
  1790.         // the root, then we need to expand it to show it.
  1791.         // NOTE: If it is root, Tree view is pretty stupid and will
  1792.         // try and deref TVI_ROOT and faults.
  1793.         if (htiParent != TVI_ROOT)
  1794.         {
  1795.             // Try expanding the parent and finding again.
  1796.             CheckForExpandOnce(_hwndTree, htiParent);
  1797.             TreeView_SelectItem(_hwndTree, htiParent);
  1798.             TreeView_Expand(_hwndTree, htiParent, TVE_EXPAND);
  1799.         }
  1800.         
  1801.         htiNewFolder = _FindFromRoot(NULL, pidlNewFolder);
  1802.     }
  1803.     if (htiNewFolder == NULL) 
  1804.     {
  1805.         // Something went very wrong here. We are not able to find newly added node.
  1806.         // One last try after refreshing the entire tree. (slow)
  1807.         // May be we didn't get notification.
  1808.         Refresh();
  1809.         htiNewFolder = _FindFromRoot(NULL, pidlNewFolder);
  1810.         if(htiNewFolder && (htiNewFolder != TVI_ROOT))
  1811.         {
  1812.             HTREEITEM htiParent = _FindFromRoot(NULL, pidlParent);
  1813.             // We are looking for the parent folder. If this is NOT
  1814.             // the root, then we need to expand it to show it.
  1815.             // NOTE: If it is root, Tree view is pretty stupid and will
  1816.             // try and deref TVI_ROOT and faults.
  1817.             if (htiParent != TVI_ROOT)
  1818.             {
  1819.                 CheckForExpandOnce(_hwndTree, htiParent);
  1820.                 TreeView_SelectItem(_hwndTree, htiParent);
  1821.                 TreeView_Expand(_hwndTree, htiParent, TVE_EXPAND);
  1822.             }
  1823.         }
  1824.         htiNewFolder = _FindFromRoot(NULL, pidlNewFolder);
  1825.     }
  1826.     // Put Edit label on the item for possible renaming by user.
  1827.     if (htiNewFolder) 
  1828.     {
  1829.         _fRclick = TRUE;  //otherwise label editing is canceled
  1830.         TreeView_EditLabel(_hwndTree, htiNewFolder);
  1831.         _fRclick = FALSE;
  1832.     }
  1833.     if (pidlParent)
  1834.         ILFree(pidlParent);
  1835.     return hres;
  1836. }
  1837. HRESULT CNscTree::_OnSHNotifyCreate(LPCITEMIDLIST pidl, int iPosition, HTREEITEM htiParent)
  1838. {
  1839.     HRESULT   hres = S_OK;
  1840.     HTREEITEM hti = NULL;
  1841.     
  1842.     if (ILIsParent(_pidlRoot, pidl, FALSE))
  1843.     {
  1844.         LPITEMIDLIST pidlParent = ILCloneParent(pidl);
  1845.         if (pidlParent)
  1846.         {
  1847.             hti = _FindFromRoot(NULL, pidlParent);
  1848.             ILFree(pidlParent);
  1849.         }
  1850.         if (hti)
  1851.         {   // folder exists in tree, if item expanded, load the node, else bag out.
  1852.             TV_ITEM tvi;
  1853.             if (hti != TVI_ROOT)
  1854.             {
  1855.                 tvi.mask = TVIF_STATE | TVIF_CHILDREN;
  1856.                 tvi.stateMask = (TVIS_EXPANDEDONCE | TVIS_EXPANDED | TVIS_EXPANDPARTIAL);
  1857.                 tvi.hItem = (HTREEITEM)hti;
  1858.                 
  1859.                 if(!TreeView_GetItem(_hwndTree, &tvi))
  1860.                     return hres;
  1861.                 
  1862.                 // If we drag and item over to a node which has never beem expanded
  1863.                 // before we will always fail to add the new node.
  1864.                 if( !(tvi.state & TVIS_EXPANDEDONCE) ) 
  1865.                 {
  1866.                     CheckForExpandOnce( _hwndTree, hti );
  1867.                     
  1868.                     tvi.mask = TVIF_STATE;
  1869.                     tvi.stateMask = (TVIS_EXPANDEDONCE | TVIS_EXPANDED | TVIS_EXPANDPARTIAL);
  1870.                     tvi.hItem = (HTREEITEM)hti;
  1871.                     // We need to reset this. This is causing some weird behaviour during drag and drop.
  1872.                     _fAsyncDrop = FALSE;
  1873.                     
  1874.                     if (!TreeView_GetItem(_hwndTree, &tvi))
  1875.                         return hres;
  1876.                 }
  1877.             }
  1878.             else
  1879.                 tvi.state = (TVIS_EXPANDEDONCE);    // evil root is always expanded.
  1880.             
  1881.             if (tvi.state & TVIS_EXPANDEDONCE)
  1882.             {
  1883.                 LPCITEMIDLIST   pidlChild;
  1884.                 IShellFolder    *psf;
  1885.                 hres = _ParentFromItem(pidl, &psf, &pidlChild);
  1886.                 if (_fAsyncDrop)    // inserted via drag/drop
  1887.                 {
  1888.                     int iNewPos =   _fInsertBefore ? (_iDragDest - 1) : _iDragDest;
  1889.                     LPITEMIDLIST pidlReal;
  1890.                     if (SUCCEEDED(_IdlRealFromIdlSimple(psf, pidlChild, &pidlReal)))
  1891.                     {
  1892.                         if (_MoveNode(_hwndTree, _iDragSrc, iNewPos, pidlReal))
  1893.                         {
  1894.                             TraceMsg(TF_NSC, "NSCBand:  Reordering Item");
  1895.                             _fDropping = TRUE;
  1896.                             _Dropped();
  1897.                             _fAsyncDrop = FALSE;
  1898.                             _fDropping = FALSE;
  1899.                         }
  1900.                     }
  1901.                     _htiCur = NULL;
  1902.                     _fDragging = _fInserting = _fDropping = FALSE;
  1903.                     _iDragDest = _iDragSrc = -1;
  1904.                 }
  1905.                 else   // standard shell notify create or drop with no insert, rename.
  1906.                 {
  1907.                     if (SUCCEEDED(hres))
  1908.                     {
  1909.                         if (_iDragDest >= 0)
  1910.                             iPosition = _iDragDest;
  1911.                         hres = _InsertChild(hti, psf, pidlChild, BOOLIFY(tvi.state & TVIS_SELECTED), iPosition);
  1912.                         if (_iDragDest >= 0 &&
  1913.                             SUCCEEDED(_PopulateOrderList(hti)))
  1914.                         {
  1915.                             _fDropping = TRUE;
  1916.                             _Dropped();
  1917.                             _fDropping = FALSE;
  1918.                         }
  1919.                         psf->Release();
  1920.                     }
  1921.                 }  
  1922.             }
  1923.         }
  1924.     }
  1925.     //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
  1926.     if (iPosition >= 0) //htiParent && (htiParent != hti) && 
  1927.     {
  1928.         //item was deleted, need to fixup order info
  1929.         _ReorderChildren(htiParent);
  1930.     }
  1931.     _UpdateActiveBorder(_htiActiveBorder);
  1932.     return hres;
  1933. }
  1934. //BUGBUG make this void
  1935. HRESULT CNscTree::_OnDeleteItem(NM_TREEVIEW *pnm)
  1936. {
  1937.     if (_htiActiveBorder == pnm->itemOld.hItem)
  1938.         _htiActiveBorder = NULL;
  1939.     ITEMINFO *  pii = (ITEMINFO *) pnm->itemOld.lParam;
  1940.     pnm->itemOld.lParam = NULL;
  1941. //    TraceMsg(TF_ALWAYS, "Nsc_Del(htiItem=%#08lx, pii=%#08lx)", pnm->itemOld.hItem, pii);
  1942.     OrderItem_Free(pii->poi, TRUE);
  1943.     LocalFree(pii);
  1944.     return S_OK;
  1945. }
  1946. void CNscTree::_GetDefaultIconIndex(LPCITEMIDLIST pidl, ULONG ulAttrs, TVITEM *pitem, BOOL fFolder)
  1947. {
  1948.     if (_iDefaultFavoriteIcon == 0)
  1949.     {
  1950.         WCHAR psz[MAX_PATH];
  1951.         DWORD cbSize = ARRAYSIZE(psz);
  1952.         int iTemp = 0;
  1953.         if (ERROR_SUCCESS == SHGetValue(HKEY_CLASSES_ROOT, L"InternetShortcut\DefaultIcon", NULL, NULL, (LPBYTE)&psz, &cbSize))
  1954.         {
  1955.             iTemp = PathParseIconLocation(psz);
  1956.         }
  1957.         _iDefaultFavoriteIcon = Shell_GetCachedImageIndex(psz, iTemp, 0);
  1958.         cbSize = ARRAYSIZE(psz);
  1959.         if (ERROR_SUCCESS == SHGetValue(HKEY_CLASSES_ROOT, L"Folder\DefaultIcon", NULL, NULL, (LPBYTE)&psz, &cbSize))
  1960.             iTemp = PathParseIconLocation(psz);
  1961.         _iDefaultFolderIcon = Shell_GetCachedImageIndex(psz, iTemp, 0);
  1962.     }
  1963.     pitem->iImage = pitem->iSelectedImage = (fFolder) ? _iDefaultFolderIcon : _iDefaultFavoriteIcon;
  1964. }
  1965. BOOL CNscTree::_LoadOrder(HTREEITEM hti, LPCITEMIDLIST pidl, IShellFolder* psf, HDPA* phdpa)
  1966. {
  1967.     BOOL fOrdered = FALSE;
  1968.     // there is no SFGAO_ bit that includes non folders so we need to enum
  1969.     // if we are showing non folders we have to do an enum to peek down at items below
  1970.     if (_hdpaOrd == NULL)
  1971.         _hdpaOrd = DPA_Create(2);
  1972.     
  1973.     if (_hdpaOrd)
  1974.     {
  1975.         HDPA hdpaOrder = NULL;
  1976.         IStream *pstm = GetOrderStream(pidl, STGM_READ);
  1977.         if (pstm)
  1978.         {
  1979.             OrderList_LoadFromStream(pstm, &hdpaOrder, psf);
  1980.             pstm->Release();
  1981.         }
  1982.         if ((hdpaOrder == NULL) || (DPA_GetPtrCount(hdpaOrder) == 0))
  1983.             fOrdered = FALSE;
  1984.         else
  1985.             fOrdered = TRUE;
  1986.         //set the tree item's ordered flag
  1987.         PORDERITEM poi;
  1988.         if (hti == TVI_ROOT)
  1989.         {
  1990.             _fOrdered = TRUE;
  1991.         }
  1992.         else if ((poi = _GetTreeOrderItem(hti)) != NULL)
  1993.         {
  1994.             poi->lParam = fOrdered;
  1995.         }
  1996.         *phdpa = hdpaOrder;
  1997.     }
  1998.     return fOrdered;
  1999. }
  2000. // load shell folder and deal with persisted ordering.
  2001. HRESULT CNscTree::_LoadSF(HTREEITEM htiRoot, LPCITEMIDLIST pidl, BOOL fAddingOnly, int * pcAdded, BOOL * pfOrdered)
  2002. {
  2003.     ASSERT(pcAdded);
  2004.     ASSERT(pfOrdered);
  2005. #ifdef DEBUG
  2006.     TraceHTREE(htiRoot, TEXT("Loading the Shell Folder for"));
  2007. #endif
  2008.     if (_CacheShellFolder(htiRoot))
  2009.     {    
  2010.         HRESULT hres;
  2011.         HDPA hdpaOrder = NULL;
  2012.         IShellFolder *psfItem = _psfCache;
  2013.         psfItem->AddRef();  // hang on as adding items may change the cached psfCache
  2014.     
  2015.         *pfOrdered = _LoadOrder(htiRoot, pidl, psfItem, &hdpaOrder);
  2016.         if (_hdpaOrd)
  2017.         {
  2018.             IEnumIDList *penum;
  2019.             hres = _GetEnum(psfItem, pidl, &penum);
  2020.             if (S_OK == hres)
  2021.             {
  2022.                 ULONG celt;
  2023.                 LPITEMIDLIST pidlTemp;
  2024.                 while (penum->Next(1, &pidlTemp, &celt) == S_OK && celt == 1)
  2025.                 {
  2026.                     if (_ShouldShow(_psfCache, pidl, pidlTemp))
  2027.                     {
  2028.                         if (!OrderList_Append(_hdpaOrd, pidlTemp, -1))
  2029.                         {
  2030.                             ILFree(pidlTemp);
  2031.                             penum->Release();
  2032.                             goto LGone;
  2033.                         }
  2034.                     }
  2035.                     else
  2036.                         ILFree(pidlTemp);
  2037.                 }
  2038.                 penum->Release();
  2039.             }
  2040.             else
  2041.                 hres = S_FALSE;
  2042.             
  2043.             ORDERINFO oinfo;
  2044.             oinfo.psf = psfItem;
  2045.             oinfo.dwSortBy = OI_SORTBYNAME; // merge depends on by name.
  2046.             if (*pfOrdered)
  2047.                 OrderList_Merge(_hdpaOrd, hdpaOrder,  -1, (LPARAM)&oinfo, NULL, NULL);
  2048.             oinfo.dwSortBy = (*pfOrdered ? OI_SORTBYORDINAL : OI_SORTBYNAME);
  2049.             
  2050.             DPA_Sort(_hdpaOrd, OrderItem_Compare, (LPARAM)&oinfo);
  2051.             
  2052.             OrderList_Reorder(_hdpaOrd);
  2053.             BOOL fParentMarked = _IsMarked(htiRoot);
  2054.             BOOL fCheckForDups = !fAddingOnly;
  2055.             
  2056.             // load merged item list into tree.
  2057.             *pcAdded = DPA_GetPtrCount(_hdpaOrd);
  2058.             for (int i = 0; i < *pcAdded; i++)
  2059.             {
  2060.                 DWORD dwAttrib = SFGAO_HASSUBFOLDER;
  2061.                 PORDERITEM pitoi = (PORDERITEM)DPA_FastGetPtr(_hdpaOrd, i);
  2062.                 if (pitoi == NULL)
  2063.                     break;
  2064.                 
  2065.                 psfItem->GetAttributesOf(1, (LPCITEMIDLIST*)&pitoi->pidl, &dwAttrib);
  2066.                     
  2067.                 // If this is a normal NSC, we need to display the plus sign correctly.
  2068.                 HTREEITEM hti = _AddItemToTree(htiRoot, pitoi->pidl, 
  2069.                                                (MODE_NORMAL != _mode || dwAttrib & SFGAO_HASSUBFOLDER)? 1 : 0, 
  2070.                                                pitoi->nOrder, TVI_LAST, fCheckForDups, fParentMarked);
  2071.                 if (hti == NULL)
  2072.                     break;
  2073.             }
  2074.         }
  2075.         else
  2076.         {
  2077.             hres = E_OUTOFMEMORY;
  2078.         }
  2079. LGone:
  2080.         OrderList_Destroy(&_hdpaOrd, FALSE);
  2081.         OrderList_Destroy(&hdpaOrder, TRUE);        // calls DPA_Destroy(hdpaOrder)
  2082.         psfItem->Release();
  2083.         
  2084.         return hres;
  2085.     }
  2086.     return S_FALSE;
  2087. }
  2088. // review chrisny:  get rid of this function.
  2089. int CNscTree::_GetChildren(IShellFolder *psf, LPCITEMIDLIST pidl, ULONG ulAttrs)
  2090. {
  2091.     int cChildren = 0;  // assume none
  2092.     
  2093.     if (ulAttrs & SFGAO_FOLDER)
  2094.     {
  2095.         if (_grfFlags & SHCONTF_NONFOLDERS)
  2096.         {
  2097.             // there is no SFGAO_ bit that includes non folders so we need to enum
  2098.             IShellFolder *psfItem;
  2099.             if (SUCCEEDED(psf->BindToObject(pidl, NULL, IID_IShellFolder, (void **)&psfItem)))
  2100.             {
  2101.                 // if we are showing non folders we have to do an enum to peek down at items below
  2102.                 IEnumIDList *penum;
  2103.                 if (S_OK == _GetEnum(psfItem, NULL, &penum))
  2104.                 {
  2105.                     ULONG celt;
  2106.                     LPITEMIDLIST pidlTemp;
  2107.                     
  2108.                     if (penum->Next(1, &pidlTemp, &celt) == S_OK && celt == 1)
  2109.                     {
  2110.                         if (_ShouldShow(psfItem, NULL, pidlTemp))
  2111.                         {
  2112.                             cChildren = 1;
  2113.                         }
  2114.                         ILFree(pidlTemp);
  2115.                     }
  2116.                     penum->Release();
  2117.                 }
  2118.                 psfItem->Release();
  2119.             }
  2120.         }
  2121.         else
  2122.         {
  2123.             // if just folders we can peek at the attributes
  2124.             ULONG ulAttrs = SFGAO_HASSUBFOLDER;
  2125.             psf->GetAttributesOf(1, &pidl, &ulAttrs);
  2126.             
  2127.             cChildren = (ulAttrs & SFGAO_HASSUBFOLDER) ? 1 : 0;
  2128.         }
  2129.     }
  2130.     return cChildren;
  2131. }
  2132. void CNscTree::_OnGetDisplayInfo(TV_DISPINFO *pnm)
  2133. {
  2134.     PORDERITEM poi = GetPoi(pnm->item.lParam);
  2135.     LPCITEMIDLIST pidl = _CacheParentShellFolder(pnm->item.hItem, poi->pidl);
  2136.     ASSERT(pidl);
  2137.     if (pidl == NULL)
  2138.         return;
  2139.     ASSERT(_psfCache);
  2140.     ASSERT(pnm->item.mask & (TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_CHILDREN));
  2141.     if (pnm->item.mask & TVIF_TEXT)
  2142.     {
  2143.         SHELLDETAILS details;
  2144.         if (SUCCEEDED(_GetDisplayNameOf(_psfCache, pidl, SHGDN_INFOLDER, &details)))
  2145.             StrRetToBuf(&details.str, pidl, pnm->item.pszText, pnm->item.cchTextMax);
  2146.     }
  2147.     // make sure we set the attributes for those flags that need them
  2148.     if (pnm->item.mask & (TVIF_CHILDREN | TVIF_IMAGE | TVIF_SELECTEDIMAGE))
  2149.     {
  2150.         ULONG ulAttrs = SFGAO_FOLDER | SFGAO_NEWCONTENT;
  2151.         _psfCache->GetAttributesOf(1, &pidl, &ulAttrs);
  2152.         // review chrisny:  still need to handle notify of changes from
  2153.         //  other navs.
  2154.         
  2155.         // HACKHACK!!!  we're using the TVIS_FOCUSED bit to stored whether there's
  2156.         // new content or not. 
  2157.         if (ulAttrs & SFGAO_NEWCONTENT)
  2158.         {
  2159.             pnm->item.mask |= TVIF_STATE;
  2160.             pnm->item.stateMask = TVIS_FOCUSED;  // init state mask to bold
  2161.             pnm->item.state = TVIS_FOCUSED;  // init state mask to bold
  2162.         }
  2163.         // Also see if this guy has any child folders
  2164.         if (pnm->item.mask & TVIF_CHILDREN)
  2165.             pnm->item.cChildren = _GetChildren(_psfCache, pidl, ulAttrs);
  2166.         
  2167.         if (pnm->item.mask & (TVIF_IMAGE | TVIF_SELECTEDIMAGE))
  2168.             // We now need to map the item into the right image index.
  2169.             _GetDefaultIconIndex(pidl, ulAttrs, &pnm->item, (ulAttrs & SFGAO_FOLDER));
  2170.         _UpdateItemDisplayInfo(pnm->item.hItem);
  2171.     }
  2172.     // force the treeview to store this so we don't get called back again
  2173.     pnm->item.mask |= TVIF_DI_SETITEM;
  2174. }
  2175. #define SZ_CUTA                 "cut"
  2176. #define SZ_CUT                  TEXT(SZ_CUTA)
  2177. #define SZ_RENAMEA              "rename"
  2178. #define SZ_RENAME               TEXT(SZ_RENAMEA)
  2179. void CNscTree::_ApplyCmd(HTREEITEM hti, IContextMenu *pcm, UINT idCmd)
  2180. {
  2181.     TCHAR szCommandString[40];
  2182.     BOOL fHandled = FALSE;
  2183.     BOOL fCutting = FALSE;
  2184.     
  2185.     // We need to special case the rename command
  2186.     if (SUCCEEDED(ContextMenu_GetCommandStringVerb(pcm, idCmd, szCommandString, ARRAYSIZE(szCommandString))))
  2187.     {
  2188.         if (StrCmpI(szCommandString, SZ_RENAME)==0) 
  2189.         {
  2190.             TreeView_EditLabel(_hwndTree, hti);
  2191.             fHandled = TRUE;
  2192.         } 
  2193.         else if (!StrCmpI(szCommandString, SZ_CUT)) 
  2194.         {
  2195.             fCutting = TRUE;
  2196.         }
  2197.     }
  2198.     
  2199.     if (!fHandled)
  2200.     {
  2201.         CMINVOKECOMMANDINFO ici = {
  2202.             sizeof(CMINVOKECOMMANDINFO),
  2203.                 0L,
  2204.                 _hwndTree,
  2205.                 MAKEINTRESOURCEA(idCmd),
  2206.                 NULL, NULL,
  2207.                 SW_NORMAL,
  2208.         };
  2209.         
  2210.         HRESULT hres = pcm->InvokeCommand(&ici);
  2211.         if (fCutting && SUCCEEDED(hres))
  2212.         {
  2213.             TV_ITEM tvi;
  2214.             tvi.mask = TVIF_STATE;
  2215.             tvi.stateMask = TVIS_CUT;
  2216.             tvi.state = TVIS_CUT;
  2217.             tvi.hItem = hti;
  2218.             TreeView_SetItem(_hwndTree, &tvi);
  2219.             
  2220.             // _hwndNextViewer = SetClipboardViewer(_hwndTree);
  2221.             // _htiCut = hti;
  2222.         }
  2223.         
  2224.         //hack to force a selection update, so oc can update it's status text
  2225.         if (_mode & MODE_CONTROL)
  2226.         {
  2227.             HTREEITEM hti = TreeView_GetSelection(_hwndTree);
  2228.             
  2229.             SendMessage(_hwndTree, WM_SETREDRAW, FALSE, 0);
  2230.             TreeView_SelectItem(_hwndTree, NULL);
  2231.             
  2232.             //only select the item if the handle is still valid
  2233.             if (hti)
  2234.                 TreeView_SelectItem(_hwndTree, hti);
  2235.             SendMessage(_hwndTree, WM_SETREDRAW, TRUE, 0);
  2236.         }
  2237.     }
  2238. }
  2239. // perform actions like they were chosen from the context menu, but without showing the menu
  2240. HRESULT CNscTree::InvokeContextMenuCommand(BSTR strCommand)
  2241. {
  2242.     ASSERT(strCommand);
  2243.     HTREEITEM htiSelected = TreeView_GetSelection(_hwndTree);
  2244.     
  2245.     if (htiSelected)
  2246.     {
  2247.         if (StrCmpIW(strCommand, L"rename") == 0) 
  2248.         {
  2249.             _fRclick = TRUE;  //otherwise label editing is canceled
  2250.             TreeView_EditLabel(_hwndTree, htiSelected);
  2251.             _fRclick = FALSE;
  2252.         }
  2253.         else
  2254.         {
  2255.             LPCITEMIDLIST pidl = _CacheParentShellFolder(htiSelected, NULL);
  2256.             if (pidl)
  2257.             {
  2258.                 IContextMenu *pcm;
  2259.                 
  2260.                 if (SUCCEEDED(_psfCache->GetUIObjectOf(_hwndTree, 1, &pidl, IID_IContextMenu, NULL, (void **)&pcm)))
  2261.                 {
  2262.                     CHAR szCommand[MAX_PATH];
  2263.                     SHUnicodeToAnsi(strCommand, szCommand, ARRAYSIZE(szCommand));
  2264.                     
  2265.                     // QueryContextMenu, even though unused, initializes the folder properly (fixes delete subscription problems)
  2266.                     HMENU hmenu = CreatePopupMenu();
  2267.                     if (hmenu)
  2268.                         pcm->QueryContextMenu(hmenu, 0, 0, 0x7fff, CMF_NORMAL);
  2269.                     /* Need to try twice, in case callee is ANSI-only */
  2270.                     CMINVOKECOMMANDINFOEX ici = 
  2271.                     {
  2272.                         CMICEXSIZE_NT4,         /* Be NT4-compat */
  2273.                         CMIC_MASK_UNICODE,
  2274.                         _hwndTree,
  2275.                         szCommand,
  2276.                         NULL, NULL,
  2277.                         SW_NORMAL,
  2278.                         0, NULL,
  2279.                         NULL,
  2280.                         strCommand,
  2281.                         NULL, NULL,
  2282.                         NULL,
  2283.                     };
  2284.                     
  2285.                     HRESULT hres = pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
  2286.                     if (hres == E_INVALIDARG) 
  2287.                     {
  2288.                         // Recipient didn't like the unicode command; send an ANSI one
  2289.                         ici.cbSize = sizeof(CMINVOKECOMMANDINFO);
  2290.                         ici.fMask &= ~CMIC_MASK_UNICODE;
  2291.                         pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
  2292.                     }
  2293.                     // do any visuals for cut state
  2294.                     if (SUCCEEDED(hres) && StrCmpIW(strCommand, L"cut") == 0) 
  2295.                     {
  2296.                         HTREEITEM hti = TreeView_GetSelection(_hwndTree);
  2297.                         if (hti) 
  2298.                         {
  2299.                             _TreeSetItemState(hti, TVIS_CUT, TVIS_CUT);
  2300.                             ASSERT(!_hwndNextViewer);
  2301.                             _hwndNextViewer = SetClipboardViewer(_hwndTree);
  2302.                             _htiCut = hti;
  2303.                         }
  2304.                     }
  2305.                     if (hmenu)
  2306.                         DestroyMenu(hmenu);
  2307.                     pcm->Release();
  2308.                 }
  2309.             }
  2310.         }
  2311.         
  2312.         //if properties was invoked, who knows what might have changed, so force a reselect
  2313.         if (StrCmpNW(strCommand, L"properties", 10) == 0)
  2314.         {
  2315.             TreeView_SelectItem(_hwndTree, htiSelected);
  2316.         }
  2317.     }
  2318.     return S_OK;
  2319. }
  2320. //
  2321. //  pcm = IContextMenu for the item the user selected
  2322. //  hti = the item the user selected
  2323. //
  2324. //  Okay, this menu thing is kind of funky.
  2325. //
  2326. //  If "Favorites", then everybody gets "Create new folder".
  2327. //
  2328. //  If expandable:
  2329. //      Show "Expand" or "Collapse"
  2330. //      (accordingly) and set it as the default.
  2331. //
  2332. //  If not expandable:
  2333. //      The default menu of the underlying context menu is
  2334. //      used as the default; or use the first item if nobody
  2335. //      picked a default.
  2336. //
  2337. //      We replace the existing "Open" command with our own.
  2338. //
  2339. HMENU CNscTree::_CreateContextMenu(IContextMenu *pcm, HTREEITEM hti)
  2340. {
  2341.     BOOL fExpandable = _IsExpandable(hti);
  2342.     HRESULT hres;
  2343.     HMENU hmenu = CreatePopupMenu();
  2344.     if (hmenu)
  2345.     {
  2346.         pcm->QueryContextMenu(hmenu, 0, idCmdStart, 0x7fff,
  2347.             CMF_EXPLORE | CMF_CANRENAME);
  2348.         //
  2349.         //  Always delete "Create shortcut" from the context menu.
  2350.         //
  2351.         //  Sometimes we need to delete "Open":
  2352.         //
  2353.         //  History mode always.  The context menu for history mode folders
  2354.         //  has "Open" but it doesn't work, so we need to replace it with
  2355.         //  Expand/Collapse.  And the context menu for history mode items
  2356.         //  has "Open" but it opens in a new window.  We want to navigate.
  2357.         //
  2358.         //  Favorites mode, expandable:  Leave "Open" alone -- it will open
  2359.         //  the expandable thing in a new window.
  2360.         //
  2361.         //  Favorites mode, non-expandable: Delete the original "Open" and
  2362.         //  replace it with ours that does a navigate.
  2363.         //
  2364.         BOOL fReplaceOpen = FALSE;
  2365.         if (_mode & MODE_HISTORY)
  2366.             fReplaceOpen = TRUE;
  2367.         else if (!fExpandable && (_mode & MODE_FAVORITES))
  2368.             fReplaceOpen = TRUE;
  2369.         UINT ilast = GetMenuItemCount(hmenu);
  2370.         for (UINT ipos = 0; ipos < ilast; ipos++)
  2371.         {
  2372.             UINT idCmd = GetMenuItemID(hmenu, ipos);
  2373.             if (idCmd >= idCmdStart && idCmd <= 0x7fff)
  2374.             {
  2375.                 TCHAR szVerb[40];
  2376.                 hres = ContextMenu_GetCommandStringVerb(pcm, idCmd-idCmdStart, szVerb, ARRAYSIZE(szVerb));
  2377.                 if (SUCCEEDED(hres))
  2378.                 {
  2379.                     if ((fReplaceOpen && 0 == StrCmpI(szVerb, TEXT("open"))) ||
  2380.                                          0 == StrCmpI(szVerb, TEXT("link")))
  2381.                         DeleteMenu(hmenu, idCmd, MF_BYCOMMAND );
  2382.                 }
  2383.             }
  2384.         }
  2385.         // Load the NSC part of the context menu and part on it separately.
  2386.         // By doing this, we save the trouble of having to do a SHPrettyMenu
  2387.         // after we dork it -- Shell_MergeMenus does all the prettying
  2388.         // automatically.
  2389.         HMENU hmenuctx = LoadMenuPopup_PrivateNoMungeW(POPUP_CONTEXT_NSC);
  2390.         if (hmenuctx)
  2391.         {
  2392.             // create new folder doesn't make sense outside of favorites
  2393.             // (actually, it does, but there's no interface to it)
  2394.             if (!(_mode & MODE_FAVORITES))
  2395.                 DeleteMenu(hmenuctx, RSVIDM_NEWFOLDER, MF_BYCOMMAND);
  2396.             //
  2397.             //  Of "Expand", "Collapse", or "Open", we will keep at most one of
  2398.             //  them.  idmKeep is the one we choose to keep.
  2399.             //
  2400.             UINT idmKeep;
  2401.             if (fExpandable)
  2402.             {
  2403.                 // Even if the item has no children, we still show Expand.
  2404.                 // The reason is that an item that has never been expanded
  2405.                 // is marked as "children: unknown" so we show an Expand
  2406.                 // and then the user picks it and nothing expands.  And then
  2407.                 // the user clicks it again and the Expand option is gone!
  2408.                 // (Because the second time, we know that the item isn't
  2409.                 // expandable.)
  2410.                 //
  2411.                 // Better to be consistently wrong than randomly wrong.
  2412.                 //
  2413.                 if (_IsItemExpanded(hti))
  2414.                     idmKeep = RSVIDM_COLLAPSE;
  2415.                 else
  2416.                     idmKeep = RSVIDM_EXPAND;
  2417.             }
  2418.             else if (!(_mode & MODE_CONTROL))
  2419.             {
  2420.                 idmKeep = RSVIDM_OPEN;
  2421.             }
  2422.             else
  2423.             {
  2424.                 idmKeep = 0;
  2425.             }
  2426.             //
  2427.             //  Now go decide which of RSVIDM_COLLAPSE, RSVIDM_EXPAND, or
  2428.             //  RSVIDM_OPEN we want to keep.
  2429.             //
  2430.             if (idmKeep != RSVIDM_EXPAND)
  2431.                 DeleteMenu(hmenuctx, RSVIDM_EXPAND,   MF_BYCOMMAND);
  2432.             if (idmKeep != RSVIDM_COLLAPSE)
  2433.                 DeleteMenu(hmenuctx, RSVIDM_COLLAPSE, MF_BYCOMMAND);
  2434.             if (idmKeep != RSVIDM_OPEN)
  2435.                 DeleteMenu(hmenuctx, RSVIDM_OPEN,     MF_BYCOMMAND);
  2436.             Shell_MergeMenus(hmenu, hmenuctx, 0, 0, 0xFFFF, fReplaceOpen ? 0 : MM_ADDSEPARATOR);
  2437.             DestroyMenu(hmenuctx);
  2438.             if (idmKeep)
  2439.                 SetMenuDefaultItem(hmenu, idmKeep, MF_BYCOMMAND);
  2440.         }
  2441.         _SHPrettyMenu(hmenu);
  2442.     }
  2443.     return hmenu;
  2444. }
  2445. LRESULT CNscTree::_OnContextMenu(short x, short y)
  2446. {
  2447.     HTREEITEM hti;
  2448.     POINT ptPopup;  // in screen coordinate
  2449.     //assert that the SetFocus() below won't be ripping focus away from anyone
  2450.     ASSERT((_mode & MODE_CONTROL) ? (GetFocus() == _hwndTree) : TRUE);
  2451.     if (x == -1 && y == -1)
  2452.     {
  2453.         // Keyboard-driven: Get the popup position from the selected item.
  2454.         hti = TreeView_GetSelection(_hwndTree);
  2455.         if (hti)
  2456.         {
  2457.             RECT rc;
  2458.             //
  2459.             // Note that TV_GetItemRect returns it in client coordinate!
  2460.             //
  2461.             TreeView_GetItemRect(_hwndTree, hti, &rc, TRUE);
  2462.             ptPopup.x = (rc.left + rc.right) / 2;
  2463.             ptPopup.y = (rc.top + rc.bottom) / 2;
  2464.             MapWindowPoints(_hwndTree, HWND_DESKTOP, &ptPopup, 1);
  2465.         }
  2466.         //so we can go into rename mode
  2467.         _fRclick = TRUE;
  2468.     }
  2469.     else
  2470.     {
  2471.         TV_HITTESTINFO tvht;
  2472.         // Mouse-driven: Pick the treeitem from the position.
  2473.         ptPopup.x = x;
  2474.         ptPopup.y = y;
  2475.         tvht.pt = ptPopup;
  2476.         ScreenToClient(_hwndTree, &tvht.pt);
  2477.         hti = TreeView_HitTest(_hwndTree, &tvht);
  2478.     }
  2479.     if (hti)
  2480.     {
  2481.         LPCITEMIDLIST pidl = _CacheParentShellFolder(hti, NULL);
  2482.         if (pidl)
  2483.         {
  2484.             IContextMenu *pcm;
  2485.             TreeView_SelectDropTarget(_hwndTree, hti);
  2486.             if (SUCCEEDED(_psfCache->GetUIObjectOf(_hwndTree, 1, &pidl, IID_IContextMenu, NULL, (void **)&pcm)))
  2487.             {
  2488.                 pcm->QueryInterface(IID_IContextMenu2, (void **)&_pcmSendTo);
  2489.                 HMENU hmenu = _CreateContextMenu(pcm, hti);
  2490.                 if (hmenu)
  2491.                 {
  2492.                     UINT idCmd;
  2493.                     _pcm = pcm; // for IContextMenu2 code
  2494.                     // use _hwnd so menu msgs go there and I can forward them
  2495.                     // using IContextMenu2 so "Sent To" works
  2496.                     // review chrisny:  useTrackPopupMenuEx for clipping etc.  
  2497.                     idCmd = TrackPopupMenu(hmenu,
  2498.                         TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
  2499.                         ptPopup.x, ptPopup.y, 0, _hwndTree, NULL);
  2500.                     // Note:  must requery selected item to verify that the hti is good.  This
  2501.                     // solves the problem where the hti was deleted, hence pointed to something
  2502.                     // bogus, then we write to it causing heap corruption, while the menu was up.  
  2503.                     TV_HITTESTINFO tvht;
  2504.                     tvht.pt = ptPopup;
  2505.                     ScreenToClient(_hwndTree, &tvht.pt);