source: trunk/EraserUI/FileTreeCtrl.cpp @ 57

Revision 57, 101.2 KB checked in by lowjoel, 7 years ago (diff)

Warning fixes for everyone.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2Module : FileTreeCtrl.cpp
3Purpose: Implementation for an MFC class which provides a tree control similiar
4         to the left hand side of explorer
5Created: PJN / 25-12-1999
6History: PJN / 11-01-2000 1. Added some asserts to HasGotSubEntries
7                          2. Fixed a problem with calling OnDblclk when no tree item is selected
8                          3. Removed an unused variable from SetSelectedPath
9         PJN / 25-01-2000 1. Minor update to the code in CTreeFileCtrl::OnDblclk to only allow
10                          selected items to be opened.
11         PJN / 31-01-2000 1. Fixed a problem when you right mouse click over a non - selected item.
12                          The control now implements the same behavior as Explorer for this.
13                          2. Removed check for keyboard invocation of the context menu in OnContextMenu
14                          3. Now displays the context menu over the selected item when invoked via
15                          the keyboard. Again this is the same bahavior as Explorer has.
16                          4. Optimized the code in PreTranslateMessage
17                          5. Fixed a bug in CTreeFileCtrl::OnEndlabeledit
18         PJN / 02-04-2000 1. Fixed a small bug in CTreeFileCtrl::SetRootFolder
19                          2. Fixed the problem with initialisation errors in the code. Client code must not
20                          explicitly call PopulateTree when the window is created. When used in a dialog
21                          resource this is not necessary as it is called for you in the DDX functions.
22         PJN / 13-05-2000 1. Fixed a problem where items on the context menu were being invoked for the
23                          wrong item when you right mouse click over an item that is not the selected item.
24                          Behaviour now is that the item is selected prior to showing the context menu.
25                          Now, this is same behaviour as Explorer.
26         PJN / 12-07-2000 1. Now uses ON_NOTIFY_REFLECT_EX instead of ON_NOTIFY_REFLECT for handling reflected
27                          messages. This allows derived classes to handle these messages also. Thanks to
28                          Christian Dahl for this.
29                          2. Sample app now allows drag drop behaviour to be toggled
30                          3. Fixed a problem whereby two items were left selected after you did a drap /
31                          drop operation. Thanks to Jonathon Ralston for this.
32                          4. Removed function declaration for unused function "InsertDriveItem".
33                          5. Removed an unreferenced variable in InsertFileItem.
34                          6. Tidied up the UpOneLevel functions and made it public.
35                          7. Removed all the message handlers in the sample code which reflect straight
36                          down to the tree control. Instead the OnCmdMsg first routes the message to this
37                          class.
38                          8. Renamed all menu items which CTreeFileCtrl uses to include the prefix TREEFILECTRL
39                          9. Renamed all the methods to more generic names
40                          10. PreTranslateMessage now uses PostMessage instead of calling functions directly.
41                          This allows up to function correctly for derived classes in addition to correctly
42                          disabling items through the OnUpdate mechanism
43                          11. Removed an unreferrenced variable in OnRclick
44                          12. Removed the unreferrenced variable m_hSelItem
45                          13. Optimized a number of expressions by putting the boolean comparisons first
46                          14. m_bAllowRename parameter is now observed for in place editing of an item
47                          15. Now supports hiding of Drive types via the SetDriveHideFlags function. See the
48                          menu options on the Tools menu in the sample program for its usage.
49                          16. Filename masks can now be specifed via the SetFileNameMask method. See the
50                          menu options on the Tools menu in the sample program for its usage.
51                          17. File types can now be specified via the GetFileHideFlags function. See the
52                          menu options on the Tools menu in the sample program for its usage.
53                          18. Fixed a small issue in one of my calls to ::GetKeyState
54                          19. Fixed a bug where programs was crashing if an icon index for it could not
55                          be found.
56                          20. Made many of the methods of CTreeFileCtrl virtual, thus allowable better
57                          use in end user derived classes.
58                          21. Fixed problem where SetSelectedPath(_T("C:\\"), FALSE) was resulting
59                          in the drive being expanded even through FALSE was being sent in to specify
60                          that the item should not be expanded.
61                          22. A virtual "CanDisplayFile" has been added to allow you to decide at runtime
62                          whether or not a certain file is to be displayed.
63                          23. A virtual "CanDisplayFolder" has been added to allow you to decide at
64                          runtime whether or not a certain folder is to be displayed
65                          24. Now optionally displays compressed files in a different color, similiar to
66                          explorer. The color is customizable through the class API.
67                          25. Code has been made smarter so that it does not have to spin up the floppy
68                          disk to determine if there are files on it. It now initially displays a "+"
69                          and only when you try to expand it will it do the actual scan.
70         PJN / 23-07-2000 1. Fixed a bug where the expansion state of the selected item was not being
71                          preserved across refreshes.
72                          2. Now includes full support for Next / Prev support similiar to Windows
73                          Explorer with the Desktop Update.
74                          3. Updated sample app to have some useful toolbars.
75                          4. Changing any tree settings which can affect its appearance now force
76                          a refresh of its contents.
77                          5. ItemToPath method has been made const.
78                          6. Addition of PathToItem method
79                          7. Auto refresh of items is now provided for by means of change notification
80                          threads. This is configurable via the SetAutoRefresh method.
81                          8. The root folder of the tree control can now be changed from the sample app
82                          9. Fixed a bug in SetRootFolder when passed an empty folder
83                          10. Fixed a bug where the system image list was not being initialized correctly
84                          if the user did not have a "C:\\" drive. This could occur on NT/Windows2000
85                          11. Fixed a bug in IsFile and IsFolder which was causing invalid files or folders
86                          to appear valid.
87                          12. Deleted items are now removed upon expansion. Also if the item being expanded was
88                          deleted and it was the only child, then its parent has the "-" expansion button removed
89                          13. Removable drive nodes are collapsed back to their root nodes if their media is
90                          changed in the intervening time when a node expansion occurs.
91                          14. Wait cursor is now displayed while a refresh is taking place.
92                          15. A "OnSelectionChanged" virtual function has now been provided
93                          16. Sample app's icon has been made the same as Explorers.
94                          17. Sample app now displays the path name of the currently selected item in the tree control.
95                          18. Fixed a bug in IsCompressed
96                          19. items are now deleted when selected if they do not exist.
97         PJN / 05-09-2000 1. Fixed a bug in CTreeFileCtrl::IsFile and CTreeFileCtrl::IsFolder
98         PJN / 20-09-2000 1. Control now includes DECLARE_DYNCREATE thereby allowing it to be used
99                          in code which requires this such as Stingray's SEC3DTabWnd. Thanks to Petter Nilsen for
100                          pointing out this omission
101         PJN / 02-10-2000 1. Fixed a stack overwrite problem in CSystemImageList::CSystemImageList
102                          2. Removed an unreferrenced variable in CTreeFileCtrl::OnSelChanged
103                          3. Removed an unreferrenced variable in CTreeFileCtrl::OnItemExpanding
104                          4. Changed the SendMessage in CTreeFileCtrl::OnDblClk to prevent a crash
105                          which was occurring when the open call sometimes caused a refresh call
106                          which changed the tree items at times. When the double click message handler
107                          continued it then caused item expand notifications for items already deleted
108                          and of course crashes.
109                          5. Removed an unreferrenced variable in CTreeFileCtrl::EndDragging
110                          6. Removed an unreferrenced variable in CTreeFileCtrl::KillNotificationThread
111                          7. Sample app now remembers the selected path and its expansion state across
112                          invocations.
113         PJN / 05-05-2001 1. Updated copright message.
114                          2. Fixed a resource leak where icon resources were not being released. Thanks to Jay Kohler for
115                          spotting this problem
116         PJN / 05-08-2001 1. You can now optionally display Network Neighborhood
117                          2. You can now turn on / off display of file extensions.
118                          3. You can now display shared items with a different icon
119                          4. Friendly names can now be displayed for drives.
120         PJN / 11-08-2001 1. Improved checking to see if action is allowed in Rename and Delete
121                          2. Fixed a bug in OnBeginLabelEdit
122                          3. Fixed a problem in OnEndLabelEdit which was causing renames to fail when filename extensions
123                          were not being shown.
124         PJN / 11-08-2001 1. Fixed a bug in OnSelChanged which was causing a crash when you right click on an empty area of the control.
125                          Thanks to Eli Fenton for spotting this one.
126                          2. The control now by default shows drives as children of "My Computer" just like in Explorer.
127                          3. When you display a rooted directory in the control, you now have the option of displaying the root
128                          folder in the control as the root item.  Thanks to Eli Fenton for suggesting this.
129         PJN / 26-10-2001 1. Fixed some stability problems with the code. This was due to adding items to the system image list.
130                          This is normally a very bad thing. Instead now the code uses TreeView custom draw (just like the blue color
131                          for compresed items) to draw the icons for the Network Neighborhood items. Thanks to Darken Screamer and
132                          Timo Haberkern for spotting this problem.
133         PJN / 24-12-2001 1. Fixed a copy and paste bug in GoForward. Thanks to Michael T. Luongo for this fix.
134                          2. Now allows encrypted files to be displayed in a different color
135                          3. Fixed memory leak which was occuring when control was being used in a dialog
136                          4. Fixed a problem with the calculation of idents when the style "TVS_LINESATROOT" is used.
137         PJN / 16-02-2002 1. Updated copyright message
138                          2. Fixed a drag/drop problem which caused the tree state to be inconsistent after the file was dropped.
139                          3. Fixed a bug in the refresh code which was causing it to not reselect the selected node
140                          after the refresh occurs. Thanks to John Noël for this fix.
141                          4. Fixed a problem where the custom draw icons for network nodes were not being drawn in the correct
142                          positions when scrollbars were present in the control. Again thanks to John Noël for this fix.
143                          5. Fixed a bug in SetSelectedPath which would not display the correct selection if the node we
144                          want to select has been deleted due to the node becoming deleted when it was previously collapsed.
145                          Thanks to Franz Fackelmann and John Noël for spotting this problem.
146         PJN / 05-06-2002 1. Implemented function "SetUsingDifferentColorForEncrypted" which was declared but had no
147                          implementation.
148                          2. Fixed report of uninitialized member variable "m_nTimerID". Thanks to Emil Isberg for spoting this.
149         PJN / 07-08-2002 1. Fixed a bug in the sample app which ships with the class which was occuring when you compiled
150                          the code in Visual Studio.Net. This was due to MS changing the location oleimpl2.h header file. Thanks
151                          to Darren Schroeder for spotting this problem.
152         PJN / 22-09-2002 1. Removed a number of unreferrenced variables from the code, as highlighted by Visual Studio.Net. Thanks
153                          to Bill Johnson for spotting this.
154
155
156
157
158Copyright (c) 1999 - 2002 by PJ Naughter.  (Web: www.naughter.com, Email: pjna@naughter.com)
159
160All rights reserved.
161
162Copyright / Usage Details:
163
164You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise)
165when your product is released in binary form. You are allowed to modify the source code in any way you want
166except you cannot modify the copyright details at the top of each module. If you want to distribute source
167code with your application, then you are only allowed to distribute versions released by the author. This is
168to maintain a single distribution point for the source code.
169
170*/
171
172/////////////////////////////////  Includes  //////////////////////////////////
173#include "stdafx.h"
174#include "resource.h"
175#include "FileTreeCtrl.h"
176#include <afxpriv.h>
177#include "Shared/SortedArray.h"
178
179
180
181
182//////////////////////////////// Defines / Locals /////////////////////////////
183
184#ifdef _DEBUG
185#define new DEBUG_NEW
186#undef THIS_FILE
187static char THIS_FILE[] = __FILE__;
188#endif
189
190#define WM_TREEUPDATE_CHANGE WM_USER
191
192//To avoid having to have the latest Platform SDK installed to compile CTreeFileCtrl
193#ifndef FILE_ATTRIBUTE_ENCRYPTED
194#define FILE_ATTRIBUTE_ENCRYPTED            0x00004000 
195#endif
196
197int CSystemImageList::sm_nRefCount = 0; //Initialise the reference count
198CSystemImageList theSystemImageList;    //The one and only system image list instance
199CShareEnumerator theSharedEnumerator;   //The one and only share enumerator
200
201//Pull in the WNet Lib automatically
202#pragma comment(lib, "mpr.lib")
203
204
205
206////////////////////////////// Implementation /////////////////////////////////
207
208CTreeFileCtrlItemInfo::CTreeFileCtrlItemInfo()
209{
210  m_pNetResource = NULL;
211  m_bNetworkNode = FALSE;
212  m_bExtensionHidden = FALSE;
213}
214
215CTreeFileCtrlItemInfo::CTreeFileCtrlItemInfo(const CTreeFileCtrlItemInfo& ItemInfo)
216{
217  m_sFQPath       = ItemInfo.m_sFQPath;
218  m_sRelativePath = ItemInfo.m_sRelativePath;
219  m_bNetworkNode  = ItemInfo.m_bNetworkNode;
220  m_pNetResource = new NETRESOURCE;
221  if (ItemInfo.m_pNetResource)
222  {
223    //Copy the direct member variables of NETRESOURCE
224    CopyMemory(m_pNetResource, ItemInfo.m_pNetResource, sizeof(NETRESOURCE)); 
225
226    //Duplicate the strings which are stored in NETRESOURCE as pointers
227    if (ItemInfo.m_pNetResource->lpLocalName)
228          m_pNetResource->lpLocalName   = _tcsdup(ItemInfo.m_pNetResource->lpLocalName);
229    if (ItemInfo.m_pNetResource->lpRemoteName)
230          m_pNetResource->lpRemoteName = _tcsdup(ItemInfo.m_pNetResource->lpRemoteName);
231    if (ItemInfo.m_pNetResource->lpComment)
232          m_pNetResource->lpComment = _tcsdup(ItemInfo.m_pNetResource->lpComment);
233    if (ItemInfo.m_pNetResource->lpProvider)
234          m_pNetResource->lpProvider    = _tcsdup(ItemInfo.m_pNetResource->lpProvider);
235  }
236  else
237    ZeroMemory(m_pNetResource, sizeof(NETRESOURCE)); 
238}
239
240CTreeFileCtrlItemInfo::~CTreeFileCtrlItemInfo()
241{
242}
243
244
245CSystemImageList::CSystemImageList()
246{
247  ASSERT(sm_nRefCount == 0); //Should only every be one instance of CSystemImageList declared
248  ++sm_nRefCount;
249
250  //Get the temp directory. This is used to then bring back the system image list
251  TCHAR pszTempDir[_MAX_PATH];
252  VERIFY(GetTempPath(_MAX_PATH, pszTempDir));
253  TCHAR pszDrive[_MAX_DRIVE + 1];
254  _tsplitpath(pszTempDir, pszDrive, NULL, NULL, NULL);
255  int nLen = _tcslen(pszDrive);
256  if (pszDrive[nLen-1] != _T('\\'))
257    _tcscat(pszDrive, _T("\\"));
258
259  //Attach to the system image list
260  SHFILEINFO sfi;
261  HIMAGELIST hSystemImageList = (HIMAGELIST) SHGetFileInfo(pszTempDir, 0, &sfi, sizeof(SHFILEINFO),
262                                                           SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
263  VERIFY(m_ImageList.Attach(hSystemImageList));
264}
265
266CSystemImageList::~CSystemImageList()
267{
268  //Decrement the reference count
269  --sm_nRefCount;
270
271  //Detach from the image list to prevent problems on 95/98 where
272  //the system image list is shared across processes
273  m_ImageList.Detach();
274}
275
276CShareEnumerator::CShareEnumerator()
277{
278  //Set out member variables to defaults
279  m_pNTShareEnum = NULL;
280  m_pWin9xShareEnum = NULL;
281  m_pNTBufferFree = NULL;
282  m_pNTShareInfo = NULL;
283  m_pWin9xShareInfo = NULL;
284  m_pWin9xShareInfo = NULL;
285  m_hNetApi = NULL;
286  m_dwShares = 0;
287
288  //Determine if we are running Windows NT or Win9x
289  OSVERSIONINFO osvi;
290  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
291  m_bWinNT = (GetVersionEx(&osvi) && osvi.dwPlatformId == VER_PLATFORM_WIN32_NT);
292  if (m_bWinNT)
293  {
294    //Load up the NETAPI dll
295    m_hNetApi = LoadLibrary(_T("NETAPI32.dll"));
296    if (m_hNetApi)
297    {
298      //Get the required function pointers
299      m_pNTShareEnum = (NT_NETSHAREENUM*) GetProcAddress(m_hNetApi, "NetShareEnum");
300      m_pNTBufferFree = (NT_NETAPIBUFFERFREE*) GetProcAddress(m_hNetApi, "NetApiBufferFree");
301    }
302  }
303  else
304  {
305    //Load up the NETAPI dll
306    m_hNetApi = LoadLibrary(_T("SVRAPI.dll"));
307    if (m_hNetApi)
308    {
309      //Get the required function pointer
310      m_pWin9xShareEnum = (WIN9X_NETSHAREENUM*) GetProcAddress(m_hNetApi, "NetShareEnum");
311    }
312  }
313
314  //Update the array of shares we know about
315  Refresh();
316}
317
318CShareEnumerator::~CShareEnumerator()
319{
320  if (m_bWinNT)
321  {
322    //Free the buffer if valid
323    if (m_pNTShareInfo)
324      m_pNTBufferFree(m_pNTShareInfo);
325  }
326  else
327    //Free up the heap memory we have used
328        delete [] m_pWin9xShareInfo;     
329
330  //Free the dll now that we are finished with it
331  if (m_hNetApi)
332  {
333    FreeLibrary(m_hNetApi);
334    m_hNetApi = NULL;
335  }
336}
337
338void CShareEnumerator::Refresh()
339{
340  m_dwShares = 0;
341  if (m_bWinNT)
342  {
343    //Free the buffer if valid
344    if (m_pNTShareInfo)
345      m_pNTBufferFree(m_pNTShareInfo);
346
347    //Call the function to enumerate the shares
348    if (m_pNTShareEnum)
349    {
350      DWORD dwEntriesRead = 0;
351      m_pNTShareEnum(NULL, 502, (LPBYTE*) &m_pNTShareInfo, MAX_PREFERRED_LENGTH, &dwEntriesRead, &m_dwShares, NULL);
352    }
353  }
354  else
355  {
356    //Free the buffer if valid
357    if (m_pWin9xShareInfo)
358      delete [] m_pWin9xShareInfo;
359
360    //Call the function to enumerate the shares
361    if (m_pWin9xShareEnum)
362    {
363      //Start with a reasonably sized buffer
364      unsigned short cbBuffer = 1024;
365      BOOL bNeedMoreMemory = TRUE;
366      BOOL bSuccess = FALSE;
367      while (bNeedMoreMemory && !bSuccess)
368      {
369        unsigned short nTotalRead = 0;
370        m_pWin9xShareInfo = (CTreeFile_share_info_50*) new BYTE[cbBuffer];
371        ZeroMemory(m_pWin9xShareInfo, cbBuffer);
372        unsigned short nShares = 0;
373        NET_API_STATUS nStatus = m_pWin9xShareEnum(NULL, 50, (char FAR *)m_pWin9xShareInfo, cbBuffer, (unsigned short FAR *)&nShares, (unsigned short FAR *)&nTotalRead);
374        if (nStatus == ERROR_MORE_DATA)
375            {           
376          //Free up the heap memory we have used
377              delete [] m_pWin9xShareInfo;     
378
379          //And double the size, ready for the next loop around
380          cbBuffer *= 2;
381            }
382        else if (nStatus == NERR_Success)
383        {
384          m_dwShares = nShares;
385          bSuccess = TRUE;
386        }
387        else
388          bNeedMoreMemory = FALSE;
389      }
390    }
391  }
392}
393
394BOOL CShareEnumerator::IsShared(const CString& sPath)
395{
396  //Assume the item is not shared
397  BOOL bShared = FALSE;
398
399  if (m_bWinNT)
400  {
401    if (m_pNTShareInfo)
402    {
403      for (DWORD i=0; i<m_dwShares && !bShared; i++)
404      {
405        CString sShare((LPWSTR) m_pNTShareInfo[i].shi502_path);
406        bShared = (sPath.CompareNoCase(sShare) == 0) && ((m_pNTShareInfo[i].shi502_type == STYPE_DISKTREE) || ((m_pNTShareInfo[i].shi502_type == STYPE_PRINTQ)));
407      }
408    }
409  } 
410  else
411  {
412    if (m_pWin9xShareInfo)
413    {
414      for (DWORD i=0; i<m_dwShares && !bShared; i++)
415      {
416        CString sShare(m_pWin9xShareInfo[i].shi50_path);
417        bShared = (sPath.CompareNoCase(sShare) == 0) && 
418                   ((m_pWin9xShareInfo[i].shi50_type == STYPE_DISKTREE) || ((m_pWin9xShareInfo[i].shi50_type == STYPE_PRINTQ)));
419      }
420    }
421  }
422
423  return bShared;
424}
425
426
427
428
429
430
431
432CTreeFileCtrlThreadInfo::CTreeFileCtrlThreadInfo() : m_TerminateEvent(FALSE, TRUE)
433{
434  m_pThread = NULL;
435  m_pTree   = NULL;
436  m_nIndex  = -1;
437}
438
439CTreeFileCtrlThreadInfo::~CTreeFileCtrlThreadInfo()
440{
441  delete m_pThread;
442}
443
444
445
446
447IMPLEMENT_DYNCREATE(CTreeFileCtrl, CTreeCtrl)
448
449BEGIN_MESSAGE_MAP(CTreeFileCtrl, CTreeCtrl)
450    //{{AFX_MSG_MAP(CTreeFileCtrl)
451    ON_COMMAND(ID_TREEFILECTRL_PROPERTIES, OnProperties)
452    ON_UPDATE_COMMAND_UI(ID_TREEFILECTRL_PROPERTIES, OnUpdateProperties)
453    ON_COMMAND(ID_TREEFILECTRL_RENAME, OnRename)
454    ON_UPDATE_COMMAND_UI(ID_TREEFILECTRL_RENAME, OnUpdateRename)
455    ON_COMMAND(ID_TREEFILECTRL_OPEN, OnOpen)
456    ON_UPDATE_COMMAND_UI(ID_TREEFILECTRL_OPEN, OnUpdateOpen)
457    ON_COMMAND(ID_TREEFILECTRL_DELETE, OnDelete)
458    ON_UPDATE_COMMAND_UI(ID_TREEFILECTRL_DELETE, OnUpdateDelete)
459    ON_COMMAND(ID_TREEFILECTRL_REFRESH, OnRefresh)
460    ON_COMMAND(ID_TREEFILECTRL_UPONELEVEL, OnUpOneLevel)
461    ON_UPDATE_COMMAND_UI(ID_TREEFILECTRL_UPONELEVEL, OnUpdateUpOneLevel)
462    ON_WM_CONTEXTMENU()
463    ON_WM_INITMENUPOPUP()
464    ON_WM_MOUSEMOVE()
465    ON_WM_LBUTTONUP()
466    ON_WM_TIMER()
467    ON_COMMAND(ID_TREEFILECTRL_BACK, OnBack)
468    ON_UPDATE_COMMAND_UI(ID_TREEFILECTRL_BACK, OnUpdateBack)
469    ON_COMMAND(ID_TREEFILECTRL_FORWARD, OnForward)
470    ON_UPDATE_COMMAND_UI(ID_TREEFILECTRL_FORWARD, OnUpdateForward)
471    ON_WM_DESTROY()
472    //}}AFX_MSG_MAP
473    ON_WM_CONTEXTMENU()
474    ON_NOTIFY_REFLECT_EX(NM_DBLCLK, OnDblclk)
475    ON_NOTIFY_REFLECT_EX(TVN_ITEMEXPANDING, OnItemExpanding)
476    ON_NOTIFY_REFLECT_EX(TVN_BEGINLABELEDIT, OnBeginLabelEdit)
477    ON_NOTIFY_REFLECT_EX(TVN_ENDLABELEDIT, OnEndLabelEdit)
478    ON_NOTIFY_REFLECT_EX(NM_RCLICK, OnRclick)
479    ON_NOTIFY_REFLECT_EX(TVN_BEGINDRAG, OnBeginDrag)
480    ON_NOTIFY_REFLECT_EX(NM_CUSTOMDRAW, OnCustomDraw)
481    ON_NOTIFY_REFLECT_EX(TVN_SELCHANGED, OnSelChanged)
482    ON_NOTIFY_REFLECT_EX(TVN_DELETEITEM, OnDeleteItem)
483  ON_MESSAGE(WM_TREEUPDATE_CHANGE, OnChange)
484END_MESSAGE_MAP()
485
486CTreeFileCtrl::CTreeFileCtrl() : CTreeCtrl()
487{
488  m_sFileNameMask = _T("*.*");
489  m_dwFileHideFlags = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | 
490                      FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_TEMPORARY;
491  m_bShowCompressedUsingDifferentColor = TRUE;
492  m_rgbCompressed = RGB(0, 0, 255);
493  m_bShowEncryptedUsingDifferentColor = TRUE;
494  m_rgbEncrypted = RGB(255, 0, 0);
495  m_dwDriveHideFlags = 0;
496  m_bShowFiles = TRUE;
497  m_pilDrag = NULL;
498  m_hItemDrag = NULL;
499  m_hItemDrop = NULL;
500  m_hNetworkRoot = NULL;
501  m_TimerTicks = 0;
502  m_bAllowDragDrop = FALSE;
503  m_bAllowRename = FALSE;
504  m_bAllowOpen = TRUE;
505  m_bAllowProperties = TRUE;
506  m_bAllowDelete = FALSE;
507  m_nMaxHistory = 20;
508  m_bUpdatingHistorySelection = FALSE;
509  m_bAutoRefresh = TRUE;
510  m_bShowSharedUsingDifferentIcon = TRUE;
511  m_nTimerID = 0;
512  for (int i=0; i<26; i++)
513    m_dwMediaID[i] = 0xFFFFFFFF;
514
515  m_pMalloc = NULL;
516  m_pShellFolder = NULL;
517  m_bDisplayNetwork = TRUE;
518  m_FileExtensions = UseTheShellSetting;
519  m_dwNetworkItemTypes = RESOURCETYPE_ANY;
520  m_hMyComputerRoot = NULL;
521  m_bShowMyComputer = TRUE;
522  m_bShowRootedFolder = FALSE;
523  m_hRootedFolder = NULL;
524  m_bShowDriveLabels = TRUE;
525}
526
527CTreeFileCtrl::~CTreeFileCtrl()
528{
529}
530
531int CTreeFileCtrl::CompareByFilenameNoCase(CString& element1, CString& element2) 
532{
533  return element1.CompareNoCase(element2);
534}
535
536#ifdef _DEBUG
537void CTreeFileCtrl::AssertValid() const
538{
539    CTreeCtrl::AssertValid();
540}
541
542void CTreeFileCtrl::Dump(CDumpContext& dc) const
543{
544    CTreeCtrl::Dump(dc);
545}
546#endif //_DEBUG
547
548void CTreeFileCtrl::SetShowFiles(BOOL bFiles) 
549{ 
550  m_bShowFiles = bFiles; 
551
552  //Force a refresh
553  Refresh();
554}
555
556void CTreeFileCtrl::SetRootFolder(const CString& sPath)
557{
558  m_sRootFolder = sPath;
559
560  //Ensure it is terminated with a "\"
561  int nLength = m_sRootFolder.GetLength();
562  if (nLength && m_sRootFolder.GetAt(nLength-1) != _T('\\'))
563    m_sRootFolder += _T('\\');
564
565  //Force a refresh
566  Refresh();
567}
568
569int CTreeFileCtrl::GetIconIndex(HTREEITEM hItem)
570{
571  TV_ITEM tvi;
572  ZeroMemory(&tvi, sizeof(TV_ITEM));
573  tvi.mask = TVIF_IMAGE;
574  tvi.hItem = hItem;
575  if (GetItem(&tvi))
576    return tvi.iImage;
577  else
578    return -1;
579}
580
581int CTreeFileCtrl::GetIconIndex(const CString& sFilename)
582{
583  //Retreive the icon index for a specified file/folder
584  SHFILEINFO sfi;
585  ZeroMemory(&sfi, sizeof(SHFILEINFO));
586  SHGetFileInfo(sFilename, 0, &sfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
587  return sfi.iIcon; 
588}
589
590int CTreeFileCtrl::GetSelIconIndex(const CString& sFilename)
591{
592  //Retreive the icon index for a specified file/folder
593  SHFILEINFO sfi;
594  ZeroMemory(&sfi, sizeof(SHFILEINFO));
595  SHGetFileInfo(sFilename, 0, &sfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_OPENICON | SHGFI_SMALLICON);
596  return sfi.iIcon; 
597}
598
599int CTreeFileCtrl::GetIconIndex(LPITEMIDLIST lpPIDL)
600{
601  SHFILEINFO sfi;
602  ZeroMemory(&sfi, sizeof(SHFILEINFO));
603  SHGetFileInfo((LPCTSTR)lpPIDL, 0, &sfi, sizeof(sfi), SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_LINKOVERLAY);
604  return sfi.iIcon; 
605}
606
607int CTreeFileCtrl::GetSelIconIndex(LPITEMIDLIST lpPIDL)
608{
609  SHFILEINFO sfi;
610  ZeroMemory(&sfi, sizeof(SHFILEINFO));
611  SHGetFileInfo((LPCTSTR)lpPIDL, 0, &sfi, sizeof(sfi), SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_OPENICON);
612  return sfi.iIcon; 
613}
614
615int CTreeFileCtrl::GetSelIconIndex(HTREEITEM hItem)
616{
617  TV_ITEM tvi;
618  ZeroMemory(&tvi, sizeof(TV_ITEM));
619  tvi.mask = TVIF_SELECTEDIMAGE;
620  tvi.hItem = hItem;
621  if (GetItem(&tvi))
622    return tvi.iSelectedImage;
623  else
624    return -1;
625}
626
627HTREEITEM CTreeFileCtrl::FindSibling(HTREEITEM hParent, const CString& sItem) const
628{
629  HTREEITEM hChild = GetChildItem(hParent);
630  while (hChild)
631  {
632    CTreeFileCtrlItemInfo* pItem = (CTreeFileCtrlItemInfo*) GetItemData(hChild);
633    ASSERT(pItem);
634    if (pItem->m_sRelativePath.CompareNoCase(sItem) == 0)
635      return hChild;
636    hChild = GetNextItem(hChild, TVGN_NEXT);
637  }
638  return NULL;
639}
640
641CString CTreeFileCtrl::GetSelectedPath()
642{
643  HTREEITEM hItem = GetSelectedItem();
644  if (hItem)
645    return ItemToPath(hItem);
646  else
647    return CString();
648}
649
650HTREEITEM CTreeFileCtrl::FindServersNode(HTREEITEM hFindFrom) const
651{
652  if (m_bDisplayNetwork)
653  {
654    //Try to find some "servers" in the child items of hFindFrom
655    HTREEITEM hChild = GetChildItem(hFindFrom);
656    while (hChild)
657    {
658      CTreeFileCtrlItemInfo* pItem = (CTreeFileCtrlItemInfo*) GetItemData(hChild);
659      ASSERT(pItem);
660      if (pItem->m_pNetResource)
661      {
662        //Found a share
663        if (pItem->m_pNetResource->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER)
664          return hFindFrom;
665      }
666 
667      //Get the next sibling for the next loop around
668      hChild = GetNextSiblingItem(hChild);
669    }
670
671    //Ok, since we got here, we did not find any servers in any of the child nodes of this
672    //item. In this case we need to call ourselves recursively to find one
673    hChild = GetChildItem(hFindFrom);
674    while (hChild)
675    {
676      HTREEITEM hFound = FindServersNode(hChild);
677      if (hFound)
678        return hFound;
679       
680      //Get the next sibling for the next loop around
681      hChild = GetNextSiblingItem(hChild);
682    }
683  }
684
685  //If we got as far as here then no servers were found.
686  return NULL;
687}
688
689void CTreeFileCtrl::SetHasPlusButton(HTREEITEM hItem, BOOL bHavePlus)
690{
691  //Remove all the child items from the parent
692  TV_ITEM tvItem;
693  tvItem.hItem = hItem;
694  tvItem.mask = TVIF_CHILDREN; 
695  tvItem.cChildren = bHavePlus;
696  SetItem(&tvItem);
697}
698
699BOOL CTreeFileCtrl::HasPlusButton(HTREEITEM hItem)
700{
701  TVITEM tvItem;
702  tvItem.hItem = hItem;
703  tvItem.mask = TVIF_HANDLE | TVIF_CHILDREN;
704  return GetItem(&tvItem) && (tvItem.cChildren != 0);
705}
706
707HTREEITEM CTreeFileCtrl::SetSelectedPath(const CString& sPath, BOOL bExpanded)
708{
709  CString sSearch(sPath);
710  int nSearchLength = sSearch.GetLength();
711  if (nSearchLength == 0)
712  {
713    TRACE(_T("Cannot select a empty path\n"));
714    return NULL;
715  }
716
717  //Remove trailing "\" from the path
718  if (nSearchLength > 3 && sSearch.GetAt(nSearchLength-1) == _T('\\'))
719    sSearch = sSearch.Left(nSearchLength-1);
720 
721  //Remove initial part of path if the root folder is setup
722  int nRootLength = m_sRootFolder.GetLength();
723  if (nRootLength)
724  {
725    if (sSearch.Find(m_sRootFolder) != 0)
726    {
727      TRACE(_T("Could not select the path %s as the root has been configued as %s\n"), sPath, m_sRootFolder);
728      return NULL;
729    }
730    sSearch = sSearch.Right(sSearch.GetLength() - nRootLength);
731  }
732
733  if (sSearch.IsEmpty())
734    return NULL;
735
736  SetRedraw(FALSE);
737
738  HTREEITEM hItemFound = TVI_ROOT;
739  if (nRootLength && m_hRootedFolder)
740    hItemFound = m_hRootedFolder;
741  BOOL bDriveMatch = m_sRootFolder.IsEmpty();
742  BOOL bNetworkMatch = m_bDisplayNetwork && ((sSearch.GetLength() > 2) && sSearch.Find(_T("\\\\")) == 0);
743  if (bNetworkMatch)
744  {
745    bDriveMatch = FALSE;
746
747    //Working here
748    BOOL bHasPlus = HasPlusButton(m_hNetworkRoot);
749    BOOL bHasChildren = (GetChildItem(m_hNetworkRoot) != NULL);
750
751    if (bHasPlus && !bHasChildren)
752      DoExpand(m_hNetworkRoot);
753    else
754      Expand(m_hNetworkRoot, TVE_EXPAND);
755    hItemFound = FindServersNode(m_hNetworkRoot);
756    sSearch = sSearch.Right(sSearch.GetLength() - 2);
757  }
758  if (bDriveMatch)
759  {
760    if (m_hMyComputerRoot)
761    {
762      //Working here
763      BOOL bHasPlus = HasPlusButton(m_hMyComputerRoot);
764      BOOL bHasChildren = (GetChildItem(m_hMyComputerRoot) != NULL);
765
766      if (bHasPlus && !bHasChildren)
767        DoExpand(m_hMyComputerRoot);
768      else
769        Expand(m_hMyComputerRoot, TVE_EXPAND);
770      hItemFound = m_hMyComputerRoot;
771    }
772  }
773
774  int nFound = sSearch.Find(_T('\\'));
775  while (nFound != -1)
776  {
777    CString sMatch;
778    if (bDriveMatch)
779    {
780      sMatch = sSearch.Left(nFound + 1);
781      bDriveMatch = FALSE;
782    }
783    else
784      sMatch = sSearch.Left(nFound);
785   
786    hItemFound = FindSibling(hItemFound, sMatch);
787    if (hItemFound == NULL)
788      break;
789    else if (!IsDrive(sPath))
790    {
791      SelectItem(hItemFound);
792
793      //Working here
794      BOOL bHasPlus = HasPlusButton(hItemFound);
795      BOOL bHasChildren = (GetChildItem(hItemFound) != NULL);
796
797      if (bHasPlus && !bHasChildren)
798        DoExpand(hItemFound);
799      else
800        Expand(hItemFound, TVE_EXPAND);
801    }
802
803    sSearch = sSearch.Right(sSearch.GetLength() - nFound - 1);
804    nFound = sSearch.Find(_T('\\'));
805  };
806
807  //The last item
808  if (hItemFound)
809  {
810    if (sSearch.GetLength())
811      hItemFound = FindSibling(hItemFound, sSearch);
812    if (hItemFound)
813      SelectItem(hItemFound);
814
815    if (bExpanded)
816    {
817      //Working here
818      BOOL bHasPlus = HasPlusButton(hItemFound);
819      BOOL bHasChildren = (GetChildItem(hItemFound) != NULL);
820
821      if (bHasPlus && !bHasChildren)
822        DoExpand(hItemFound);
823      else
824        Expand(hItemFound, TVE_EXPAND);
825    }
826  }
827
828  //Turn back on the redraw flag
829  SetRedraw(TRUE);
830
831  return hItemFound;
832}
833
834BOOL CTreeFileCtrl::Rename(HTREEITEM hItem)
835{
836  if (hItem)
837  {
838    CTreeFileCtrlItemInfo* pItem = (CTreeFileCtrlItemInfo*) GetItemData(hItem);
839    ASSERT(pItem);
840      if (m_bAllowRename && !IsDrive(hItem) && !pItem->m_bNetworkNode)
841      return (EditLabel(hItem) != NULL);
842    else
843      return FALSE;
844  }
845  else
846    return FALSE;
847}
848
849BOOL CTreeFileCtrl::ShowProperties(HTREEITEM hItem)
850{
851  BOOL bSuccess = FALSE;
852  if (m_bAllowProperties && hItem)
853  {
854    //Show the "properties" for the selected file
855    CString sFile = ItemToPath(hItem);
856    SHELLEXECUTEINFO sei;
857    ZeroMemory(&sei,sizeof(sei));
858    sei.cbSize = sizeof(sei);
859    sei.hwnd = AfxGetMainWnd()->GetSafeHwnd();
860    sei.nShow = SW_SHOW;
861    sei.lpFile = sFile.GetBuffer(sFile.GetLength());
862    sei.lpVerb = _T("properties");
863    sei.fMask  = SEE_MASK_INVOKEIDLIST;
864    bSuccess = ShellExecuteEx(&sei);
865    sFile.ReleaseBuffer();
866  }
867  return bSuccess;
868}
869
870BOOL CTreeFileCtrl::Delete(HTREEITEM hItem)
871{
872  BOOL bSuccess = FALSE;
873
874  if (hItem)
875  {
876    CTreeFileCtrlItemInfo* pItem = (CTreeFileCtrlItemInfo*) GetItemData(hItem);
877    ASSERT(pItem);
878    if (m_bAllowDelete && !IsDrive(hItem) && !pItem->m_bNetworkNode)
879    {
880      //Create a Multi SZ string with the filename to delete
881      CString sFileToDelete = ItemToPath(hItem);
882      int nChars = sFileToDelete.GetLength() + 1;
883      nChars++;
884      SHFILEOPSTRUCT shfo;
885      ZeroMemory(&shfo, sizeof(SHFILEOPSTRUCT));
886      shfo.hwnd = AfxGetMainWnd()->GetSafeHwnd();
887      shfo.wFunc = FO_DELETE;
888
889      //Undo is not allowed if the SHIFT key is held down
890      if (!(GetKeyState(VK_SHIFT) & 0x8000))
891        shfo.fFlags = FOF_ALLOWUNDO;
892
893      TCHAR* pszFrom = new TCHAR[nChars];
894      TCHAR* pszCur = pszFrom;
895      _tcscpy(pszCur, sFileToDelete);
896      pszCur[nChars-1] = _T('\0');
897      shfo.pFrom = pszFrom;
898
899      BOOL bOldAutoRefresh = m_bAutoRefresh;
900      m_bAutoRefresh = FALSE; //Prevents us from getting thread notifications
901
902      //Let the shell perform the actual deletion
903      if (SHFileOperation(&shfo) == 0 && shfo.fAnyOperationsAborted == FALSE)
904      {
905        //Delete the item from the view
906        bSuccess = DeleteItem(hItem);
907      }
908
909      m_bAutoRefresh = bOldAutoRefresh;
910
911      //Free up the memory we had allocated
912      delete [] pszFrom;
913    }
914  }
915  return bSuccess;
916}
917
918BOOL CTreeFileCtrl::Open(HTREEITEM hItem)
919{
920  BOOL bSuccess = FALSE;
921  if (m_bAllowOpen && hItem)
922  {
923    //Show the "properties" for the selected file
924    CString sFile = ItemToPath(hItem);
925    SHELLEXECUTEINFO sei;
926    ZeroMemory(&sei,sizeof(sei));
927    sei.cbSize = sizeof(sei);
928    sei.hwnd = AfxGetMainWnd()->GetSafeHwnd();
929    sei.nShow = SW_SHOW;
930    sei.lpFile = sFile.GetBuffer(sFile.GetLength());
931    sei.fMask  = SEE_MASK_INVOKEIDLIST;
932    bSuccess = ShellExecuteEx(&sei);
933    sFile.ReleaseBuffer();
934  }
935  return bSuccess;
936}
937
938void CTreeFileCtrl::SetFlags(DWORD dwFlags)
939{
940  SetShowFiles((dwFlags & TFC_SHOWFILES) != 0);
941  SetAllowDragDrop((dwFlags & TFC_ALLOWDRAGDROP) != 0);
942  SetAllowRename((dwFlags & TFC_ALLOWRENAME) != 0); 
943  SetAllowOpen((dwFlags & TFC_ALLOWOPEN) != 0);   
944  SetAllowProperties((dwFlags & TFC_ALLOWPROPERTIES) != 0);
945  SetAllowDelete((dwFlags & TFC_ALLOWDELETE) != 0);
946}
947
948void CTreeFileCtrl::SetDriveHideFlags(DWORD dwDriveHideFlags)
949{
950  m_dwDriveHideFlags = dwDriveHideFlags;
951
952  //Force a refresh
953  Refresh();
954}
955
956void CTreeFileCtrl::SetFileHideFlags(DWORD dwFileHideFlags)
957{
958  m_dwFileHideFlags = dwFileHideFlags;
959
960  //Force a refresh
961  Refresh();
962}
963
964void CTreeFileCtrl::SetFileNameMask(const CString& sFileNameMask)
965{
966  m_sFileNameMask = sFileNameMask;
967
968  //Force a refresh
969  Refresh();
970}
971
972
973void CTreeFileCtrl::SetCompressedColor(COLORREF rgbCompressed)
974{
975  m_rgbCompressed = rgbCompressed;
976
977  //Force a refresh
978  Refresh();
979}
980
981void CTreeFileCtrl::SetDisplayNetwork(BOOL bDisplayNetwork)
982{
983  m_bDisplayNetwork = bDisplayNetwork;
984
985  //Force a refresh
986  Refresh();
987}
988
989void CTreeFileCtrl::SetUsingDifferentColorForCompressed(BOOL bShowCompressedUsingDifferentColor)
990{
991  m_bShowCompressedUsingDifferentColor = bShowCompressedUsingDifferentColor;
992
993  //Force a refresh
994  Refresh();
995}
996
997void CTreeFileCtrl::SetUsingDifferentIconForSharedFolders(BOOL bShowSharedUsingDifferentIcon)
998{
999  m_bShowSharedUsingDifferentIcon = bShowSharedUsingDifferentIcon;
1000
1001  //Force a refresh
1002  Refresh();
1003}
1004
1005void CTreeFileCtrl::SetUsingDifferentColorForEncrypted(BOOL bShowEncryptedUsingDifferentColor)
1006{
1007  m_bShowEncryptedUsingDifferentColor = bShowEncryptedUsingDifferentColor;
1008
1009  //Force a refresh
1010  Refresh();
1011};
1012
1013void CTreeFileCtrl::SetShowFileExtensions(HideFileExtension FileExtensions)
1014{
1015  m_FileExtensions = FileExtensions;
1016
1017  //Force a refresh
1018  Refresh();
1019}
1020
1021void CTreeFileCtrl::SetNetworkItemTypes(DWORD dwTypes)
1022{
1023  m_dwNetworkItemTypes = dwTypes;
1024
1025  //Force a refresh
1026  Refresh();
1027}
1028
1029void CTreeFileCtrl::SetShowDriveLabels(BOOL bShowDriveLabels)
1030{
1031  m_bShowDriveLabels = bShowDriveLabels;
1032
1033  //Force a refresh
1034  Refresh();
1035}
1036
1037void CTreeFileCtrl::SetShowMyComputer(BOOL bShowMyComputer)
1038{
1039  m_bShowMyComputer = bShowMyComputer;
1040
1041  //Force a refresh
1042  Refresh();
1043}
1044
1045void CTreeFileCtrl::SetShowRootedFolder(BOOL bShowRootedFolder)
1046{
1047  m_bShowRootedFolder = bShowRootedFolder;
1048
1049  //Force a refresh
1050  Refresh();
1051}
1052
1053BOOL CTreeFileCtrl::CanDisplayFile(const CFileFind& find)
1054{
1055  //Derived classes can decide dynamically whether or not a
1056  //certain file are to be displayed. CTreeFileCtrl by default
1057  //displays all files which do not have attributes as set in m_dwFileHideFlags
1058
1059  return (m_bShowFiles && !find.IsDirectory() && !find.MatchesMask(m_dwFileHideFlags));
1060}
1061
1062BOOL CTreeFileCtrl::CanDisplayFolder(const CFileFind& find)
1063{
1064  //Derived classes can decide dynamically whether or not a
1065  //certain folder are to be displayed. CTreeFileCtrl by default
1066  //displays all folders excluding the ".." and "." entries
1067
1068  return (find.IsDirectory() && !find.IsDots());
1069}
1070
1071BOOL CTreeFileCtrl::CanDisplayNetworkItem(CTreeFileCtrlItemInfo* /*pItem*/)
1072{
1073  //Derived classes can decide dynamically whether or not a
1074  //certain network items are to be displayed. CTreeFileCtrl by default
1075  //displays all network items
1076
1077  return TRUE;
1078}
1079
1080BOOL CTreeFileCtrl::CanHandleChangeNotifications(const CString& sPath)
1081{
1082  //check if this drive is one of the types which can issue notification changes
1083  CString sDrive(sPath);
1084  if (!IsDrive(sDrive))
1085    sDrive = sPath.Left(3);
1086
1087  UINT nDrive = GetDriveType(sDrive);
1088  return ((nDrive != DRIVE_REMOVABLE) && nDrive != DRIVE_CDROM);
1089}
1090
1091BOOL CTreeFileCtrl::CanDisplayDrive(const CString& sDrive)
1092{
1093  //Derived classes can decide dynamically whether or not a
1094  //certain drive is to be displayed. CTreeFileCtrl by default
1095  //displays all drives which do not have attributes as set in
1096  //m_dwDriveHideFlags
1097
1098  //check if this drive is one of the types to hide
1099  BOOL bDisplay = TRUE;
1100  UINT nDrive = GetDriveType(sDrive);
1101  switch (nDrive)
1102  {
1103    case DRIVE_REMOVABLE:
1104    {
1105      if (m_dwDriveHideFlags & DRIVE_ATTRIBUTE_REMOVABLE)
1106        bDisplay = FALSE;
1107      break;
1108    }
1109    case DRIVE_FIXED:
1110    {
1111      if (m_dwDriveHideFlags & DRIVE_ATTRIBUTE_FIXED)
1112        bDisplay = FALSE;
1113      break;
1114    }
1115    case DRIVE_REMOTE:
1116    {
1117      if (m_dwDriveHideFlags & DRIVE_ATTRIBUTE_REMOTE)
1118        bDisplay = FALSE;
1119      break;
1120    }
1121    case DRIVE_CDROM:
1122    {
1123      if (m_dwDriveHideFlags & DRIVE_ATTRIBUTE_CDROM)
1124        bDisplay = FALSE;
1125      break;
1126    }
1127    case DRIVE_RAMDISK:
1128    {
1129      if (m_dwDriveHideFlags & DRIVE_ATTRIBUTE_RAMDISK)
1130        bDisplay = FALSE;
1131      break;
1132    }
1133    default:
1134    {
1135      break;
1136    }
1137  }
1138
1139  return bDisplay;
1140}
1141
1142void CTreeFileCtrl::OnRename() 
1143{
1144  Rename(GetSelectedItem());
1145}
1146
1147void CTreeFileCtrl::OnUpdateRename(CCmdUI* pCmdUI) 
1148{
1149  HTREEITEM hSelItem = GetSelectedItem();
1150  if (hSelItem)
1151  {
1152    CTreeFileCtrlItemInfo* pItem = (CTreeFileCtrlItemInfo*) GetItemData(hSelItem);
1153    ASSERT(pItem);
1154      pCmdUI->Enable(m_bAllowRename && !IsDrive(hSelItem) && !pItem->m_bNetworkNode);
1155  }
1156  else
1157    pCmdUI->Enable(FALSE);
1158}
1159
1160void CTreeFileCtrl::OnProperties() 
1161{
1162  ShowProperties(GetSelectedItem());
1163}
1164
1165void CTreeFileCtrl::OnUpdateProperties(CCmdUI* pCmdUI) 
1166{
1167  HTREEITEM hSelItem = GetSelectedItem();
1168  if (hSelItem)
1169  {
1170    if (m_bAllowProperties)
1171    {
1172      CTreeFileCtrlItemInfo* pItem = (CTreeFileCtrlItemInfo*) GetItemData(hSelItem);
1173      ASSERT(pItem);
1174      if (pItem->m_bNetworkNode)
1175      { 
1176        if (pItem->m_pNetResource)
1177          pCmdUI->Enable(pItem->m_pNetResource->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER ||
1178                         pItem->m_pNetResource->dwDisplayType == RESOURCEDISPLAYTYPE_SHARE);
1179        else
1180          pCmdUI->Enable(FALSE);
1181      }
1182      else
1183        pCmdUI->Enable(TRUE);
1184    }
1185    else
1186      pCmdUI->Enable(FALSE);
1187  }
1188  else
1189    pCmdUI->Enable(FALSE);
1190}
1191
1192void CTreeFileCtrl::OnOpen() 
1193{
1194  Open(GetSelectedItem());
1195}
1196
1197void CTreeFileCtrl::OnUpdateOpen(CCmdUI* pCmdUI) 
1198{
1199  HTREEITEM hSelItem = GetSelectedItem();
1200  if (hSelItem)
1201  {
1202    if (m_bAllowOpen)
1203    {
1204      CTreeFileCtrlItemInfo* pItem = (CTreeFileCtrlItemInfo*) GetItemData(hSelItem);
1205      ASSERT(pItem);
1206      if (pItem->m_bNetworkNode)
1207      { 
1208        if (pItem->m_pNetResource)
1209          pCmdUI->Enable(pItem->m_pNetResource->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER ||
1210                         pItem->m_pNetResource->dwDisplayType == RESOURCEDISPLAYTYPE_SHARE);
1211        else
1212          pCmdUI->Enable(FALSE);
1213      }
1214      else
1215        pCmdUI->Enable(TRUE);
1216    }
1217    else
1218      pCmdUI->Enable(FALSE);
1219  }
1220  else
1221    pCmdUI->Enable(FALSE);
1222
1223}
1224
1225void CTreeFileCtrl::OnDelete() 
1226{
1227  Delete(GetSelectedItem());
1228}
1229
1230void CTreeFileCtrl::OnUpdateDelete(CCmdUI* pCmdUI) 
1231{
1232  HTREEITEM hSelItem = GetSelectedItem();
1233  if (hSelItem)
1234  {
1235    CTreeFileCtrlItemInfo* pItem = (CTreeFileCtrlItemInfo*) GetItemData(hSelItem);
1236    ASSERT(pItem);
1237      pCmdUI->Enable(m_bAllowDelete && !IsDrive(hSelItem) && !pItem->m_bNetworkNode);
1238  }
1239  else
1240    pCmdUI->Enable(FALSE);
1241}
1242
1243void CTreeFileCtrl::OnContextMenu(CWnd*, CPoint point)
1244{
1245    CMenu menu;
1246  VERIFY(menu.LoadMenu(IDR_TREEFILECTRL_POPUP));
1247    CMenu* pPopup = menu.GetSubMenu(0);
1248    ASSERT(pPopup != NULL);
1249    pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,   this);
1250}
1251
1252HTREEITEM CTreeFileCtrl::InsertFileItem(HTREEITEM hParent, CTreeFileCtrlItemInfo* pItem, BOOL bShared, int nIcon, int nSelIcon, BOOL bCheckForChildren)
1253{
1254  CString sLabel;
1255
1256  //Correct the label if need be
1257  if (IsDrive(pItem->m_sFQPath) && m_bShowDriveLabels)
1258    sLabel = GetDriveLabel(pItem->m_sFQPath);
1259  else
1260    sLabel = GetCorrectedLabel(pItem);
1261
1262  //Add the actual item
1263    TV_INSERTSTRUCT tvis;
1264  ZeroMemory(&tvis, sizeof(TV_INSERTSTRUCT));
1265    tvis.hParent = hParent;
1266    tvis.hInsertAfter = TVI_LAST;
1267    tvis.item.mask = TVIF_CHILDREN | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_PARAM;
1268  tvis.item.lParam = (LPARAM) pItem;
1269  tvis.item.pszText = sLabel.GetBuffer(sLabel.GetLength());
1270  tvis.item.iImage = nIcon;
1271  tvis.item.iSelectedImage = nSelIcon;
1272  if (bCheckForChildren)
1273      tvis.item.cChildren = HasGotSubEntries(pItem->m_sFQPath);
1274  else
1275    tvis.item.cChildren = TRUE;
1276  if (bShared)
1277  {
1278    tvis.item.mask |= TVIF_STATE;
1279    tvis.item.stateMask |= TVIS_OVERLAYMASK;
1280    tvis.item.state |= INDEXTOOVERLAYMASK(1); //1 is the index for the shared overlay image
1281  }
1282
1283  HTREEITEM hItem = InsertItem(&tvis);
1284  sLabel.ReleaseBuffer();
1285  return hItem;
1286}
1287
1288BOOL CTreeFileCtrl::GetChecked(HTREEITEM hItem) const
1289{
1290    ASSERT(::IsWindow(m_hWnd));
1291    TVITEM item;
1292    item.mask = TVIF_HANDLE | TVIF_STATE;
1293    item.hItem = hItem;
1294    item.stateMask = TVIS_STATEIMAGEMASK;
1295    VERIFY(::SendMessage(m_hWnd, TVM_GETITEM, 0, (LPARAM)&item));
1296    // Return zero if it's not checked, or nonzero otherwise.
1297    return ((BOOL)(item.state >> 12) -1);
1298}
1299
1300BOOL CTreeFileCtrl::SetChecked(HTREEITEM hItem, BOOL fCheck)
1301{
1302    ASSERT(::IsWindow(m_hWnd));
1303    TVITEM item;
1304    item.mask = TVIF_HANDLE | TVIF_STATE;
1305    item.hItem = hItem;
1306    item.stateMask = TVIS_STATEIMAGEMASK;
1307
1308    /*
1309    Since state images are one-based, 1 in this macro turns the check off, and
1310    2 turns it on.
1311    */
1312    item.state = INDEXTOSTATEIMAGEMASK((fCheck ? 2 : 1));
1313
1314    return (BOOL)::SendMessage(m_hWnd, TVM_SETITEM, 0, (LPARAM)&item);
1315}
1316
1317
1318void CTreeFileCtrl::Refresh()
1319{
1320  if (GetSafeHwnd())
1321    OnRefresh();
1322}
1323
1324BOOL CTreeFileCtrl::IsExpanded(HTREEITEM hItem)
1325{
1326  TVITEM tvItem;
1327  tvItem.hItem = hItem;
1328  tvItem.mask = TVIF_HANDLE | TVIF_STATE;
1329  return GetItem(&tvItem) && (tvItem.state & TVIS_EXPANDED);
1330}
1331
1332CString CTreeFileCtrl::GetCorrectedLabel(CTreeFileCtrlItemInfo* pItem)
1333{
1334  CString sLabel(pItem->m_sRelativePath);
1335
1336  switch (m_FileExtensions)
1337  {
1338    case UseTheShellSetting:
1339    {
1340      TCHAR pszLabel[_MAX_PATH];
1341      if (IsFile(pItem->m_sFQPath) && GetFileTitle(pItem->m_sRelativePath, pszLabel, _MAX_PATH) == 0)
1342      {
1343        pItem->m_bExtensionHidden = (sLabel.CompareNoCase(pszLabel) != 0);
1344        sLabel = pszLabel;
1345      }
1346      break;
1347    }
1348    case HideExtension:
1349    {
1350      //Remove the extension if the item is a file
1351      if (IsFile(pItem->m_sFQPath))
1352      {
1353        TCHAR szPath[_MAX_PATH];
1354        TCHAR szDrive[_MAX_DRIVE];
1355        TCHAR szDir[_MAX_DIR];
1356        TCHAR szFname[_MAX_FNAME];
1357        _tsplitpath(pItem->m_sRelativePath, szDrive, szDir, szFname, NULL);
1358        _tmakepath(szPath, szDrive, szDir, szFname, NULL);
1359        sLabel = szPath;
1360        pItem->m_bExtensionHidden = TRUE;
1361      }
1362      break;
1363    }
1364    default:
1365    {
1366      pItem->m_bExtensionHidden = FALSE;
1367      break;
1368    }
1369  }
1370
1371  return sLabel;
1372}
1373
1374void CTreeFileCtrl::OnRefresh() 
1375{
1376  //Just in case this will take some time
1377  CWaitCursor wait;
1378
1379  SetRedraw(FALSE);
1380
1381  //Get the item which is currently selected
1382  HTREEITEM hSelItem = GetSelectedItem();
1383  CString sItem;
1384  BOOL bExpanded = FALSE;
1385  if (hSelItem)
1386  {
1387    sItem = ItemToPath(hSelItem);
1388    bExpanded = IsExpanded(hSelItem); 
1389  }
1390
1391  theSharedEnumerator.Refresh();
1392
1393  KillNotificationThreads();
1394
1395  //Remove all nodes that currently exist
1396  Clear();
1397
1398  //Display the folder items in the tree
1399  if (m_sRootFolder.IsEmpty())
1400  {
1401    //Should we insert a "My Computer" node
1402    if (m_bShowMyComputer)
1403    {
1404      CTreeFileCtrlItemInfo* pItem = new CTreeFileCtrlItemInfo;
1405      pItem->m_bNetworkNode = FALSE;
1406      int nIcon = 0;
1407      int nSelIcon = 0;
1408
1409      //Get the localized name and correct icons for "My Computer"
1410      LPITEMIDLIST lpMCPidl;
1411      if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &lpMCPidl)))
1412      {
1413        SHFILEINFO sfi;
1414        if (SHGetFileInfo((LPCTSTR)lpMCPidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL | SHGFI_DISPLAYNAME))
1415        {
1416          pItem->m_sRelativePath = sfi.szDisplayName;
1417          pItem->m_sFQPath = pItem->m_sRelativePath;
1418        }
1419        nIcon = GetIconIndex(lpMCPidl);
1420        nSelIcon = GetSelIconIndex(lpMCPidl);
1421
1422        //Free up the pidl now that we are finished with it
1423        ASSERT(m_pMalloc);
1424        m_pMalloc->Free(lpMCPidl);
1425        m_pMalloc->Release();
1426      }
1427
1428      //Add it to the tree control
1429      m_hMyComputerRoot = InsertFileItem(TVI_ROOT, pItem, FALSE, nIcon, nSelIcon, FALSE);
1430    }
1431
1432    //Display all the drives
1433    if (!m_bShowMyComputer)
1434      DisplayDrives(TVI_ROOT, FALSE);
1435
1436    //Also add network neighborhood if requested to do so
1437    if (m_bDisplayNetwork)
1438    {
1439      CTreeFileCtrlItemInfo* pItem = new CTreeFileCtrlItemInfo;
1440      pItem->m_bNetworkNode = TRUE;
1441      int nIcon = 0;
1442      int nSelIcon = 0;
1443
1444      //Get the localized name and correct icons for "Network Neighborhood"
1445      LPITEMIDLIST lpNNPidl;
1446      if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_NETWORK, &lpNNPidl)))
1447      {
1448        SHFILEINFO sfi;
1449        if (SHGetFileInfo((LPCTSTR)lpNNPidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL | SHGFI_DISPLAYNAME))
1450        {
1451          pItem->m_sRelativePath = sfi.szDisplayName;
1452          pItem->m_sFQPath = pItem->m_sRelativePath;
1453        }
1454        nIcon = GetIconIndex(lpNNPidl);
1455        nSelIcon = GetSelIconIndex(lpNNPidl);
1456
1457        //Free up the pidl now that we are finished with it
1458        ASSERT(m_pMalloc);
1459        m_pMalloc->Free(lpNNPidl);
1460        m_pMalloc->Release();
1461      }
1462
1463      //Add it to the tree control
1464      m_hNetworkRoot = InsertFileItem(TVI_ROOT, pItem, FALSE, nIcon, nSelIcon, FALSE);
1465    }
1466  }
1467  else
1468  {
1469    DisplayPath(m_sRootFolder, TVI_ROOT, FALSE);
1470    if (CanHandleChangeNotifications(m_sRootFolder))
1471      CreateMonitoringThread(m_sRootFolder);
1472  }
1473 
1474  //Reselect the initially selected item
1475  if (hSelItem)
1476    SetSelectedPath(sItem, bExpanded);
1477
1478  //Turn back on the redraw flag
1479  SetRedraw(TRUE);
1480}
1481
1482BOOL CTreeFileCtrl::OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT* pResult) 
1483{
1484    TV_DISPINFO* pDispInfo = (TV_DISPINFO*)pNMHDR;
1485
1486  if (pDispInfo->item.hItem)
1487  {
1488    CTreeFileCtrlItemInfo* pItem = (CTreeFileCtrlItemInfo*) GetItemData(pDispInfo->item.hItem);
1489    ASSERT(pItem);
1490      if (m_bAllowRename && !IsDrive(pDispInfo->item.hItem) && !pItem->m_bNetworkNode)
1491      *pResult = FALSE;
1492    else
1493        *pResult = TRUE;
1494  }
1495  else
1496    *pResult = TRUE;
1497
1498  return TRUE; //Allow the message to be reflected again
1499}
1500
1501BOOL CTreeFileCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) 
1502{
1503  NMTVCUSTOMDRAW* pCustomDraw = (NMTVCUSTOMDRAW*) pNMHDR;
1504  switch (pCustomDraw->nmcd.dwDrawStage) 
1505  {
1506    case CDDS_PREPAINT:
1507    {
1508      *pResult = CDRF_NOTIFYITEMDRAW; //Tell the control that we are interested in item notifications
1509      break;
1510        }   
1511    case CDDS_ITEMPREPAINT:
1512    {
1513      //Check to see if this item is compressed and if it it is, change its
1514      //color just like explorer does
1515      if (m_bShowCompressedUsingDifferentColor && 
1516          ((pCustomDraw->nmcd.uItemState & CDIS_SELECTED) == 0) && 
1517          IsCompressed((HTREEITEM) pCustomDraw->nmcd.dwItemSpec))
1518        pCustomDraw->clrText = m_rgbCompressed;
1519      //also check for encrypted files
1520      else if (m_bShowEncryptedUsingDifferentColor && 
1521          ((pCustomDraw->nmcd.uItemState & CDIS_SELECTED) == 0) && 
1522          IsEncrypted((HTREEITEM) pCustomDraw->nmcd.dwItemSpec))
1523        pCustomDraw->clrText = m_rgbEncrypted;
1524
1525      //Let it know that we want post paint notifications
1526      *pResult = CDRF_NOTIFYPOSTPAINT;
1527
1528      break;
1529    }
1530    case CDDS_ITEMPOSTPAINT:
1531    {
1532      HTREEITEM hItem = (HTREEITEM) pCustomDraw->nmcd.dwItemSpec;
1533
1534      CTreeFileCtrlItemInfo* pItem = (CTreeFileCtrlItemInfo*) GetItemData(hItem);
1535      ASSERT(pItem);
1536      if (pItem->m_pNetResource)
1537      {
1538        //Determine if we should custom draw the bitmap
1539        BOOL bDrawIcon = FALSE;
1540        int nIndexToDraw = -1;
1541        switch (pItem->m_pNetResource->dwDisplayType)
1542        {
1543          case RESOURCEDISPLAYTYPE_DOMAIN:
1544          {
1545            bDrawIcon = TRUE;
1546            nIndexToDraw = 0;
1547            break;
1548          }
1549          case RESOURCEDISPLAYTYPE_SERVER:
1550          {
1551            bDrawIcon = TRUE;
1552            nIndexToDraw = 3;
1553            break;
1554          }
1555          case RESOURCEDISPLAYTYPE_SHARE:
1556          {
1557            //Deliberately do nothing
1558            break;
1559          }
1560          case RESOURCEDISPLAYTYPE_ROOT:
1561          {
1562            bDrawIcon = TRUE;
1563            nIndexToDraw = 2;
1564            break;
1565          }
1566          default:
1567          { 
1568            bDrawIcon = TRUE;
1569            nIndexToDraw = 1;
1570            break;
1571          }
1572        }
1573
1574        if (bDrawIcon)
1575        { 
1576          //Draw the icon of the tree view item using the specified bitmap
1577          CDC dc;
1578          dc.Attach(pCustomDraw->nmcd.hdc);
1579
1580          //First work out the position of the icon
1581                    CRect rItemRect;
1582                    GetItemRect(hItem, rItemRect, TRUE);
1583          CPoint point(rItemRect.left, rItemRect.top);
1584          UINT nFlags = 0;
1585          CPoint testPoint(pCustomDraw->nmcd.rc.left, rItemRect.top+2);
1586          BOOL bFound = FALSE;
1587          do
1588          {
1589            HitTest(testPoint, &nFlags); 
1590
1591            //Prepare for the next time around
1592            bFound  = (nFlags & TVHT_ONITEMICON) || (nFlags & TVHT_ONITEMSTATEICON);
1593            if (!bFound)
1594              testPoint.x++;
1595          }
1596          while (!bFound);
1597          point.x = testPoint.x;
1598          CRect r(point.x, point.y, point.x+16, point.y+16);
1599
1600          //Draw it using the IL
1601          dc.FillSolidRect(&r, RGB(255, 255, 255));
1602          m_ilNetwork.Draw(&dc, nIndexToDraw, point, ILD_NORMAL);
1603
1604          //Release the DC
1605          dc.Detach();
1606        }
1607      }
1608
1609      *pResult = CDRF_DODEFAULT;
1610      break;
1611    }
1612    default:
1613    {
1614      break;
1615    }
1616  }
1617 
1618  return TRUE; //Allow the message to be reflected again
1619}
1620
1621BOOL CTreeFileCtrl::OnSelChanged(NMHDR* pNMHDR, LRESULT* pResult) 
1622{
1623    NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
1624
1625  //Nothing selected
1626  if (pNMTreeView->itemNew.hItem == NULL)
1627    return FALSE;
1628
1629  //Check to see if the current item is valid, if not then delete it (Exclude network items from this check)
1630  CTreeFileCtrlItemInfo* pItem = (CTreeFileCtrlItemInfo*) GetItemData(pNMTreeView->itemNew.hItem);
1631  ASSERT(pItem);
1632  CString sPath = pItem->m_sFQPath;
1633  if ((pNMTreeView->itemNew.hItem != m_hNetworkRoot) && (pItem->m_pNetResource == NULL) && 
1634      (pNMTreeView->itemNew.hItem != m_hMyComputerRoot) && !IsDrive(sPath) && (GetFileAttributes(sPath) == 0xFFFFFFFF))
1635  {
1636    //Before we delete it see if we are the only child item
1637    HTREEITEM hParent = GetParentItem(pNMTreeView->itemNew.hItem);
1638
1639    //Delete the item
1640    DeleteItem(pNMTreeView->itemNew.hItem);
1641
1642    //Remove all the child items from the parent
1643    SetHasPlusButton(hParent, FALSE);
1644
1645    *pResult = 1;
1646
1647    return FALSE; //Allow the message to be reflected again
1648  }
1649
1650  //Add to the prev array the item we were just at
1651  if (pNMTreeView->itemOld.hItem && !m_bUpdatingHistorySelection)
1652  {
1653    if (m_PrevItems.GetSize() > m_nMaxHistory)
1654      m_PrevItems.RemoveAt(0);
1655    m_PrevItems.Add(pNMTreeView->itemOld.hItem);
1656  }
1657
1658  //Remeber the serial number for this item (if it is a drive)
1659  if (IsDrive(sPath))
1660  {
1661    int nDrive = sPath.GetAt(0) - _T('A');
1662    GetSerialNumber(sPath, m_dwMediaID[nDrive]); 
1663  }
1664
1665  //call the virtual function
1666  OnSelectionChanged(pNMTreeView, sPath);
1667   
1668    *pResult = 0;
1669
1670  return FALSE; //Allow the message to be reflected again
1671}
1672
1673void CTreeFileCtrl::OnSelectionChanged(NM_TREEVIEW*, const CString&)
1674{
1675}
1676
1677BOOL CTreeFileCtrl::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult) 
1678{
1679    TV_DISPINFO* pDispInfo = (TV_DISPINFO*)pNMHDR;
1680  if (pDispInfo->item.pszText)
1681  {
1682    SHFILEOPSTRUCT shfo;
1683    ZeroMemory(&shfo, sizeof(SHFILEOPSTRUCT));
1684    shfo.hwnd = AfxGetMainWnd()->GetSafeHwnd();
1685    shfo.wFunc = FO_RENAME;
1686    shfo.fFlags = FOF_ALLOWUNDO;
1687
1688    //Work out the "From" string
1689    CString sFrom = ItemToPath(pDispInfo->item.hItem);
1690    int nFromLength = sFrom.GetLength();
1691    TCHAR* pszFrom = new TCHAR[nFromLength + 2];
1692    _tcscpy(pszFrom, sFrom);
1693    pszFrom[nFromLength+1] = _T('\0');
1694    shfo.pFrom = pszFrom;
1695    HTREEITEM hParent = GetParentItem(pDispInfo->item.hItem);
1696    CString sParent = ItemToPath(hParent);
1697
1698    //Work out the "To" string
1699    CString sTo;
1700    CString sToRelative(pDispInfo->item.pszText);
1701    if (IsDrive(sParent))
1702      sTo = sParent + pDispInfo->item.pszText;
1703    else
1704      sTo = sParent + _T("\\") + pDispInfo->item.pszText;
1705    CTreeFileCtrlItemInfo* pItem = (CTreeFileCtrlItemInfo*) GetItemData(pDispInfo->item.hItem);
1706    ASSERT(pItem);
1707    if (pItem->m_bExtensionHidden)
1708    {
1709      TCHAR szExt[_MAX_EXT];
1710      _tsplitpath(sFrom, NULL, NULL, NULL, szExt);
1711      sTo += szExt;
1712      sToRelative += szExt;
1713    }
1714    size_t nToLength = _tcslen(sTo);
1715    TCHAR* pszTo = new TCHAR[nToLength + 2];
1716    _tcscpy(pszTo, sTo);
1717    pszTo[nToLength+1] = _T('\0');
1718    shfo.pTo = pszTo;
1719
1720    BOOL bOldAutoRefresh = m_bAutoRefresh;
1721    m_bAutoRefresh = FALSE; //Prevents us from getting thread notifications
1722
1723    //Let the shell perform the actual rename
1724    if (SHFileOperation(&shfo) == 0 && shfo.fAnyOperationsAborted == FALSE)
1725    {
1726      *pResult = TRUE;
1727
1728      //Update its text 
1729      SetItemText(pDispInfo->item.hItem, pDispInfo->item.pszText);
1730
1731      //Update the item data
1732      pItem->m_sFQPath = sTo;
1733      pItem->m_sRelativePath = sToRelative;
1734
1735      //Also update the icons for it (if need be)
1736      if (!pItem->m_bExtensionHidden)
1737      {
1738        CString sPath = ItemToPath(pDispInfo->item.hItem);
1739        SetItemImage(pDispInfo->item.hItem, GetIconIndex(sPath), GetSelIconIndex(sPath));
1740      }
1741    }
1742
1743    m_bAutoRefresh = bOldAutoRefresh;
1744
1745    //Don't forget to free up the memory we allocated
1746    delete [] pszFrom;
1747    delete [] pszTo;
1748  }
1749   
1750    *pResult = 0;
1751
1752  return FALSE; //Allow the message to be reflected again
1753}
1754
1755BOOL CTreeFileCtrl::PreTranslateMessage(MSG* pMsg) 
1756{
1757  // When an item is being edited make sure the edit control
1758  // receives certain important key strokes
1759  if (GetEditControl())
1760  {
1761    ::TranslateMessage(pMsg);
1762    ::DispatchMessage(pMsg);
1763    return TRUE; // DO NOT process further
1764  }
1765
1766  //Context menu via the keyboard
1767    if ((((pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN) &&   // If we hit a key and
1768        (pMsg->wParam == VK_F10) && (GetKeyState(VK_SHIFT) & 0x8000)) != 0) || // it's Shift+F10 OR
1769          (pMsg->message == WM_CONTEXTMENU))                                                 // Natural keyboard key
1770    {
1771        CRect rect;
1772        GetItemRect(GetSelectedItem(), rect, TRUE);
1773        ClientToScreen(rect);
1774        OnContextMenu(NULL, rect.CenterPoint());
1775        return TRUE;
1776    }
1777  //Hitting the Escape key, Cancelling drag & drop
1778    else if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE && IsDragging())
1779  {
1780    EndDragging(TRUE);
1781    return TRUE;
1782  }
1783  //Hitting the Alt-Enter key combination, show the properties sheet
1784    else if (pMsg->message == WM_SYSKEYDOWN && pMsg->wParam == VK_RETURN)
1785  {
1786    PostMessage(WM_COMMAND, ID_TREEFILECTRL_PROPERTIES);
1787    return TRUE;
1788  }
1789  //Hitting the Enter key, open the item
1790    else if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)
1791  {
1792    PostMessage(WM_COMMAND, ID_TREEFILECTRL_OPEN);
1793    return TRUE;
1794  }
1795  //Hitting the delete key, delete the item
1796  else if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_DELETE)
1797  {
1798    PostMessage(WM_COMMAND, ID_TREEFILECTRL_DELETE);
1799    return TRUE;
1800  }
1801  //hitting the backspace key, go to the parent folder
1802  else if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_BACK)
1803  {
1804    UpOneLevel();
1805    return TRUE;
1806  }
1807  //hitting the F2 key, being in-place editing of an item
1808  else if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_F2)
1809  {
1810    PostMessage(WM_COMMAND, ID_TREEFILECTRL_RENAME);
1811    return TRUE;
1812  }
1813  //hitting the F5 key, force a refresh of the whole tree
1814  else if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_F5)
1815  {
1816    PostMessage(WM_COMMAND, ID_TREEFILECTRL_REFRESH);
1817    return TRUE;
1818  }
1819  //Hitting the Alt-Left Arrow key combination, move to the previous item
1820    else if (pMsg->message == WM_SYSKEYDOWN && pMsg->wParam == VK_LEFT)
1821  {
1822    PostMessage(WM_COMMAND, ID_TREEFILECTRL_BACK);
1823    return TRUE;
1824  }
1825  //Hitting the Alt-Right Arrow key combination, move to the next item
1826    else if (pMsg->message == WM_SYSKEYDOWN && pMsg->wParam == VK_RIGHT)
1827  {
1828    PostMessage(WM_COMMAND, ID_TREEFILECTRL_FORWARD);
1829    return TRUE;
1830  }
1831
1832  //Let the parent class do its thing
1833    return CTreeCtrl::PreTranslateMessage(pMsg);
1834}
1835
1836void CTreeFileCtrl::OnUpOneLevel() 
1837{
1838  HTREEITEM hItem = GetSelectedItem();
1839  if (hItem)
1840  {
1841    HTREEITEM hParent = GetParentItem(hItem);
1842    if (hParent)
1843      Select(hParent, TVGN_CARET);
1844  }
1845}
1846
1847void CTreeFileCtrl::UpOneLevel()
1848{
1849  SendMessage(WM_COMMAND, ID_TREEFILECTRL_UPONELEVEL);
1850}
1851
1852void CTreeFileCtrl::OnUpdateUpOneLevel(CCmdUI* pCmdUI)
1853{
1854  HTREEITEM hItem = GetSelectedItem();
1855  if (hItem)
1856    pCmdUI->Enable(GetParentItem(hItem) != NULL);
1857  else
1858    pCmdUI->Enable(FALSE);
1859}
1860
1861BOOL CTreeFileCtrl::IsFile(HTREEITEM hItem)
1862{
1863  return IsFile(ItemToPath(hItem));
1864}
1865
1866BOOL CTreeFileCtrl::IsFolder(HTREEITEM hItem)
1867{
1868  return IsFolder(ItemToPath(hItem));
1869}
1870
1871BOOL CTreeFileCtrl::IsDrive(HTREEITEM hItem)
1872{
1873  return IsDrive(ItemToPath(hItem));
1874}
1875
1876BOOL CTreeFileCtrl::DriveHasRemovableMedia(const CString& sPath)
1877{
1878  BOOL bRemovableMedia = FALSE;
1879  if (IsDrive(sPath))
1880  {
1881    UINT nDriveType = GetDriveType(sPath);
1882    bRemovableMedia = ((nDriveType == DRIVE_REMOVABLE) ||
1883                       (nDriveType == DRIVE_CDROM)); 
1884  }
1885
1886  return bRemovableMedia;
1887}
1888
1889BOOL CTreeFileCtrl::IsCompressed(HTREEITEM hItem)
1890{
1891  return IsCompressed(ItemToPath(hItem));
1892}
1893
1894BOOL CTreeFileCtrl::IsEncrypted(HTREEITEM hItem)
1895{
1896  return IsEncrypted(ItemToPath(hItem));
1897}
1898
1899BOOL CTreeFileCtrl::IsFile(const CString& sPath)
1900{
1901  DWORD dwAttributes = GetFileAttributes(sPath);
1902  return ((dwAttributes != 0xFFFFFFFF) && ((dwAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0));
1903}
1904
1905BOOL CTreeFileCtrl::IsFolder(const CString& sPath)
1906{
1907  DWORD dwAttributes = GetFileAttributes(sPath);
1908  return ((dwAttributes != 0xFFFFFFFF) && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY));
1909}
1910
1911BOOL CTreeFileCtrl::IsDrive(const CString& sPath)
1912{
1913  return (sPath.GetLength() == 3 && sPath.GetAt(1) == _T(':') && sPath.GetAt(2) == _T('\\'));
1914}
1915
1916BOOL CTreeFileCtrl::IsCompressed(const CString& sPath)
1917{
1918  BOOL bCompressed = FALSE;
1919  if (!IsDrive(sPath))
1920  {
1921    DWORD dwAttributes = GetFileAttributes(sPath);
1922    bCompressed = ((dwAttributes != 0xFFFFFFFF) && (dwAttributes & FILE_ATTRIBUTE_COMPRESSED));
1923  }
1924
1925  return bCompressed;
1926}
1927
1928BOOL CTreeFileCtrl::IsEncrypted(const CString& sPath)
1929{
1930  BOOL bEncrypted = FALSE;
1931  if (!IsDrive(sPath))
1932  {
1933    DWORD dwAttributes = GetFileAttributes(sPath);
1934    bEncrypted = ((dwAttributes != 0xFFFFFFFF) && (dwAttributes & FILE_ATTRIBUTE_ENCRYPTED));
1935  }
1936
1937  return bEncrypted;
1938}
1939
1940
1941BOOL CTreeFileCtrl::HasGotSubEntries(const CString& sDirectory)
1942{
1943  ASSERT(sDirectory.GetLength());
1944
1945  if (DriveHasRemovableMedia(sDirectory))
1946  {
1947    return TRUE; //we do not bother searching for files on drives
1948                 //which have removable media as this would cause
1949                 //the drive to spin up, which for the case of a
1950                 //floppy is annoying
1951  }
1952  else
1953  {
1954    //First check to see if there is any sub directories 
1955    CFileFind find1;
1956    CString sFile;
1957    if (sDirectory.GetAt(sDirectory.GetLength()-1) == _T('\\'))
1958      sFile = sDirectory + _T("*.*");
1959    else
1960      sFile = sDirectory + _T("\\*.*");
1961    BOOL bFind = find1.FindFile(sFile); 
1962    while (bFind)
1963    {
1964      bFind = find1.FindNextFile();
1965      if (CanDisplayFolder(find1))
1966        return TRUE;
1967    }
1968
1969    //Now check to see if there is any files of the specfied file mask 
1970    CFileFind find2;
1971    if (sDirectory.GetAt(sDirectory.GetLength()-1) == _T('\\'))
1972      sFile = sDirectory + m_sFileNameMask;
1973    else
1974      sFile = sDirectory + _T("\\") + m_sFileNameMask;
1975    bFind = find2.FindFile(sFile); 
1976    while (bFind)
1977    {
1978      bFind = find2.FindNextFile();
1979      if (CanDisplayFile(find2))
1980        return TRUE;
1981    }
1982  }
1983
1984  return FALSE;
1985}
1986
1987void CTreeFileCtrl::PopulateTree()
1988{
1989  ASSERT(GetSafeHwnd()); //Should only call this function after the creation of it on screen
1990
1991  //attach the image list to the tree control
1992  SetImageList(&theSystemImageList.m_ImageList, TVSIL_NORMAL);
1993
1994  //Force a refresh
1995  Refresh();
1996}
1997
1998void CTreeFileCtrl::CreateMonitoringThread(const CString& sPath)
1999{
2000  //Setup the structure we will be passing to the thread function
2001  CTreeFileCtrlThreadInfo* pInfo = new CTreeFileCtrlThreadInfo;
2002  pInfo->m_sPath = sPath;
2003  int nLength = pInfo->m_sPath.GetLength();
2004  ASSERT(nLength);
2005  if (nLength && pInfo->m_sPath.GetAt(nLength-1) != _T('\\'))
2006    pInfo->m_sPath += _T('\\');
2007  pInfo->m_pTree = this;
2008
2009  TRACE(_T("Creating monitoring thread for %s\n"), pInfo->m_sPath);
2010
2011  CWinThread* pThread = AfxBeginThread(MonitoringThread, pInfo, THREAD_PRIORITY_IDLE, 0, CREATE_SUSPENDED);
2012  ASSERT(pThread);
2013  pThread->m_bAutoDelete = FALSE;
2014  pInfo->m_pThread = pThread;
2015
2016  //Add the info struct to the thread array
2017  int nIndex = m_ThreadInfo.Add(pInfo);
2018  m_ThreadInfo.GetAt(nIndex)->m_nIndex = nIndex;
2019
2020  //Resume the thread now that everything is ready to go
2021  pThread->ResumeThread();
2022}
2023
2024UINT CTreeFileCtrl::MonitoringThread(LPVOID pParam)
2025{
2026  //Validate our parameters
2027  ASSERT(pParam);
2028  CTreeFileCtrlThreadInfo* pInfo = (CTreeFileCtrlThreadInfo*) pParam;
2029  ASSERT(pInfo->m_pTree);
2030
2031  //Form the notification flag to use
2032  DWORD dwNotifyFilter = FILE_NOTIFY_CHANGE_DIR_NAME;
2033  if (pInfo->m_pTree->m_bShowFiles)
2034    dwNotifyFilter |= FILE_NOTIFY_CHANGE_FILE_NAME;
2035
2036  //Get a handle to a file change notification object
2037  HANDLE hChange = ::FindFirstChangeNotification(pInfo->m_sPath, TRUE, dwNotifyFilter);
2038  if (hChange != INVALID_HANDLE_VALUE)
2039  {
2040    HANDLE handles[2];
2041    handles[0] = hChange;
2042    handles[1] = pInfo->m_TerminateEvent.m_hObject;
2043
2044    //Sleep until a file change notification wakes this thread or m_TerminateEvent becomes
2045    //set indicating it's time for the thread to end
2046    BOOL bContinue = TRUE;
2047    while (bContinue)
2048    {
2049      if (::WaitForMultipleObjects(2, handles, FALSE, INFINITE) - WAIT_OBJECT_0 == 0)
2050      {
2051        //Respond to the change notification by posting a user defined message
2052        //back to the GUI thread
2053        if (!pInfo->m_pTree->m_bAutoRefresh)
2054          bContinue = FALSE;
2055        else
2056          pInfo->m_pTree->PostMessage(WM_TREEUPDATE_CHANGE, (WPARAM) pInfo->m_nIndex);
2057       
2058        //Move onto the next notification
2059        ::FindNextChangeNotification(hChange);
2060      }
2061      else
2062      {
2063        //Kill the thread
2064        bContinue = FALSE;
2065      }
2066    }
2067
2068    //Close the handle we have open
2069    ::FindCloseChangeNotification(hChange);
2070  }
2071
2072  return 0;
2073}
2074
2075BOOL CTreeFileCtrl::GetSerialNumber(const CString& sDrive, DWORD& dwSerialNumber)
2076{
2077  return GetVolumeInformation(sDrive, NULL, 0, &dwSerialNumber, NULL, NULL, NULL, 0);
2078}
2079
2080BOOL CTreeFileCtrl::IsMediaValid(const CString& sDrive)
2081{
2082  //return TRUE if the drive does not support removable media
2083  UINT nDriveType = GetDriveType(sDrive);
2084  if ((nDriveType != DRIVE_REMOVABLE) && (nDriveType != DRIVE_CDROM))
2085    return TRUE;
2086
2087  //Return FALSE if the drive is empty (::GetVolumeInformation fails)
2088  DWORD dwSerialNumber;
2089  int nDrive = sDrive.GetAt(0) - _T('A');
2090  if (GetSerialNumber(sDrive, dwSerialNumber))
2091    m_dwMediaID[nDrive] = dwSerialNumber;
2092  else
2093  {
2094    m_dwMediaID[nDrive] = 0xFFFFFFFF;
2095    return FALSE;
2096  }
2097
2098  //Also return FALSE if the disk's serial number has changed
2099  if ((m_dwMediaID[nDrive] != dwSerialNumber) &&
2100      (m_dwMediaID[nDrive] != 0xFFFFFFFF))
2101  {
2102    m_dwMediaID[nDrive] = 0xFFFFFFFF;
2103    return FALSE;
2104  }
2105
2106  return TRUE;
2107}
2108
2109BOOL CTreeFileCtrl::EnumNetwork(HTREEITEM hParent)
2110{
2111  //What will be the return value from this function
2112    BOOL bGotChildren = FALSE;
2113
2114    //Check if the item already has a network resource and use it.
2115  CTreeFileCtrlItemInfo* pItem = (CTreeFileCtrlItemInfo*) GetItemData(hParent);
2116  ASSERT(pItem);
2117    NETRESOURCE* pNetResource = pItem->m_pNetResource;
2118
2119    //Setup for the network enumeration
2120    HANDLE hEnum;     
2121    DWORD dwResult = WNetOpenEnum(pNetResource ? RESOURCE_GLOBALNET : RESOURCE_CONTEXT, m_dwNetworkItemTypes,
2122                                              0, pNetResource ? pNetResource : NULL, &hEnum);
2123
2124    //Was the read sucessful
2125    if (dwResult != NO_ERROR)     
2126    {
2127        TRACE(_T("Cannot enumerate network drives, Error:%d\n"), dwResult);
2128        return FALSE;
2129    } 
2130   
2131  //Do the network enumeration
2132    DWORD cbBuffer = 16384;
2133
2134  BOOL bNeedMoreMemory = TRUE;
2135  BOOL bSuccess = FALSE;
2136  LPNETRESOURCE lpnrDrv = NULL;
2137    DWORD cEntries = 0;     
2138  while (bNeedMoreMemory && !bSuccess)
2139  {
2140    //Allocate the memory and enumerate
2141    lpnrDrv = (LPNETRESOURCE) new BYTE[cbBuffer];
2142    cEntries = 0xFFFFFFFF;
2143        dwResult = WNetEnumResource(hEnum, &cEntries, lpnrDrv, &cbBuffer);
2144
2145    if (dwResult == ERROR_MORE_DATA)
2146        {           
2147      //Free up the heap memory we have used
2148          delete [] lpnrDrv;     
2149
2150      cbBuffer *= 2;
2151        }
2152    else if (dwResult == NO_ERROR)
2153      bSuccess = TRUE;
2154    else
2155      bNeedMoreMemory = FALSE;
2156    }
2157
2158  //Enumeration successful?
2159  if (bSuccess)
2160  {
2161        //Scan through the results
2162        for (DWORD i=0; i<cEntries; i++)           
2163        {
2164            CString sNameRemote = lpnrDrv[i].lpRemoteName;
2165            if (sNameRemote.IsEmpty())
2166                sNameRemote = lpnrDrv[i].lpComment;
2167
2168            //Remove leading back slashes
2169            if (sNameRemote.GetLength() > 0 && sNameRemote[0] == _T('\\'))
2170                sNameRemote = sNameRemote.Mid(1);
2171            if (sNameRemote.GetLength() > 0 && sNameRemote[0] == _T('\\'))
2172                sNameRemote = sNameRemote.Mid(1);
2173
2174      //Setup the item data for the new item
2175      CTreeFileCtrlItemInfo* pItem = new CTreeFileCtrlItemInfo;
2176            pItem->m_pNetResource = new NETRESOURCE;
2177      ZeroMemory(pItem->m_pNetResource, sizeof(NETRESOURCE));
2178            *pItem->m_pNetResource = lpnrDrv[i];
2179      if (lpnrDrv[i].lpLocalName)
2180                pItem->m_pNetResource->lpLocalName  = _tcsdup(lpnrDrv[i].lpLocalName);
2181      if (lpnrDrv[i].lpRemoteName)
2182                pItem->m_pNetResource->lpRemoteName = _tcsdup(lpnrDrv[i].lpRemoteName);
2183      if (lpnrDrv[i].lpComment)
2184                pItem->m_pNetResource->lpComment    = _tcsdup(lpnrDrv[i].lpComment);
2185      if (lpnrDrv[i].lpProvider)
2186                pItem->m_pNetResource->lpProvider   = _tcsdup(lpnrDrv[i].lpProvider);
2187      if (lpnrDrv[i].lpRemoteName)
2188        pItem->m_sFQPath = lpnrDrv[i].lpRemoteName;
2189      else
2190        pItem->m_sFQPath = sNameRemote;
2191      pItem->m_sRelativePath = sNameRemote;
2192      pItem->m_bNetworkNode = TRUE;
2193
2194            //Display a share or the appropiate icon
2195            if (lpnrDrv[i].dwDisplayType == RESOURCEDISPLAYTYPE_SHARE)
2196            {
2197                //Display only the share name
2198                int nPos = pItem->m_sRelativePath.Find(_T('\\'));
2199                if (nPos >= 0)
2200                    pItem->m_sRelativePath = pItem->m_sRelativePath.Mid(nPos+1);
2201
2202        //Now add the item into the control
2203        if (CanDisplayNetworkItem(pItem))
2204          InsertFileItem(hParent, pItem, m_bShowSharedUsingDifferentIcon, GetIconIndex(pItem->m_sFQPath), 
2205                         GetSelIconIndex(pItem->m_sFQPath), TRUE);
2206        else
2207          delete pItem;
2208            }
2209            else
2210            {
2211        //Now add the item into the control
2212        if (CanDisplayNetworkItem(pItem))
2213          InsertFileItem(hParent, pItem, FALSE, 0, 0, FALSE);  //Indexes for the network icons do not matter here as we will be drawing over them in OnCustomDraw
2214        else
2215          delete pItem;
2216            }
2217            bGotChildren = TRUE;
2218        }
2219  }
2220  else
2221      TRACE(_T("Cannot complete network drive enumeration, Error:%d\n"), dwResult);
2222
2223    //Clean up the enumeration handle
2224    WNetCloseEnum(hEnum);   
2225
2226  //Free up the heap memory we have used
2227    delete [] lpnrDrv;     
2228
2229  //Return whether or not we added any items
2230    return bGotChildren;
2231}
2232
2233void CTreeFileCtrl::DisplayDrives(HTREEITEM hParent, BOOL bUseSetRedraw)
2234{
2235  CWaitCursor c;
2236
2237  //Speed up the job by turning off redraw
2238  if (bUseSetRedraw)
2239    SetRedraw(FALSE);
2240
2241  //Enumerate the drive letters and add them to the tree control
2242  DWORD dwDrives = GetLogicalDrives();
2243  DWORD dwMask = 1;
2244  for (int i=0; i<32; i++)
2245  {
2246    if (dwDrives & dwMask)
2247    {
2248      CString sDrive;
2249      sDrive.Format(_T("%c:\\"), i + _T('A'));
2250
2251      //check if this drive is one of the types to hide
2252      if (CanDisplayDrive(sDrive))
2253      {
2254        CTreeFileCtrlItemInfo* pItem = new CTreeFileCtrlItemInfo;
2255        pItem->m_sFQPath = sDrive;
2256        pItem->m_sRelativePath = sDrive;
2257
2258        //Insert the item into the view
2259        InsertFileItem(hParent, pItem, m_bShowSharedUsingDifferentIcon && IsShared(sDrive), GetIconIndex(sDrive), GetSelIconIndex(sDrive), TRUE);
2260      }
2261    }
2262    dwMask <<= 1;
2263  }
2264
2265  if (bUseSetRedraw)
2266    SetRedraw(TRUE);
2267}
2268
2269CString CTreeFileCtrl::GetDriveLabel(const CString& sDrive)
2270{
2271  USES_CONVERSION;
2272
2273  //Let's start with the drive letter
2274  CString sLabel(sDrive);
2275
2276  //Try to find the item directory using ParseDisplayName
2277  LPITEMIDLIST lpItem;
2278  HRESULT hr = m_pShellFolder->ParseDisplayName(NULL, NULL, T2W((LPTSTR) (LPCTSTR)sDrive), NULL, &lpItem, NULL);
2279  if (SUCCEEDED(hr))
2280  {
2281    SHFILEINFO sfi;
2282    if (SHGetFileInfo((LPCTSTR)lpItem, 0, &sfi, sizeof(sfi), SHGFI_PIDL | SHGFI_DISPLAYNAME))
2283      sLabel = sfi.szDisplayName;
2284
2285    //Free the pidl now that we are finished with it
2286    m_pMalloc->Free(lpItem);
2287  }
2288
2289  return sLabel;
2290}
2291
2292BOOL CTreeFileCtrl::IsShared(const CString& sPath)
2293{
2294  //Defer all the work to the share enumerator class
2295  return theSharedEnumerator.IsShared(sPath);
2296}
2297
2298void CTreeFileCtrl::DisplayPath(const CString& sPath, HTREEITEM hParent, BOOL bUseSetRedraw)
2299{
2300  CWaitCursor c;
2301
2302  //Speed up the job by turning off redraw
2303  if (bUseSetRedraw)
2304    SetRedraw(FALSE);
2305
2306  //Remove all the items currently under hParent
2307  HTREEITEM hChild = GetChildItem(hParent);
2308  while (hChild)
2309  {
2310    DeleteItem(hChild);
2311    hChild = GetChildItem(hParent);
2312  }
2313
2314  //Should we display the root folder
2315  if (m_bShowRootedFolder && (hParent == TVI_ROOT)) 
2316  {
2317    CTreeFileCtrlItemInfo* pItem = new CTreeFileCtrlItemInfo;
2318    pItem->m_sFQPath = m_sRootFolder;
2319    pItem->m_sRelativePath = m_sRootFolder;
2320    m_hRootedFolder = InsertFileItem(TVI_ROOT, pItem, FALSE, GetIconIndex(m_sRootFolder), GetSelIconIndex(m_sRootFolder), TRUE);
2321    Expand(m_hRootedFolder, TVE_EXPAND);
2322    return;
2323  }
2324
2325  //First find all the directories underneath sPath
2326  CSortedArray<CString, CString&> DirectoryPaths;
2327  CFileFind find1;
2328  CString sFile;
2329  if (sPath.GetAt(sPath.GetLength()-1) == _T('\\'))
2330    sFile = sPath + _T("*.*");
2331  else
2332    sFile = sPath + _T("\\*.*");
2333  BOOL bFind = find1.FindFile(sFile); 
2334  while (bFind)
2335  {
2336    bFind = find1.FindNextFile();
2337    if (CanDisplayFolder(find1))
2338    {
2339      CString sPath = find1.GetFilePath();
2340      DirectoryPaths.Add(sPath);
2341    }
2342  }
2343
2344  //Now check to see if there is any files of the specfied file mask 
2345  CSortedArray<CString, CString&> FilePaths;
2346  CFileFind find2;
2347  if (sPath.GetAt(sPath.GetLength()-1) == _T('\\'))
2348    sFile = sPath + m_sFileNameMask;
2349  else
2350    sFile = sPath + _T("\\") + m_sFileNameMask;
2351  bFind = find2.FindFile(sFile); 
2352  while (bFind)
2353  {
2354    bFind = find2.FindNextFile();
2355    if (CanDisplayFile(find2))
2356    {
2357      CString sPath = find2.GetFilePath();
2358      FilePaths.Add(sPath);
2359    } 
2360  }
2361
2362  //Now sort the 2 arrays prior to added to the tree control
2363  DirectoryPaths.SetCompareFunction(CompareByFilenameNoCase);
2364  FilePaths.SetCompareFunction(CompareByFilenameNoCase);
2365  DirectoryPaths.Sort();
2366  FilePaths.Sort();
2367
2368  //Now add all the directories to the tree control
2369  int nDirectories = DirectoryPaths.GetSize();
2370  for (int i=0; i<nDirectories; i++)
2371  {
2372    CString& sPath = DirectoryPaths.ElementAt(i);
2373    TCHAR szPath[_MAX_PATH];
2374    TCHAR szFname[_MAX_FNAME];
2375    TCHAR szExt[_MAX_EXT];
2376    _tsplitpath(sPath, NULL, NULL, szFname, szExt);
2377    _tmakepath(szPath, NULL, NULL, szFname, szExt);
2378
2379    CTreeFileCtrlItemInfo* pItem = new CTreeFileCtrlItemInfo;
2380    pItem->m_sFQPath = sPath;
2381    pItem->m_sRelativePath = szPath;
2382    InsertFileItem(hParent, pItem, m_bShowSharedUsingDifferentIcon && IsShared(sPath), GetIconIndex(sPath), GetSelIconIndex(sPath), TRUE);
2383  }
2384
2385  //And the files to the tree control (if required)
2386  int nFiles = FilePaths.GetSize();
2387  int ii;
2388  for (ii=0; ii<nFiles; ii++)
2389  {
2390    CString& sPath = FilePaths.ElementAt(ii);
2391    TCHAR szPath[_MAX_PATH];
2392    TCHAR szFname[_MAX_FNAME];
2393    TCHAR szExt[_MAX_EXT];
2394    _tsplitpath(sPath, NULL, NULL, szFname, szExt);
2395    _tmakepath(szPath, NULL, NULL, szFname, szExt);
2396
2397    CTreeFileCtrlItemInfo* pItem = new CTreeFileCtrlItemInfo;
2398    pItem->m_sFQPath = sPath;
2399    pItem->m_sRelativePath = szPath;
2400    InsertFileItem(hParent, pItem, FALSE, GetIconIndex(sPath), GetSelIconIndex(sPath), TRUE);
2401  }
2402
2403  //If no items were added then remove the "+" indicator from hParent
2404  if (nFiles == 0 && nDirectories == 0)
2405    SetHasPlusButton(hParent, FALSE);
2406
2407  //Turn back on the redraw flag
2408  if (bUseSetRedraw)
2409    SetRedraw(TRUE);
2410}
2411
2412void CTreeFileCtrl::DoExpand(HTREEITEM hItem)
2413{
2414  CTreeFileCtrlItemInfo* pItem = (CTreeFileCtrlItemInfo*) GetItemData(hItem);
2415  ASSERT(pItem);
2416
2417  //Reset the drive node if the drive is empty or the media has changed
2418  if (IsMediaValid(pItem->m_sFQPath))
2419  {
2420    //Delete the item if the path is no longer valid
2421    if (IsFolder(pItem->m_sFQPath))
2422    {
2423      //Add the new items to the tree if it does not have any child items
2424      //already
2425      if (!GetChildItem(hItem))
2426        DisplayPath(pItem->m_sFQPath, hItem);
2427
2428      //Create a thread to monitor file changes
2429      if (m_bAutoRefresh && IsDrive(pItem->m_sFQPath))
2430        CreateMonitoringThread(pItem->m_sFQPath);
2431    }
2432    else if (hItem == m_hMyComputerRoot)
2433    {
2434      //Display an hour glass as this may take some time
2435      CWaitCursor wait;
2436
2437      //Enumerate the local drive letters
2438      DisplayDrives(m_hMyComputerRoot, FALSE);
2439    }
2440    else if ((hItem == m_hNetworkRoot) || (pItem->m_pNetResource))
2441    {
2442      //Display an hour glass as this may take some time
2443      CWaitCursor wait;
2444
2445      //Enumerate the network resources
2446      EnumNetwork(hItem);
2447    }
2448    else
2449    {
2450      //Before we delete it see if we are the only child item
2451      HTREEITEM hParent = GetParentItem(hItem);
2452
2453      //Delete the item
2454      DeleteItem(hItem);
2455
2456      //Remove all the child items from the parent
2457      SetHasPlusButton(hParent, FALSE);
2458    }
2459  }
2460  else
2461  {
2462    //Display an hour glass as this may take some time
2463    CWaitCursor wait;
2464
2465    //Collapse the drive node and remove all the child items from it
2466    Expand(hItem, TVE_COLLAPSE);
2467    DeleteChildren(hItem, TRUE);
2468  }
2469}
2470
2471BOOL CTreeFileCtrl::OnItemExpanding(NMHDR* pNMHDR, LRESULT* pResult) 
2472{
2473    NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
2474  if (pNMTreeView->action == TVE_EXPAND)
2475  {
2476    BOOL bHasPlus = HasPlusButton(pNMTreeView->itemNew.hItem);
2477    BOOL bHasChildren = (GetChildItem(pNMTreeView->itemNew.hItem) != NULL);
2478
2479    if (bHasPlus && !bHasChildren)
2480      DoExpand(pNMTreeView->itemNew.hItem);
2481  }
2482  else if (pNMTreeView->action == TVE_COLLAPSE)
2483  {
2484    CTreeFileCtrlItemInfo* pItem = (CTreeFileCtrlItemInfo*) GetItemData(pNMTreeView->itemNew.hItem);
2485    ASSERT(pItem);
2486
2487    //Display an hour glass as this may take some time
2488    CWaitCursor wait;
2489
2490    CString sPath = ItemToPath(pNMTreeView->itemNew.hItem);
2491    if (IsDrive(sPath))
2492      KillNotificationThread(sPath);
2493
2494    //Collapse the node and remove all the child items from it
2495    Expand(pNMTreeView->itemNew.hItem, TVE_COLLAPSE);
2496
2497    //Never uppdate the child indicator for a network node which is not a share
2498
2499    BOOL bUpdateChildIndicator = TRUE;
2500    if (pItem->m_bNetworkNode)
2501    {
2502      if (pItem->m_pNetResource)
2503        bUpdateChildIndicator = (pItem->m_pNetResource->dwDisplayType == RESOURCEDISPLAYTYPE_SHARE);
2504      else
2505        bUpdateChildIndicator = FALSE;                             
2506    } 
2507    DeleteChildren(pNMTreeView->itemNew.hItem, bUpdateChildIndicator);
2508  }
2509
2510  *pResult = 0;
2511
2512  return FALSE; //Allow the message to be reflected again
2513}
2514
2515int CTreeFileCtrl::DeleteChildren(HTREEITEM hItem, BOOL bUpdateChildIndicator)
2516{
2517  int nCount = 0;
2518  HTREEITEM hChild = GetChildItem(hItem);
2519  while (hChild)
2520  {
2521    //Get the next sibling before we delete the current one
2522    HTREEITEM hNextItem = GetNextSiblingItem(hChild);
2523
2524    //Delete the current child
2525    DeleteItem(hChild);
2526
2527    //Get ready for the next loop
2528    hChild = hNextItem;
2529    ++nCount;
2530  }
2531
2532  //Also update its indicator to suggest that their is children
2533  if (bUpdateChildIndicator)
2534    SetHasPlusButton(hItem, (nCount != 0));
2535
2536  return nCount;
2537}
2538
2539HTREEITEM CTreeFileCtrl::PathToItem(const CString& sPath) const
2540{
2541  CString sSearch(sPath);
2542  int nSearchLength = sSearch.GetLength();
2543  if (nSearchLength == 0)
2544    return NULL;
2545
2546  //Remove trailing "\" from the path
2547  if (nSearchLength > 3 && sSearch.GetAt(nSearchLength-1) == _T('\\'))
2548    sSearch = sSearch.Left(nSearchLength-1);
2549 
2550  //Remove initial part of path if the root folder is setup
2551  int nRootLength = m_sRootFolder.GetLength();
2552  if (nRootLength)
2553  {
2554    if (sSearch.Find(m_sRootFolder) != 0)
2555    {
2556      TRACE(_T("Could not find the path %s as the root has been configued as %s\n"), sPath, m_sRootFolder);
2557      return NULL;
2558    }
2559    sSearch = sSearch.Right(sSearch.GetLength() - 1 - nRootLength);
2560  }
2561
2562  if (sSearch.IsEmpty())
2563    return NULL;
2564
2565  HTREEITEM hItemFound = TVI_ROOT;
2566  if (nRootLength && m_hRootedFolder)
2567    hItemFound = m_hRootedFolder;
2568  BOOL bDriveMatch = m_sRootFolder.IsEmpty();
2569  BOOL bNetworkMatch = m_bDisplayNetwork && ((sSearch.GetLength() > 2) && sSearch.Find(_T("\\\\")) == 0);
2570  if (bNetworkMatch)
2571  {
2572    bDriveMatch = FALSE;
2573    hItemFound = FindServersNode(m_hNetworkRoot);
2574    sSearch = sSearch.Right(sSearch.GetLength() - 2);
2575  }
2576  if (bDriveMatch)
2577  {
2578    if (m_hMyComputerRoot)
2579      hItemFound = m_hMyComputerRoot;
2580  }
2581  int nFound = sSearch.Find(_T('\\'));
2582  while (nFound != -1)
2583  {
2584    CString sMatch;
2585    if (bDriveMatch)
2586    {
2587      sMatch = sSearch.Left(nFound + 1);
2588      bDriveMatch = FALSE;
2589    }
2590    else
2591      sMatch = sSearch.Left(nFound);
2592    hItemFound = FindSibling(hItemFound, sMatch);
2593    if (hItemFound == NULL)
2594      break;
2595
2596    sSearch = sSearch.Right(sSearch.GetLength() - nFound - 1);
2597    nFound = sSearch.Find(_T('\\'));
2598  };
2599
2600  //The last item
2601  if (hItemFound)
2602  {
2603    if (sSearch.GetLength())
2604      hItemFound = FindSibling(hItemFound, sSearch);
2605  }
2606
2607  return hItemFound;
2608}
2609
2610CString CTreeFileCtrl::ItemToPath(HTREEITEM hItem) const
2611{
2612  CString sPath;
2613  if (hItem)
2614  {
2615    CTreeFileCtrlItemInfo* pItem = (CTreeFileCtrlItemInfo*) GetItemData(hItem);
2616    ASSERT(pItem);
2617    sPath = pItem->m_sFQPath;
2618  }
2619  return sPath;
2620}
2621
2622BOOL CTreeFileCtrl::OnRclick(NMHDR* /*pNMHDR*/, LRESULT* pResult) 
2623{
2624  //Work out the position of where the context menu should be
2625  CPoint p(GetCurrentMessage()->pt);
2626  CPoint pt(p);
2627  ScreenToClient(&pt);
2628  Select(HitTest(pt), TVGN_CARET);
2629    OnContextMenu(NULL, p);
2630
2631    *pResult = 0;
2632
2633  return FALSE; //Allow the message to be reflected again
2634}
2635
2636BOOL CTreeFileCtrl::OnDblclk(NMHDR* /*pNMHDR*/, LRESULT* pResult) 
2637{
2638  HTREEITEM hItem = GetSelectedItem();
2639  CPoint pt = GetCurrentMessage()->pt;
2640  ScreenToClient(&pt);
2641
2642    if (hItem && (hItem == HitTest(pt)))
2643    {
2644        if (!HasPlusButton(hItem))
2645            PostMessage(WM_COMMAND, ID_TREEFILECTRL_OPEN);
2646    }
2647   
2648    *pResult = 0;
2649
2650  return FALSE; //Allow the message to be reflected again
2651}
2652
2653//Copied from CFrameWnd::OnInitMenuPopup to provide OnUpdateCmdUI functionality
2654//in the tree control
2655void CTreeFileCtrl::OnInitMenuPopup(CMenu* pMenu, UINT /*nIndex*/, BOOL bSysMenu) 
2656{
2657    //AfxCancelModes(m_hWnd);
2658
2659    if (bSysMenu)
2660        return;     // don't support system menu
2661
2662    ASSERT(pMenu != NULL);
2663    // check the enabled state of various menu items
2664
2665    CCmdUI state;
2666    state.m_pMenu = pMenu;
2667    ASSERT(state.m_pOther == NULL);
2668    ASSERT(state.m_pParentMenu == NULL);
2669
2670    // determine if menu is popup in top-level menu and set m_pOther to
2671    //  it if so (m_pParentMenu == NULL indicates that it is secondary popup)
2672    HMENU hParentMenu;
2673    if (AfxGetThreadState()->m_hTrackingMenu == pMenu->m_hMenu)
2674        state.m_pParentMenu = pMenu;    // parent == child for tracking popup
2675    else if ((hParentMenu = ::GetMenu(m_hWnd)) != NULL)
2676    {
2677        CWnd* pParent = GetTopLevelParent();
2678            // child windows don't have menus -- need to go to the top!
2679        if (pParent != NULL &&
2680            (hParentMenu = ::GetMenu(pParent->m_hWnd)) != NULL)
2681        {
2682            int nIndexMax = ::GetMenuItemCount(hParentMenu);
2683            for (int nIndex = 0; nIndex < nIndexMax; nIndex++)
2684            {
2685                if (::GetSubMenu(hParentMenu, nIndex) == pMenu->m_hMenu)
2686                {
2687                    // when popup is found, m_pParentMenu is containing menu
2688                    state.m_pParentMenu = CMenu::FromHandle(hParentMenu);
2689                    break;
2690                }
2691            }
2692        }
2693    }
2694
2695    state.m_nIndexMax = pMenu->GetMenuItemCount();
2696    for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax;
2697      state.m_nIndex++)
2698    {
2699        state.m_nID = pMenu->GetMenuItemID(state.m_nIndex);
2700        if (state.m_nID == 0)
2701            continue; // menu separator or invalid cmd - ignore it
2702
2703        ASSERT(state.m_pOther == NULL);
2704        ASSERT(state.m_pMenu != NULL);
2705        if (state.m_nID == (UINT)-1)
2706        {
2707            // possibly a popup menu, route to first item of that popup
2708            state.m_pSubMenu = pMenu->GetSubMenu(state.m_nIndex);
2709            if (state.m_pSubMenu == NULL ||
2710                (state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0 ||
2711                state.m_nID == (UINT)-1)
2712            {
2713                continue;       // first item of popup can't be routed to
2714            }
2715            state.DoUpdate(this, FALSE);    // popups are never auto disabled
2716        }
2717        else
2718        {
2719            // normal menu item
2720            // Auto enable/disable if frame window has 'm_bAutoMenuEnable'
2721            //    set and command is _not_ a system command.
2722            state.m_pSubMenu = NULL;
2723            //state.DoUpdate(this, m_bAutoMenuEnable && state.m_nID < 0xF000);
2724      state.DoUpdate(this, TRUE && state.m_nID < 0xF000);
2725        }
2726
2727        // adjust for menu deletions and additions
2728        UINT nCount = pMenu->GetMenuItemCount();
2729        if (nCount < state.m_nIndexMax)
2730        {
2731            state.m_nIndex -= (state.m_nIndexMax - nCount);
2732            while (state.m_nIndex < nCount &&
2733                pMenu->GetMenuItemID(state.m_nIndex) == state.m_nID)
2734            {
2735                state.m_nIndex++;
2736            }
2737        }
2738        state.m_nIndexMax = nCount;
2739    }
2740}
2741
2742BOOL CTreeFileCtrl::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult) 
2743{
2744    NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
2745    *pResult = 0;
2746
2747  if (!m_bAllowDragDrop || !IsDropSource(pNMTreeView->itemNew.hItem))
2748    return FALSE; //Allow the message to be reflected again
2749
2750  m_pilDrag = CreateDragImage(pNMTreeView->itemNew.hItem);
2751  if (!m_pilDrag)
2752    return FALSE; //Allow the message to be reflected again
2753
2754  m_hItemDrag = pNMTreeView->itemNew.hItem;
2755  m_hItemDrop = NULL;
2756
2757  // Calculate the offset to the hotspot
2758  CPoint offsetPt(8,8);   // Initialize a default offset
2759
2760  CPoint dragPt = pNMTreeView->ptDrag;    // Get the Drag point
2761  UINT nHitFlags = 0;
2762  HTREEITEM htiHit = HitTest(dragPt, &nHitFlags);
2763  if (htiHit != NULL)
2764  {
2765    // The drag point has Hit an item in the tree
2766    CRect itemRect;
2767    if (GetItemRect(htiHit, &itemRect, FALSE))
2768    {
2769      // Count indent levels
2770      HTREEITEM htiParent = htiHit;
2771      int nIndentCnt = 0;
2772      while (htiParent != NULL)
2773      {
2774        htiParent = GetParentItem(htiParent);
2775        nIndentCnt++;
2776      }
2777
2778      if (!(GetStyle() & TVS_LINESATROOT)) 
2779        nIndentCnt--;
2780
2781      // Calculate the new offset
2782      offsetPt.y = dragPt.y - itemRect.top;
2783      offsetPt.x = dragPt.x - (nIndentCnt * GetIndent()) + GetScrollPos(SB_HORZ);
2784    }
2785  }
2786
2787  //Begin the dragging 
2788  m_pilDrag->BeginDrag(0, offsetPt);
2789  POINT pt = pNMTreeView->ptDrag;
2790  ClientToScreen(&pt);
2791  m_pilDrag->DragEnter(NULL, pt);
2792  SetCapture();
2793
2794  //Create the timer which is used for auto expansion
2795  m_nTimerID = SetTimer(1, 300, NULL);
2796
2797  return FALSE; //Allow the message to be reflected again
2798}
2799
2800void CTreeFileCtrl::OnMouseMove(UINT nFlags, CPoint point) 
2801{
2802    if (IsDragging())
2803  {
2804    CRect clientRect;
2805    GetClientRect(&clientRect);
2806
2807    //Draw the drag
2808    POINT pt = point;
2809    ClientToScreen(&pt);
2810    CImageList::DragMove(pt);
2811
2812    //Only select the drop item if we are in the client area
2813    HTREEITEM hItem = NULL;
2814    if (clientRect.PtInRect(point))
2815    {
2816      UINT flags;
2817      hItem = HitTest(point, &flags);
2818      if (m_hItemDrop != hItem)
2819      {
2820        CImageList::DragShowNolock(FALSE);
2821        SelectDropTarget(hItem);
2822        m_hItemDrop = hItem;
2823        CImageList::DragShowNolock(TRUE);
2824      }
2825    }
2826   
2827    if (hItem)
2828      hItem = GetDropTarget(hItem);
2829
2830    //Change the cursor to give feedback
2831    if (hItem)
2832    {
2833      if ((GetKeyState(VK_CONTROL) & 0x8000))
2834        SetCursor(m_DropCopyCursor);
2835      else
2836        SetCursor(m_DropMoveCursor);
2837    }
2838    else
2839    {
2840      if ((GetKeyState(VK_CONTROL) & 0x8000))
2841        SetCursor(m_NoDropCopyCursor);
2842      else
2843        SetCursor(m_NoDropMoveCursor);
2844    }
2845  }
2846
2847  //Let the parent class do its thing   
2848    CTreeCtrl::OnMouseMove(nFlags, point);
2849}
2850
2851HTREEITEM CTreeFileCtrl::GetDropTarget(HTREEITEM hItem)
2852{
2853  if (!IsFile(hItem) && (hItem != m_hItemDrag) && (hItem != GetParentItem(m_hItemDrag)) && IsFolder(hItem))
2854  {
2855    HTREEITEM htiParent = hItem;
2856    while ((htiParent = GetParentItem(htiParent)) != NULL)
2857    {
2858      if (htiParent == m_hItemDrag)
2859        return NULL;
2860    }
2861    return hItem;
2862  }
2863  return NULL;
2864}
2865
2866int CTreeFileCtrl::NumberOfChildItems(HTREEITEM hItem)
2867{
2868  int nChildren = 0;
2869  HTREEITEM hChild = GetChildItem(hItem);
2870  while (hChild)
2871  {
2872    ++nChildren;
2873    hChild = GetNextSiblingItem(hChild);
2874  }
2875  return nChildren;
2876}
2877
2878BOOL CTreeFileCtrl::IsDropSource(HTREEITEM hItem)
2879{
2880  return !IsDrive(hItem) && IsFile(hItem);
2881}
2882
2883BOOL CTreeFileCtrl::IsDragging()
2884{
2885  return (m_pilDrag != NULL);
2886}
2887
2888void CTreeFileCtrl::OnLButtonUp(UINT nFlags, CPoint point) 
2889{
2890  CRect clientRect;
2891  GetClientRect(&clientRect);
2892
2893  if (clientRect.PtInRect(point))
2894    EndDragging(FALSE);
2895  else
2896    EndDragging(TRUE);
2897     
2898  //Let the parent class do its thing   
2899    CTreeCtrl::OnLButtonUp(nFlags, point);
2900}
2901
2902void CTreeFileCtrl::EndDragging(BOOL bCancel)
2903{
2904  if (IsDragging())
2905  {
2906    //Kill the timer that is being used
2907    KillTimer(m_nTimerID);
2908
2909    CImageList::DragLeave(this);
2910    CImageList::EndDrag();
2911    ReleaseCapture();
2912
2913    //Delete the drag image list
2914    delete m_pilDrag;
2915    m_pilDrag = NULL;
2916
2917    //Remove drop target highlighting
2918    SelectDropTarget(NULL);
2919
2920    //Find out where we are dropping
2921    m_hItemDrop = GetDropTarget(m_hItemDrop);
2922    if (m_hItemDrop == NULL)
2923      return;
2924
2925    if (!bCancel)
2926    {
2927      //Also need to make the change on disk
2928      CString sFromPath = ItemToPath(m_hItemDrag);
2929      CString sToPath = ItemToPath(m_hItemDrop);
2930
2931      int nFromLength = sFromPath.GetLength();
2932      int nToLength = sToPath.GetLength();
2933      SHFILEOPSTRUCT shfo;
2934      ZeroMemory(&shfo, sizeof(SHFILEOPSTRUCT));
2935      shfo.hwnd = GetSafeHwnd();
2936
2937      if ((GetKeyState(VK_CONTROL) & 0x8000))
2938        shfo.wFunc = FO_COPY;
2939      else
2940        shfo.wFunc = FO_MOVE;
2941
2942      shfo.fFlags = FOF_SILENT | FOF_NOCONFIRMMKDIR;
2943      //Undo is not allowed if the SHIFT key is held down
2944      if (!(GetKeyState(VK_SHIFT) & 0x8000))
2945        shfo.fFlags |= FOF_ALLOWUNDO;
2946
2947      TCHAR* pszFrom = new TCHAR[nFromLength + 2];
2948      _tcscpy(pszFrom, sFromPath);
2949      pszFrom[nFromLength+1] = _T('\0');
2950      shfo.pFrom = pszFrom;
2951
2952      TCHAR* pszTo = new TCHAR[nToLength + 2];
2953      _tcscpy(pszTo, sToPath);
2954      pszTo[nToLength+1] = _T('\0');
2955      shfo.pTo = pszTo;
2956
2957      BOOL bOldAutoRefresh = m_bAutoRefresh;
2958      m_bAutoRefresh = FALSE; //Prevents us from getting thread notifications
2959
2960      //Let the shell perform the actual deletion
2961      BOOL bSuccess = ((SHFileOperation(&shfo) == 0) && (shfo.fAnyOperationsAborted == FALSE));
2962
2963      m_bAutoRefresh = bOldAutoRefresh;
2964
2965      //Free up the memory we had allocated
2966      delete [] pszFrom;
2967      delete [] pszTo;
2968
2969      if (bSuccess)
2970      {
2971        //Only copy the item in the tree if there is not an item with the same
2972        //text under m_hItemDrop
2973        CString sText = GetItemText(m_hItemDrag);
2974        if (!HasChildWithText(m_hItemDrop, sText))
2975        {
2976          Expand(m_hItemDrop, TVE_COLLAPSE);
2977          DeleteChildren(m_hItemDrop, FALSE);
2978          SelectItem(m_hItemDrop);
2979
2980          //Update the children indicator for the folder we just dropped into
2981          SetHasPlusButton(m_hItemDrop, TRUE);
2982        }
2983
2984        if (shfo.wFunc == FO_MOVE)
2985        {
2986          //Get the parent of the item we moved prior to deleting it
2987          HTREEITEM hParent = GetParentItem(m_hItemDrag);
2988
2989          //Delete the item we just moved
2990          DeleteItem(m_hItemDrag);
2991
2992          //Update the children indicator for the item we just dragged from
2993          BOOL bHasChildren = (GetChildItem(hParent) != NULL);
2994          if (hParent && !bHasChildren)
2995            SetHasPlusButton(hParent, FALSE);
2996        }
2997      }
2998    }
2999  }
3000}
3001
3002BOOL CTreeFileCtrl::HasChildWithText(HTREEITEM hParent, const CString& sText)
3003{
3004  HTREEITEM hChild = GetChildItem(hParent);
3005  while (hChild)
3006  {
3007    CString sItemText = GetItemText(hChild);
3008    if (sItemText.CompareNoCase(sText) == 0)
3009      return TRUE;
3010    hChild = GetNextSiblingItem(hChild);
3011  }
3012  return FALSE;
3013}
3014
3015HTREEITEM CTreeFileCtrl::CopyItem(HTREEITEM hItem, HTREEITEM htiNewParent, HTREEITEM htiAfter)
3016{
3017  //Get the details of the item to copy
3018  TV_INSERTSTRUCT tvstruct;
3019  tvstruct.item.hItem = hItem;
3020  tvstruct.item.mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
3021  GetItem(&tvstruct.item);
3022  CString sText = GetItemText(hItem);
3023  tvstruct.item.cchTextMax = sText.GetLength();
3024  tvstruct.item.pszText = sText.GetBuffer(tvstruct.item.cchTextMax);
3025
3026  //Make a copy of the item data we are carying around
3027  CTreeFileCtrlItemInfo* pOldInfo = (CTreeFileCtrlItemInfo*) tvstruct.item.lParam;
3028  tvstruct.item.lParam = (LPARAM) new CTreeFileCtrlItemInfo(*pOldInfo);
3029
3030  //Insert the item at the proper location
3031  tvstruct.hParent = htiNewParent;
3032  tvstruct.hInsertAfter = htiAfter;
3033  tvstruct.item.mask |= TVIF_TEXT;
3034  HTREEITEM hNewItem = InsertItem(&tvstruct);
3035
3036  //Don't forget to release the CString buffer 
3037  sText.ReleaseBuffer();
3038
3039  return hNewItem;
3040}
3041
3042HTREEITEM CTreeFileCtrl::CopyBranch(HTREEITEM htiBranch, HTREEITEM htiNewParent, HTREEITEM htiAfter)
3043{
3044  HTREEITEM hNewItem = CopyItem(htiBranch, htiNewParent, htiAfter);
3045  HTREEITEM hChild = GetChildItem(htiBranch);
3046  while (hChild != NULL)
3047  {
3048    //recursively transfer all the items
3049    CopyBranch(hChild, hNewItem);
3050    hChild = GetNextSiblingItem(hChild);
3051  }
3052  return hNewItem;
3053}
3054
3055void CTreeFileCtrl::OnTimer(UINT_PTR nIDEvent) 
3056{
3057    if (nIDEvent != m_nTimerID)
3058  {
3059      CTreeCtrl::OnTimer(nIDEvent);
3060    return;
3061  }
3062
3063  //Show the dragging effect
3064  POINT pt;
3065  GetCursorPos(&pt);
3066  RECT rect;
3067  GetClientRect(&rect);
3068  ClientToScreen(&rect);
3069  CImageList::DragMove(pt);
3070
3071  HTREEITEM hFirstItem = GetFirstVisibleItem();
3072  CRect ItemRect;
3073  GetItemRect(hFirstItem, &ItemRect, FALSE);
3074  if (pt.y < (rect.top + (ItemRect.Height()*2)) && pt.y > rect.top)
3075  {
3076    //we need to scroll up
3077    CImageList::DragShowNolock(FALSE);
3078    SendMessage(WM_VSCROLL, SB_LINEUP);
3079    EnsureVisible(hFirstItem);
3080    SelectDropTarget(hFirstItem);
3081    m_hItemDrop = hFirstItem;
3082    CImageList::DragShowNolock(TRUE);
3083  }
3084  else if (pt.y > (rect.bottom - (ItemRect.Height()*2)) && pt.y < rect.bottom)
3085  {
3086    //we need to scroll down
3087    CImageList::DragShowNolock(FALSE);
3088    SendMessage(WM_VSCROLL, SB_LINEDOWN);
3089    HTREEITEM hLastItem = hFirstItem;
3090    int nCount = GetVisibleCount();
3091    for (int i=0; i<(nCount-1); i++)
3092      hLastItem = GetNextVisibleItem(hLastItem);
3093    SelectDropTarget(hLastItem);
3094    EnsureVisible(hLastItem);
3095    m_hItemDrop = hLastItem;
3096    CImageList::DragShowNolock(TRUE);
3097  }
3098
3099  //Expand the item if the timer ticks has expired
3100  if (m_TimerTicks == 3)
3101  {
3102    m_TimerTicks = 0;
3103    Expand(m_hItemDrop, TVE_EXPAND);
3104  }
3105
3106  //Expand the selected item if it is collapsed and
3107  //the timeout has occurred
3108  TV_ITEM tvItem;
3109  tvItem.hItem = m_hItemDrop;
3110  tvItem.mask = TVIF_HANDLE | TVIF_CHILDREN | TVIF_STATE;
3111  tvItem.stateMask = TVIS_EXPANDED;
3112  GetItem(&tvItem);
3113  if (tvItem.cChildren && ((tvItem.state & TVIS_EXPANDED) == 0))
3114  {
3115    m_TimerTicks++;
3116  }
3117}
3118
3119void CTreeFileCtrl::OnBack() 
3120{
3121  int nSize = m_PrevItems.GetSize();
3122  if (nSize)
3123  {
3124    HTREEITEM hOldItem = GetSelectedItem();
3125    HTREEITEM hNewItem = (HTREEITEM) m_PrevItems.GetAt(nSize - 1);
3126
3127    //Select the previous item
3128    m_bUpdatingHistorySelection = TRUE;
3129    m_PrevItems.RemoveAt(nSize - 1);
3130    SelectItem(hNewItem);
3131    EnsureVisible(hNewItem);
3132    m_bUpdatingHistorySelection = FALSE;
3133
3134    //Add the old item to the next stack
3135    m_NextItems.Add(hOldItem);
3136  }
3137}
3138
3139void CTreeFileCtrl::OnUpdateBack(CCmdUI* pCmdUI) 
3140{
3141    pCmdUI->Enable(CanGoBack());
3142}
3143
3144void CTreeFileCtrl::OnForward() 
3145{
3146  int nSize = m_NextItems.GetSize();
3147  if (nSize)
3148  {
3149    HTREEITEM hOldItem = GetSelectedItem();
3150    HTREEITEM hNewItem = (HTREEITEM) m_NextItems.GetAt(nSize - 1);
3151
3152    //Select the previous item
3153    m_bUpdatingHistorySelection = TRUE;
3154    m_NextItems.RemoveAt(nSize - 1);
3155    SelectItem(hNewItem);
3156    EnsureVisible(hNewItem);
3157    m_bUpdatingHistorySelection = FALSE;
3158
3159    //Add the old item to the prev stack
3160    m_PrevItems.Add(hOldItem);
3161  }
3162}
3163
3164void CTreeFileCtrl::OnUpdateForward(CCmdUI* pCmdUI) 
3165{
3166    pCmdUI->Enable(CanGoForward()); 
3167}
3168
3169BOOL CTreeFileCtrl::GoBack()
3170{
3171  BOOL bSuccess = FALSE;
3172  if (m_PrevItems.GetSize())
3173  {
3174    SendMessage(WM_COMMAND, ID_TREEFILECTRL_BACK);
3175    bSuccess = TRUE;
3176  }
3177  return bSuccess;
3178}
3179
3180BOOL CTreeFileCtrl::GoForward()
3181{
3182  BOOL bSuccess = FALSE;
3183  if (m_NextItems.GetSize())
3184  {
3185    SendMessage(WM_COMMAND, ID_TREEFILECTRL_FORWARD);
3186    bSuccess = TRUE;
3187  }
3188  return bSuccess;
3189}
3190
3191void CTreeFileCtrl::SetMaxHistory(int nMaxHistory)
3192{
3193  m_nMaxHistory = nMaxHistory;
3194
3195  //Shrink the prev array if necessary
3196  INT_PTR nCurItems = m_PrevItems.GetSize();
3197  if (nCurItems > m_nMaxHistory)
3198  {
3199    int nItemsToDelete = nCurItems - m_nMaxHistory;
3200    for (int i=0; i<nItemsToDelete; i++)
3201      m_PrevItems.RemoveAt(nCurItems - i - 1);
3202  }
3203
3204  //Shrink the next array if necessary
3205  nCurItems = m_NextItems.GetSize();
3206  if (nCurItems > m_nMaxHistory)
3207  {
3208    int nItemsToDelete = nCurItems - m_nMaxHistory;
3209    for (int i=0; i<nItemsToDelete; i++)
3210      m_NextItems.RemoveAt(nCurItems - i - 1);
3211  }
3212}
3213
3214int CTreeFileCtrl::GetBackSize() const
3215{
3216  return m_PrevItems.GetSize();
3217}
3218
3219CString CTreeFileCtrl::GetBackItemText(int nBack) const
3220{
3221  ASSERT(nBack < GetBackSize());
3222  HTREEITEM hItem = (HTREEITEM) m_PrevItems.GetAt(nBack);
3223  return ItemToPath(hItem);
3224}
3225
3226int CTreeFileCtrl::GetForwardSize() const
3227{
3228  return m_NextItems.GetSize();
3229}
3230
3231CString CTreeFileCtrl::GetForwardItemText(int nForward) const
3232{
3233  ASSERT(nForward < GetForwardSize());
3234  HTREEITEM hItem = (HTREEITEM) m_NextItems.GetAt(nForward);
3235  return ItemToPath(hItem);
3236}
3237
3238void CTreeFileCtrl::KillNotificationThread(const CString& sPath)
3239{
3240    //Kill all the running file change notification threads
3241  int nThreads = m_ThreadInfo.GetSize();
3242  for (int i=0; i<nThreads; i++)
3243  {
3244    CTreeFileCtrlThreadInfo* pInfo = m_ThreadInfo.GetAt(i);
3245    if (pInfo->m_sPath.CompareNoCase(sPath) == 0)
3246    {
3247      TRACE(_T("Killing monitoring thread for %s\n"), sPath);
3248
3249      //Signal the worker thread to exit and wait for it to return
3250      pInfo->m_TerminateEvent.SetEvent();
3251      WaitForSingleObject(pInfo->m_pThread->m_hThread, INFINITE);
3252
3253      delete pInfo;
3254      m_ThreadInfo.RemoveAt(i);
3255      return;
3256    }
3257  }
3258}
3259
3260void CTreeFileCtrl::KillNotificationThreads()
3261{
3262    //Kill all the running file change notification threads
3263  int nThreads = m_ThreadInfo.GetSize();
3264  if (nThreads)
3265  {
3266    HANDLE* pThreads = new HANDLE[nThreads];
3267    for (int i=0; i<nThreads; i++)
3268    {
3269      CTreeFileCtrlThreadInfo* pInfo = m_ThreadInfo.GetAt(i);
3270      pThreads[i] = pInfo->m_pThread->m_hThread;
3271      pInfo->m_TerminateEvent.SetEvent();
3272    }
3273
3274    //wait for the threads to exit
3275    ::WaitForMultipleObjects(nThreads, pThreads, TRUE, INFINITE);
3276
3277    //Free up all the objects we have
3278    delete [] pThreads;
3279   
3280    for (int i=0; i<nThreads; i++)
3281      delete m_ThreadInfo.GetAt(i);
3282    m_ThreadInfo.RemoveAll();
3283
3284    //Reset the event
3285    m_TerminateEvent.ResetEvent();
3286  }
3287}
3288
3289void CTreeFileCtrl::OnDestroy() 
3290{
3291  KillNotificationThreads();
3292
3293  //Remove all the items from the tree control
3294  //This ensures that all the heap memory we
3295  //have used in the item datas is freed
3296  Clear();
3297
3298  //Let the parent class do its thing
3299    CTreeCtrl::OnDestroy();
3300}
3301
3302LRESULT CTreeFileCtrl::OnChange(WPARAM wParam, LPARAM /*lParam*/)
3303{
3304  //Return immediately if auto refresh is turned of
3305  if (!m_bAutoRefresh)
3306    return 0L;
3307
3308  //Validate our parameters
3309  CTreeFileCtrlThreadInfo* pInfo = m_ThreadInfo.GetAt(wParam);
3310  ASSERT(pInfo);
3311
3312  //Trace message which is helpful for diagnosing autorefresh
3313  TRACE(_T("Refreshing %s due to change\n"), pInfo->m_sPath), 
3314
3315  SetRedraw(FALSE);
3316
3317  //Remember what was selected
3318  HTREEITEM hSelItem = GetSelectedItem();
3319  CString sItem;
3320  BOOL bExpanded = FALSE;
3321  if (hSelItem)
3322  {
3323    sItem  = ItemToPath(hSelItem);
3324    bExpanded = IsExpanded(hSelItem); 
3325  }
3326
3327  //Cause the redisplay
3328  HTREEITEM hItem = PathToItem(pInfo->m_sPath);
3329  DisplayPath(pInfo->m_sPath, hItem, TRUE);
3330
3331  //Reselect the initially selected item
3332  if (sItem.GetLength())
3333    hSelItem = SetSelectedPath(sItem, bExpanded);
3334
3335  //Turn back on the redraw flag
3336  SetRedraw(TRUE);
3337
3338  return 0L;
3339}
3340
3341void CTreeFileCtrl::SetAutoRefresh(BOOL bAutoRefresh) 
3342{
3343  //Since it can be touched by more than one thead
3344  InterlockedExchange((LPLONG) &m_bAutoRefresh, bAutoRefresh); 
3345
3346  Refresh(); //Force the monitoring threads to be recreated
3347}
3348
3349void CTreeFileCtrl::CollapseExpandBranch( HTREEITEM hti, int nAction)
3350{
3351  if (ItemHasChildren(hti))
3352  {
3353    Expand(hti, nAction);
3354    hti = GetChildItem(hti);               
3355    while (hti)
3356    {
3357      CollapseExpandBranch(hti, nAction);
3358      hti = GetNextSiblingItem(hti);
3359    }
3360  }
3361}
3362
3363void CTreeFileCtrl::Collapseall() 
3364{
3365  HTREEITEM hti = GetRootItem();       
3366  while (hti)
3367  {
3368    CollapseExpandBranch(hti, TVE_COLLAPSE);
3369    hti = GetNextSiblingItem(hti);
3370  }   
3371}
3372
3373void CTreeFileCtrl::Expandall() 
3374{
3375  HTREEITEM hti = GetRootItem();       
3376  while (hti)
3377  {
3378    CollapseExpandBranch(hti, TVE_EXPAND);
3379    hti = GetNextSiblingItem(hti);
3380  }
3381}
3382
3383void CTreeFileCtrl::Clear()
3384{
3385  //Delete all the items
3386  DeleteAllItems();
3387
3388  //Reset the member variables we have
3389  m_hMyComputerRoot = NULL;
3390  m_hNetworkRoot = NULL;
3391  m_hRootedFolder = NULL;
3392}
3393
3394void CTreeFileCtrl::PreSubclassWindow() 
3395{
3396  //Let the base class do its thing
3397    CTreeCtrl::PreSubclassWindow();
3398
3399  //Get a pointer to IShellFolder and IMalloc
3400  ASSERT(m_pShellFolder == NULL);
3401  VERIFY(SUCCEEDED(SHGetDesktopFolder(&m_pShellFolder)));
3402  VERIFY(SUCCEEDED(SHGetMalloc(&m_pMalloc)));
3403
3404  //Load up the cursors we need
3405  CWinApp* pApp = AfxGetApp();
3406  ASSERT(pApp);
3407  m_NoDropCopyCursor = pApp->LoadCursor(IDR_TREEFILECTRL_NO_DROPCOPY);
3408  VERIFY(m_NoDropCopyCursor);
3409  m_DropCopyCursor = pApp->LoadCursor(IDR_TREEFILECTRL_DROPCOPY);
3410  VERIFY(m_DropCopyCursor);
3411  m_NoDropMoveCursor = pApp->LoadCursor(IDR_TREEFILECTRL_NO_DROPMOVE);
3412  VERIFY(m_NoDropMoveCursor);
3413  m_DropMoveCursor = pApp->LoadStandardCursor(IDC_ARROW);
3414  VERIFY(m_DropMoveCursor);
3415
3416  //Load up the bitmaps used to supplement the system image list
3417  VERIFY(m_ilNetwork.Create(IDB_FILETREECTRL_NETWORK, 16, 1, RGB(255, 0, 255)));
3418}
3419
3420BOOL CTreeFileCtrl::OnDeleteItem(NMHDR* pNMHDR, LRESULT* pResult) 
3421{
3422    NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
3423  if (pNMTreeView->itemOld.hItem != TVI_ROOT)
3424  {
3425    CTreeFileCtrlItemInfo* pItem = (CTreeFileCtrlItemInfo*) pNMTreeView->itemOld.lParam;
3426    if (pItem->m_pNetResource)
3427    {
3428      free(pItem->m_pNetResource->lpLocalName);
3429      free(pItem->m_pNetResource->lpRemoteName);
3430      free(pItem->m_pNetResource->lpComment);
3431      free(pItem->m_pNetResource->lpProvider);
3432      delete pItem->m_pNetResource;
3433    }
3434    delete pItem;
3435  }
3436
3437    *pResult = 0;
3438
3439  return FALSE; //Allow the message to be reflected again
3440}
3441
3442void DDX_FileTreeValue(CDataExchange* pDX, CTreeFileCtrl& ctrlFileTree, CString& sItem)
3443{
3444  if (pDX->m_bSaveAndValidate)
3445    sItem = ctrlFileTree.GetSelectedPath();
3446  else
3447    ctrlFileTree.SetSelectedPath(sItem);
3448}
3449
3450
Note: See TracBrowser for help on using the repository browser.