/* POSSUM_MOD_BEGIN 

Most of this code was stolen/adapted/massacred from...

Module : FileTreeCtrl.cpp
Purpose: Interface for an MFC class which provides a tree control similiar 
         to the left hand side of explorer

Copyright (c) 1999 - 2003 by PJ Naughter.  (Web: www.naughter.com, Email: pjna@naughter.com)
*/

#include "stdafx.h"
#include "../client/DCPlusPlus.h"
#include "../client/Util.h"
#include "../client/ShareManager.h"
#include "Resource.h"
#include "../client/ResourceManager.h"
#include "../client/versiontemplate.h"
#include "HashProgressDlg.h"
#include "LineDlg.h"

#include "foldertree.h"

//Pull in the WNet Lib automatically
#pragma comment(lib, "mpr.lib")

FolderTreeItemInfo::FolderTreeItemInfo()
{
  m_pNetResource = NULL;
  m_bNetworkNode = false;
}

FolderTreeItemInfo::FolderTreeItemInfo(const FolderTreeItemInfo& ItemInfo)
{
  m_sFQPath       = ItemInfo.m_sFQPath;
  m_sRelativePath = ItemInfo.m_sRelativePath;
  m_bNetworkNode  = ItemInfo.m_bNetworkNode;
  m_pNetResource = new NETRESOURCE;
  if (ItemInfo.m_pNetResource)
  {
    //Copy the direct member variables of NETRESOURCE
    CopyMemory(m_pNetResource, ItemInfo.m_pNetResource, sizeof(NETRESOURCE));

    //Duplicate the strings which are stored in NETRESOURCE as pointers
    if (ItemInfo.m_pNetResource->lpLocalName)
		  m_pNetResource->lpLocalName	= _tcsdup(ItemInfo.m_pNetResource->lpLocalName);
    if (ItemInfo.m_pNetResource->lpRemoteName)
		  m_pNetResource->lpRemoteName = _tcsdup(ItemInfo.m_pNetResource->lpRemoteName);
    if (ItemInfo.m_pNetResource->lpComment)
		  m_pNetResource->lpComment	= _tcsdup(ItemInfo.m_pNetResource->lpComment);
    if (ItemInfo.m_pNetResource->lpProvider)
		  m_pNetResource->lpProvider	= _tcsdup(ItemInfo.m_pNetResource->lpProvider);
  }
  else
    memzero(m_pNetResource, sizeof(NETRESOURCE));
}

SystemImageList::SystemImageList()
{
	//Get the temp directory. This is used to then bring back the system image list
	TCHAR pszTempDir[_MAX_PATH];
	GetTempPath(_MAX_PATH, pszTempDir);
	TCHAR pszDrive[_MAX_DRIVE + 1];
	_tsplitpath(pszTempDir, pszDrive, NULL, NULL, NULL);
	int nLen = _tcslen(pszDrive);
	if (pszDrive[nLen-1] != _T('\\'))
		_tcscat(pszDrive, _T("\\"));

	//Attach to the system image list
	SHFILEINFO sfi;
	HIMAGELIST hSystemImageList = (HIMAGELIST) SHGetFileInfo(pszTempDir, 0, &sfi, sizeof(SHFILEINFO),
															SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
	m_ImageList.Attach(hSystemImageList);
}

SystemImageList* SystemImageList::getInstance()
{
	static SystemImageList instance;
	return &instance;
}

SystemImageList::~SystemImageList()
{
	//Detach from the image list to prevent problems on 95/98 where
	//the system image list is shared across processes
	m_ImageList.Detach();
}

ShareEnumerator::ShareEnumerator()
{
	//Set out member variables to defaults
	m_pNTShareEnum = NULL;
	m_pWin9xShareEnum = NULL;
	m_pNTBufferFree = NULL;
	m_pNTShareInfo = NULL;
	m_pWin9xShareInfo = NULL;
	m_pWin9xShareInfo = NULL;
	m_hNetApi = NULL;
	m_dwShares = 0;

	//Determine if we are running Windows NT or Win9x
	OSVERSIONINFO osvi;
	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	m_bWinNT = (GetVersionEx(&osvi) && osvi.dwPlatformId == VER_PLATFORM_WIN32_NT);
	if (m_bWinNT)
	{
		//Load up the NETAPI dll
		m_hNetApi = LoadLibrary(_T("NETAPI32.dll"));
		if (m_hNetApi)
		{
		//Get the required function pointers
		m_pNTShareEnum = (NT_NETSHAREENUM*) GetProcAddress(m_hNetApi, "NetShareEnum");
		m_pNTBufferFree = (NT_NETAPIBUFFERFREE*) GetProcAddress(m_hNetApi, "NetApiBufferFree");
		}
	}
	else
	{
		//Load up the NETAPI dll
		m_hNetApi = LoadLibrary(_T("SVRAPI.dll"));
		if (m_hNetApi)
		{
		//Get the required function pointer
		m_pWin9xShareEnum = (WIN9X_NETSHAREENUM*) GetProcAddress(m_hNetApi, "NetShareEnum");
		}
	}

	//Update the array of shares we know about
	Refresh();
}

ShareEnumerator::~ShareEnumerator()
{
	if (m_bWinNT)
	{
		//Free the buffer if valid
		if (m_pNTShareInfo)
		m_pNTBufferFree(m_pNTShareInfo);
	}
	else
		//Free up the heap memory we have used
			delete [] m_pWin9xShareInfo;

	//Free the dll now that we are finished with it
	if (m_hNetApi)
	{
		FreeLibrary(m_hNetApi);
		m_hNetApi = NULL;
	}
}

void ShareEnumerator::Refresh()
{
	m_dwShares = 0;
	if (m_bWinNT)
	{
		//Free the buffer if valid
		if (m_pNTShareInfo)
			m_pNTBufferFree(m_pNTShareInfo);

		//Call the function to enumerate the shares
		if (m_pNTShareEnum)
		{
			DWORD dwEntriesRead = 0;
			m_pNTShareEnum(NULL, 502, (LPBYTE*) &m_pNTShareInfo, MAX_PREFERRED_LENGTH, &dwEntriesRead, &m_dwShares, NULL);
		}
	}
	else
	{
		//Free the buffer if valid
		if (m_pWin9xShareInfo)
			delete [] m_pWin9xShareInfo;

		//Call the function to enumerate the shares
		if (m_pWin9xShareEnum)
		{
			//Start with a reasonably sized buffer
			unsigned short cbBuffer = 1024;
			bool bNeedMoreMemory = true;
			bool bSuccess = false;
			while (bNeedMoreMemory && !bSuccess)
			{
				unsigned short nTotalRead = 0;
				m_pWin9xShareInfo = (FolderTree_share_info_50*) new BYTE[cbBuffer];
				memzero(m_pWin9xShareInfo, cbBuffer);
				unsigned short nShares = 0;
				NET_API_STATUS nStatus = m_pWin9xShareEnum(NULL, 50, (char FAR *)m_pWin9xShareInfo, cbBuffer, (unsigned short FAR *)&nShares, (unsigned short FAR *)&nTotalRead);
				if (nStatus == ERROR_MORE_DATA)
				{
					//Free up the heap memory we have used
					delete [] m_pWin9xShareInfo;

					//And double the size, ready for the next loop around
					cbBuffer *= 2;
				}
				else if (nStatus == NERR_Success)
				{
					m_dwShares = nShares;
					bSuccess = true;
				}
				else
					bNeedMoreMemory = false;
			}
		}
	}
}

bool ShareEnumerator::IsShared(const tstring& sPath)
{
	//Assume the item is not shared
	bool bShared = false;

	if (m_bWinNT)
	{
		if (m_pNTShareInfo)
		{
			for (DWORD i=0; i<m_dwShares && !bShared; i++)
			{
				tstring sShare(m_pNTShareInfo[i].shi502_path);
				bShared = (stricmp(sPath, sShare) == 0) && ((m_pNTShareInfo[i].shi502_type == STYPE_DISKTREE) || ((m_pNTShareInfo[i].shi502_type == STYPE_PRINTQ)));
			}
		}
	}
	else
	{
		if (m_pWin9xShareInfo)
		{
			for (DWORD i=0; i<m_dwShares && !bShared; i++)
			{
				tstring sShare(Text::toT(m_pWin9xShareInfo[i].shi50_path));
				bShared = (stricmp(sPath, sShare) == 0) &&
						((m_pWin9xShareInfo[i].shi50_type == STYPE_DISKTREE) || ((m_pWin9xShareInfo[i].shi50_type == STYPE_PRINTQ)));
			}
		}
	}

	return bShared;
}

FolderTree::FolderTree()
{
	m_dwFileHideFlags = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM |
						FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_TEMPORARY;
	m_bShowCompressedUsingDifferentColor = true;
	m_rgbCompressed = RGB(0, 0, 255);
	m_bShowEncryptedUsingDifferentColor = true;
	m_rgbEncrypted = RGB(255, 0, 0);
	m_dwDriveHideFlags = 0;
	m_hNetworkRoot = NULL;
	m_bShowSharedUsingDifferentIcon = true;
	for (int i=0; i<26; i++)
		m_dwMediaID[i] = 0xFFFFFFFF;

	SHGetMalloc(&m_pMalloc);
	SHGetDesktopFolder(&m_pShellFolder);
	m_bDisplayNetwork = true;
	m_dwNetworkItemTypes = RESOURCETYPE_ANY;
	m_hMyComputerRoot = NULL;
	m_bShowMyComputer = true;
	m_bShowRootedFolder = false;
	m_hRootedFolder = NULL;
	m_bShowDriveLabels = true;
	m_pStaticCtrl = NULL;

	m_nShareSizeDiff = 0;
	m_bDirty = false;
}

FolderTree::~FolderTree()
{
	m_pShellFolder->Release();
	m_pMalloc->Release();
}

void FolderTree::PopulateTree()
{
	//attach the image list to the tree control
	SetImageList(*SystemImageList::getInstance()->getImageList(), TVSIL_NORMAL);

	//Force a refresh
	Refresh();

	Expand(m_hMyComputerRoot, TVE_EXPAND );
}

void FolderTree::Refresh()
{
	//Just in case this will take some time
	//CWaitCursor wait;

	SetRedraw(FALSE);

	//Get the item which is currently selected
	HTREEITEM hSelItem = GetSelectedItem();
	tstring sItem;
	bool bExpanded = false;
	if (hSelItem)
	{
		sItem = ItemToPath(hSelItem);
		bExpanded = IsExpanded(hSelItem);
	}

	theSharedEnumerator.Refresh();

	//Remove all nodes that currently exist
	Clear();

	//Display the folder items in the tree
	if (m_sRootFolder.empty())
	{
		//Should we insert a "My Computer" node
		if (m_bShowMyComputer)
		{
			FolderTreeItemInfo* pItem = new FolderTreeItemInfo;
			pItem->m_bNetworkNode = false;
			int nIcon = 0;
			int nSelIcon = 0;

			//Get the localized name and correct icons for "My Computer"
			LPITEMIDLIST lpMCPidl;
			if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &lpMCPidl)))
			{
				SHFILEINFO sfi;
				if (SHGetFileInfo((LPCTSTR)lpMCPidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL | SHGFI_DISPLAYNAME))
				pItem->m_sRelativePath = sfi.szDisplayName;
				nIcon = GetIconIndex(lpMCPidl);
				nSelIcon = GetSelIconIndex(lpMCPidl);

				//Free up the pidl now that we are finished with it
				//ASSERT(m_pMalloc);
				m_pMalloc->Free(lpMCPidl);
				m_pMalloc->Release();
			}

			//Add it to the tree control
			m_hMyComputerRoot = InsertFileItem(TVI_ROOT, pItem, false, nIcon, nSelIcon, false);
			SetHasSharedChildren(m_hMyComputerRoot);
		}

		//Display all the drives
		if (!m_bShowMyComputer)
			DisplayDrives(TVI_ROOT, false);

		//Also add network neighborhood if requested to do so
		if (m_bDisplayNetwork)
		{
			FolderTreeItemInfo* pItem = new FolderTreeItemInfo;
			pItem->m_bNetworkNode = true;
			int nIcon = 0;
			int nSelIcon = 0;

			//Get the localized name and correct icons for "Network Neighborhood"
			LPITEMIDLIST lpNNPidl;
			if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_NETWORK, &lpNNPidl)))
			{
				SHFILEINFO sfi;
				if (SHGetFileInfo((LPCTSTR)lpNNPidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL | SHGFI_DISPLAYNAME))
				pItem->m_sRelativePath = sfi.szDisplayName;
				nIcon = GetIconIndex(lpNNPidl);
				nSelIcon = GetSelIconIndex(lpNNPidl);

				//Free up the pidl now that we are finished with it
				//ASSERT(m_pMalloc);
				m_pMalloc->Free(lpNNPidl);
				m_pMalloc->Release();
			}

			//Add it to the tree control
			m_hNetworkRoot = InsertFileItem(TVI_ROOT, pItem, false, nIcon, nSelIcon, false);
			SetHasSharedChildren(m_hNetworkRoot);
		}
	}
	else
	{
		DisplayPath(m_sRootFolder, TVI_ROOT, false);
	}

	//Reselect the initially selected item
	if (hSelItem)
		SetSelectedPath(sItem, bExpanded);

	//Turn back on the redraw flag
	SetRedraw(true);
}

tstring FolderTree::ItemToPath(HTREEITEM hItem) const
{
	tstring sPath;
	if(hItem)
	{
		FolderTreeItemInfo* pItem = (FolderTreeItemInfo*) GetItemData(hItem);
		//ASSERT(pItem);
		sPath = pItem->m_sFQPath;
	}
	return sPath;
}

bool FolderTree::IsExpanded(HTREEITEM hItem)
{
	TVITEM tvItem;
	tvItem.hItem = hItem;
	tvItem.mask = TVIF_HANDLE | TVIF_STATE;
	return (GetItem(&tvItem) && (tvItem.state & TVIS_EXPANDED));
}

void FolderTree::Clear()
{
	//Delete all the items
	DeleteAllItems();

	//Reset the member variables we have
	m_hMyComputerRoot = NULL;
	m_hNetworkRoot = NULL;
	m_hRootedFolder = NULL;
}

int FolderTree::GetIconIndex(HTREEITEM hItem)
{
	TV_ITEM tvi;
	memzero(&tvi, sizeof(TV_ITEM));
	tvi.mask = TVIF_IMAGE;
	tvi.hItem = hItem;
	if (GetItem(&tvi))
		return tvi.iImage;
	else
		return -1;
}

int FolderTree::GetIconIndex(const tstring &sFilename)
{
	//Retreive the icon index for a specified file/folder
	SHFILEINFO sfi;
	memzero(&sfi, sizeof(SHFILEINFO));
	SHGetFileInfo(sFilename.c_str(), 0, &sfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
	return sfi.iIcon;
}

int FolderTree::GetIconIndex(LPITEMIDLIST lpPIDL)
{
	SHFILEINFO sfi;
	memzero(&sfi, sizeof(SHFILEINFO));
	SHGetFileInfo((LPCTSTR)lpPIDL, 0, &sfi, sizeof(sfi), SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_LINKOVERLAY);
	return sfi.iIcon;
}

int FolderTree::GetSelIconIndex(LPITEMIDLIST lpPIDL)
{
	SHFILEINFO sfi;
	memzero(&sfi, sizeof(SHFILEINFO));
	SHGetFileInfo((LPCTSTR)lpPIDL, 0, &sfi, sizeof(sfi), SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_OPENICON);
	return sfi.iIcon;
}

int FolderTree::GetSelIconIndex(HTREEITEM hItem)
{
	TV_ITEM tvi;
	memzero(&tvi, sizeof(TV_ITEM));
	tvi.mask = TVIF_SELECTEDIMAGE;
	tvi.hItem = hItem;
	if (GetItem(&tvi))
		return tvi.iSelectedImage;
	else
		return -1;
}

int FolderTree::GetSelIconIndex(const tstring &sFilename)
{
	//Retreive the icon index for a specified file/folder
	SHFILEINFO sfi;
	memzero(&sfi, sizeof(SHFILEINFO));
	SHGetFileInfo(sFilename.c_str(), 0, &sfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_OPENICON | SHGFI_SMALLICON);
	return sfi.iIcon;
}

HTREEITEM FolderTree::InsertFileItem(HTREEITEM hParent, FolderTreeItemInfo *pItem, bool bShared, int nIcon, int nSelIcon, bool bCheckForChildren)
{
	tstring sLabel;

	//Correct the label if need be
	if(IsDrive(pItem->m_sFQPath) && m_bShowDriveLabels)
		sLabel = GetDriveLabel(pItem->m_sFQPath);
	else
		sLabel = GetCorrectedLabel(pItem);

	//Add the actual item
	TV_INSERTSTRUCT tvis;
	memzero(&tvis, sizeof(TV_INSERTSTRUCT));
	tvis.hParent = hParent;
	tvis.hInsertAfter = TVI_LAST;
	tvis.item.mask = TVIF_CHILDREN | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_PARAM;
	tvis.item.iImage = nIcon;
	tvis.item.iSelectedImage = nSelIcon;

	tvis.item.lParam = (LPARAM) pItem;
	tvis.item.pszText = (LPWSTR)sLabel.c_str();
	if (bCheckForChildren)
		tvis.item.cChildren = HasGotSubEntries(pItem->m_sFQPath);
	else
		tvis.item.cChildren = true;
	
	if(bShared)
	{
		tvis.item.mask |= TVIF_STATE;
		tvis.item.stateMask |= TVIS_OVERLAYMASK;
		tvis.item.state |= INDEXTOOVERLAYMASK(1); //1 is the index for the shared overlay image
	}

	HTREEITEM hItem = InsertItem(&tvis);

	string path = Text::fromT(pItem->m_sFQPath);
	if( path[ path.length() -1 ] != PATH_SEPARATOR )
		path += PATH_SEPARATOR;

	bool bChecked = ShareManager::getInstance()->shareFolder(path, true);
	SetChecked(hItem, bChecked);
	if(!bChecked)
		SetHasSharedChildren(hItem);

	return hItem;
}

void FolderTree::DisplayDrives(HTREEITEM hParent, bool bUseSetRedraw /* = true */)
{
	//CWaitCursor c;

	//Speed up the job by turning off redraw
	if (bUseSetRedraw)
		SetRedraw(false);

	//Enumerate the drive letters and add them to the tree control
	DWORD dwDrives = GetLogicalDrives();
	DWORD dwMask = 1;
	for (int i=0; i<32; i++)
	{
		if (dwDrives & dwMask)
		{
			tstring sDrive;
			sDrive = (wchar_t)('A' + i);
			sDrive += _T(":\\");

			//check if this drive is one of the types to hide
			if (CanDisplayDrive(sDrive))
			{
				FolderTreeItemInfo* pItem = new FolderTreeItemInfo;
				pItem->m_sFQPath = sDrive;
				pItem->m_sRelativePath = sDrive;

				//Insert the item into the view
				InsertFileItem(hParent, pItem, m_bShowSharedUsingDifferentIcon && IsShared(sDrive), GetIconIndex(sDrive), GetSelIconIndex(sDrive), true);
			}
		}
		dwMask <<= 1;
	}

	if (bUseSetRedraw)
		SetRedraw(true);
}

void FolderTree::DisplayPath(const tstring &sPath, HTREEITEM hParent, bool bUseSetRedraw /* = true */)
{
	//CWaitCursor c;

	//Speed up the job by turning off redraw
	if (bUseSetRedraw)
		SetRedraw(false);

	//Remove all the items currently under hParent
	HTREEITEM hChild = GetChildItem(hParent);
	while (hChild)
	{
		DeleteItem(hChild);
		hChild = GetChildItem(hParent);
	}

	//Should we display the root folder
	if (m_bShowRootedFolder && (hParent == TVI_ROOT))
	{
		FolderTreeItemInfo* pItem = new FolderTreeItemInfo;
		pItem->m_sFQPath = m_sRootFolder;
		pItem->m_sRelativePath = m_sRootFolder;
		m_hRootedFolder = InsertFileItem(TVI_ROOT, pItem, false, GetIconIndex(m_sRootFolder), GetSelIconIndex(m_sRootFolder), true);
		Expand(m_hRootedFolder, TVE_EXPAND);
		return;
	}

	//find all the directories underneath sPath
	int nDirectories = 0;
	
	tstring sFile;
	if (sPath[sPath.size()-1] != _T('\\'))
		sFile = sPath + _T("\\");
	else
		sFile = sPath;

	WIN32_FIND_DATA fData;
	HANDLE hFind;
	hFind = FindFirstFile((sFile + _T("*")).c_str(), &fData);
	if(hFind != INVALID_HANDLE_VALUE)
	{
		do
		{
			tstring filename = fData.cFileName;
			if((fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && 
				(filename != _T(".")) && (filename != _T("..")))
			{
				++nDirectories;
				tstring sPath = sFile + filename;

				TCHAR szPath[_MAX_PATH];
				TCHAR szFname[_MAX_FNAME];
				TCHAR szExt[_MAX_EXT];
				_tsplitpath(sPath.c_str(), NULL, NULL, szFname, szExt);
				_tmakepath(szPath, NULL, NULL, szFname, szExt);

				FolderTreeItemInfo* pItem = new FolderTreeItemInfo;
				pItem->m_sFQPath = sPath;
				pItem->m_sRelativePath = szPath;
				InsertFileItem(hParent, pItem, m_bShowSharedUsingDifferentIcon && IsShared(sPath), GetIconIndex(sPath), GetSelIconIndex(sPath), true);
			}
		} while (FindNextFile(hFind, &fData));
	}

	FindClose(hFind);

	//Now sort the items we just added
	TVSORTCB tvsortcb;
	tvsortcb.hParent = hParent;
	tvsortcb.lpfnCompare = CompareByFilenameNoCase;
	tvsortcb.lParam = 0;
	SortChildrenCB(&tvsortcb);

	//If no items were added then remove the "+" indicator from hParent
	if(nDirectories == 0)
		SetHasPlusButton(hParent, FALSE);

	//Turn back on the redraw flag
	if(bUseSetRedraw)
		SetRedraw(true);
}

HTREEITEM FolderTree::SetSelectedPath(const tstring &sPath, bool bExpanded /* = false */)
{
	tstring sSearch = sPath;
	sSearch = Text::toLower(sSearch);
	int nSearchLength = sSearch.size();
	if(nSearchLength == 0)
	{
		//TRACE(_T("Cannot select a empty path\n"));
		return NULL;
	}

	//Remove initial part of path if the root folder is setup
	tstring sRootFolder = m_sRootFolder;
	sRootFolder = Text::toLower(sRootFolder);
	int nRootLength = sRootFolder.size();
	if (nRootLength)
	{
		if(sSearch.find(sRootFolder) != 0)
		{
			//TRACE(_T("Could not select the path %s as the root has been configued as %s\n"), sPath, m_sRootFolder);
			return NULL;
		}
		sSearch = sSearch.substr(nRootLength);
	}

	//Remove trailing "\" from the path
	nSearchLength = sSearch.size();
	if (nSearchLength > 3 && sSearch[nSearchLength-1] == _T('\\'))
		sSearch = sSearch.substr(0, nSearchLength-1);

	if (sSearch.empty())
		return NULL;

	SetRedraw(FALSE);

	HTREEITEM hItemFound = TVI_ROOT;
	if (nRootLength && m_hRootedFolder)
		hItemFound = m_hRootedFolder;
	bool bDriveMatch = sRootFolder.empty();
	bool bNetworkMatch = m_bDisplayNetwork && ((sSearch.size() > 2) && sSearch.find(_T("\\\\")) == 0);
	if (bNetworkMatch)
	{
		bDriveMatch = false;

		//Working here
		bool bHasPlus = HasPlusButton(m_hNetworkRoot);
		bool bHasChildren = (GetChildItem(m_hNetworkRoot) != NULL);

		if (bHasPlus && !bHasChildren)
			DoExpand(m_hNetworkRoot);
		else
			Expand(m_hNetworkRoot, TVE_EXPAND);

		hItemFound = FindServersNode(m_hNetworkRoot);
		sSearch = sSearch.substr(2);
	}
	if (bDriveMatch)
	{
		if (m_hMyComputerRoot)
		{
			//Working here
			bool bHasPlus = HasPlusButton(m_hMyComputerRoot);
			bool bHasChildren = (GetChildItem(m_hMyComputerRoot) != NULL);

			if (bHasPlus && !bHasChildren)
				DoExpand(m_hMyComputerRoot);
			else
				Expand(m_hMyComputerRoot, TVE_EXPAND);

			hItemFound = m_hMyComputerRoot;
		}
	}

	int nFound = sSearch.find(_T('\\'));
	while(nFound != tstring::npos)
	{
		tstring sMatch;
		if (bDriveMatch)
		{
			sMatch = sSearch.substr(0, nFound + 1);
			bDriveMatch = false;
		}
		else
			sMatch = sSearch.substr(0, nFound);

		hItemFound = FindSibling(hItemFound, sMatch);
		if (hItemFound == NULL)
			break;
		else if (!IsDrive(sPath))
		{
			SelectItem(hItemFound);

			//Working here
			bool bHasPlus = HasPlusButton(hItemFound);
			bool bHasChildren = (GetChildItem(hItemFound) != NULL);

			if (bHasPlus && !bHasChildren)
				DoExpand(hItemFound);
			else
				Expand(hItemFound, TVE_EXPAND);
		}

		sSearch = sSearch.substr(nFound - 1);
		nFound = sSearch.find(_T('\\'));
	};

	//The last item
	if (hItemFound)
	{
		if (sSearch.size())
			hItemFound = FindSibling(hItemFound, sSearch);
		if (hItemFound)
			SelectItem(hItemFound);

		if (bExpanded)
		{
			//Working here
			bool bHasPlus = HasPlusButton(hItemFound);
			bool bHasChildren = (GetChildItem(hItemFound) != NULL);

			if (bHasPlus && !bHasChildren)
				DoExpand(hItemFound);
			else
				Expand(hItemFound, TVE_EXPAND);
		}
	}

	//Turn back on the redraw flag
	SetRedraw(TRUE);

	return hItemFound;
}

bool FolderTree::IsDrive(HTREEITEM hItem)
{
	return IsDrive(ItemToPath(hItem));
}

bool FolderTree::IsDrive(const tstring &sPath)
{
	return (sPath.size() == 3 && sPath[1] == _T(':') && sPath[2] == _T('\\'));
}

tstring FolderTree::GetDriveLabel(const tstring &sDrive)
{
	USES_CONVERSION;
	//Let's start with the drive letter
	tstring sLabel = sDrive;

	//Try to find the item directory using ParseDisplayName
	LPITEMIDLIST lpItem;
	HRESULT hr = m_pShellFolder->ParseDisplayName(NULL, NULL, T2W((LPTSTR) (LPCTSTR)sDrive.c_str()), NULL, &lpItem, NULL);
	if(SUCCEEDED(hr))
	{
		SHFILEINFO sfi;
		if (SHGetFileInfo((LPCTSTR)lpItem, 0, &sfi, sizeof(sfi), SHGFI_PIDL | SHGFI_DISPLAYNAME))
		sLabel = sfi.szDisplayName;

		//Free the pidl now that we are finished with it
		m_pMalloc->Free(lpItem);
	}

  return sLabel;
}

tstring FolderTree::GetCorrectedLabel(FolderTreeItemInfo *pItem)
{
	tstring sLabel = pItem->m_sRelativePath;	
	return sLabel;
}

bool FolderTree::HasGotSubEntries(const tstring &sDirectory)
{
	if(sDirectory.empty())
		return false;

	if(DriveHasRemovableMedia(sDirectory))
	{
		return true; //we do not bother searching for files on drives
					//which have removable media as this would cause
					//the drive to spin up, which for the case of a
					//floppy is annoying
	}
	else
	{
		//First check to see if there is any sub directories
		tstring sFile;
		if (sDirectory[sDirectory.size()-1] == _T('\\'))
			sFile = sDirectory + _T("*.*");
		else
			sFile = sDirectory + _T("\\*.*");

		WIN32_FIND_DATA fData;
		HANDLE hFind;
		hFind = FindFirstFile(sFile.c_str(), &fData);
		if(hFind != INVALID_HANDLE_VALUE)
		{
			do
			{
				tstring cFileName = fData.cFileName;
				if((fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && 
					(cFileName != _T(".")) && (cFileName != _T("..")))
				{
					FindClose(hFind);
					return true;
				}
			} while (FindNextFile(hFind, &fData));
		}

		FindClose(hFind);
	}

	return false;
}

bool FolderTree::CanDisplayDrive(const tstring &sDrive)
{
	//check if this drive is one of the types to hide
	bool bDisplay = true;
	UINT nDrive = GetDriveType(sDrive.c_str());
	switch (nDrive)
	{
		case DRIVE_REMOVABLE:
		{
			if (m_dwDriveHideFlags & DRIVE_ATTRIBUTE_REMOVABLE)
				bDisplay = false;
			break;
		}
		case DRIVE_FIXED:
		{
			if (m_dwDriveHideFlags & DRIVE_ATTRIBUTE_FIXED)
				bDisplay = false;
			break;
		}
		case DRIVE_REMOTE:
		{
			if (m_dwDriveHideFlags & DRIVE_ATTRIBUTE_REMOTE)
				bDisplay = false;
			break;
		}
		case DRIVE_CDROM:
		{
			if (m_dwDriveHideFlags & DRIVE_ATTRIBUTE_CDROM)
				bDisplay = false;
			break;
		}
		case DRIVE_RAMDISK:
		{
			if (m_dwDriveHideFlags & DRIVE_ATTRIBUTE_RAMDISK)
				bDisplay = false;
			break;
		}
		default:
		{
			break;
		}
	}

	return bDisplay;
}

bool FolderTree::IsShared(const tstring &sPath)
{
	//Defer all the work to the share enumerator class
	return theSharedEnumerator.IsShared(sPath);
}

int CALLBACK FolderTree::CompareByFilenameNoCase(LPARAM lParam1, LPARAM lParam2, LPARAM /*lParamSort*/)
{
	FolderTreeItemInfo* pItem1 = (FolderTreeItemInfo*) lParam1;
	FolderTreeItemInfo* pItem2 = (FolderTreeItemInfo*) lParam2;

	return stricmp(pItem1->m_sRelativePath, pItem2->m_sRelativePath);
}

void FolderTree::SetHasPlusButton(HTREEITEM hItem, bool bHavePlus)
{
	//Remove all the child items from the parent
	TV_ITEM tvItem;
	tvItem.hItem = hItem;
	tvItem.mask = TVIF_CHILDREN;
	tvItem.cChildren = bHavePlus;
	SetItem(&tvItem);
}

bool FolderTree::HasPlusButton(HTREEITEM hItem)
{
	TVITEM tvItem;
	tvItem.hItem = hItem;
	tvItem.mask = TVIF_HANDLE | TVIF_CHILDREN;
	return GetItem(&tvItem) && (tvItem.cChildren != 0);
}

void FolderTree::DoExpand(HTREEITEM hItem)
{
	FolderTreeItemInfo* pItem = (FolderTreeItemInfo*) GetItemData(hItem);

	//Reset the drive node if the drive is empty or the media has changed
	if (IsMediaValid(pItem->m_sFQPath))
	{
		//Delete the item if the path is no longer valid
		if (IsFolder(pItem->m_sFQPath))
		{
			//Add the new items to the tree if it does not have any child items
			//already
			if (!GetChildItem(hItem))
				DisplayPath(pItem->m_sFQPath, hItem);
		}
		else if (hItem == m_hMyComputerRoot)
		{
			//Display an hour glass as this may take some time
			//CWaitCursor wait;

			//Enumerate the local drive letters
			DisplayDrives(m_hMyComputerRoot, FALSE);
		}
		else if ((hItem == m_hNetworkRoot) || (pItem->m_pNetResource))
		{
			//Display an hour glass as this may take some time
			//CWaitCursor wait;

			//Enumerate the network resources
			EnumNetwork(hItem);
		}
		else
		{
			//Before we delete it see if we are the only child item
			HTREEITEM hParent = GetParentItem(hItem);

			//Delete the item
			DeleteItem(hItem);

			//Remove all the child items from the parent
			SetHasPlusButton(hParent, false);
		}
	}
	else
	{
		//Display an hour glass as this may take some time
		//CWaitCursor wait;

		//Collapse the drive node and remove all the child items from it
		Expand(hItem, TVE_COLLAPSE);
		DeleteChildren(hItem, true);
	}
}

HTREEITEM FolderTree::FindServersNode(HTREEITEM hFindFrom) const
{
	if (m_bDisplayNetwork)
	{
		//Try to find some "servers" in the child items of hFindFrom
		HTREEITEM hChild = GetChildItem(hFindFrom);
		while (hChild)
		{
			FolderTreeItemInfo* pItem = (FolderTreeItemInfo*) GetItemData(hChild);
			
			if (pItem->m_pNetResource)
			{
				//Found a share
				if (pItem->m_pNetResource->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER)
					return hFindFrom;
			}

			//Get the next sibling for the next loop around
			hChild = GetNextSiblingItem(hChild);
		}

		//Ok, since we got here, we did not find any servers in any of the child nodes of this
		//item. In this case we need to call ourselves recursively to find one
		hChild = GetChildItem(hFindFrom);
		while (hChild)
		{
			HTREEITEM hFound = FindServersNode(hChild);
			if (hFound)
				return hFound;

			//Get the next sibling for the next loop around
			hChild = GetNextSiblingItem(hChild);
		}
	}

	//If we got as far as here then no servers were found.
	return NULL;
}

HTREEITEM FolderTree::FindSibling(HTREEITEM hParent, const tstring &sItem) const
{
	HTREEITEM hChild = GetChildItem(hParent);
	while(hChild)
	{
		FolderTreeItemInfo* pItem = (FolderTreeItemInfo*) GetItemData(hChild);
		
		if (stricmp(pItem->m_sRelativePath, sItem) == 0)
			return hChild;
		hChild = GetNextItem(hChild, TVGN_NEXT);
	}
	return NULL;
}

bool FolderTree::DriveHasRemovableMedia(const tstring &sPath)
{
	bool bRemovableMedia = false;
	if(IsDrive(sPath))
	{
		UINT nDriveType = GetDriveType(sPath.c_str());
		bRemovableMedia = ((nDriveType == DRIVE_REMOVABLE) ||
						(nDriveType == DRIVE_CDROM));
	}

	return bRemovableMedia;
}

bool FolderTree::IsMediaValid(const tstring &sDrive)
{
	//return TRUE if the drive does not support removable media
	UINT nDriveType = GetDriveType(sDrive.c_str());
	if ((nDriveType != DRIVE_REMOVABLE) && (nDriveType != DRIVE_CDROM))
		return true;

	//Return FALSE if the drive is empty (::GetVolumeInformation fails)
	DWORD dwSerialNumber;
	int nDrive = sDrive[0] - _T('A');
	if(GetSerialNumber(sDrive, dwSerialNumber))
		m_dwMediaID[nDrive] = dwSerialNumber;
	else
	{
		m_dwMediaID[nDrive] = 0xFFFFFFFF;
		return false;
	}

	//Also return FALSE if the disk's serial number has changed
	if ((m_dwMediaID[nDrive] != dwSerialNumber) &&
		(m_dwMediaID[nDrive] != 0xFFFFFFFF))
	{
		m_dwMediaID[nDrive] = 0xFFFFFFFF;
		return false;
	}

	return true;
}

bool FolderTree::IsFolder(const tstring &sPath)
{
	DWORD dwAttributes = GetFileAttributes(sPath.c_str());
	return ((dwAttributes != 0xFFFFFFFF) && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY));
}

bool FolderTree::EnumNetwork(HTREEITEM hParent)
{
	//What will be the return value from this function
	bool bGotChildren = false;

	//Check if the item already has a network resource and use it.
	FolderTreeItemInfo* pItem = (FolderTreeItemInfo*) GetItemData(hParent);
	
	NETRESOURCE* pNetResource = pItem->m_pNetResource;

	//Setup for the network enumeration
	HANDLE hEnum;
	DWORD dwResult = WNetOpenEnum(pNetResource ? RESOURCE_GLOBALNET : RESOURCE_CONTEXT, m_dwNetworkItemTypes,
      											0, pNetResource ? pNetResource : NULL, &hEnum);

	//Was the read sucessful
	if (dwResult != NO_ERROR)
	{
		//TRACE(_T("Cannot enumerate network drives, Error:%d\n"), dwResult);
		return FALSE;
	}

	//Do the network enumeration
	DWORD cbBuffer = 16384;

	bool bNeedMoreMemory = true;
	bool bSuccess = false;
	LPNETRESOURCE lpnrDrv = NULL;
	DWORD cEntries = 0;
	while (bNeedMoreMemory && !bSuccess)
	{
		//Allocate the memory and enumerate
  		lpnrDrv = (LPNETRESOURCE) new BYTE[cbBuffer];
		cEntries = 0xFFFFFFFF;
		dwResult = WNetEnumResource(hEnum, &cEntries, lpnrDrv, &cbBuffer);

		if (dwResult == ERROR_MORE_DATA)
		{
			//Free up the heap memory we have used
			delete [] lpnrDrv;

			cbBuffer *= 2;
		}
		else if (dwResult == NO_ERROR)
			bSuccess = true;
		else
			bNeedMoreMemory = false;
	}

	//Enumeration successful?
	if (bSuccess)
	{
		//Scan through the results
		for (DWORD i=0; i<cEntries; i++)
		{
			tstring sNameRemote;
			if(lpnrDrv[i].lpRemoteName != NULL)
				sNameRemote = lpnrDrv[i].lpRemoteName;
			else
				sNameRemote = lpnrDrv[i].lpComment;

			//Remove leading back slashes
			if (sNameRemote.size() > 0 && sNameRemote[0] == _T('\\'))
				sNameRemote = sNameRemote.substr(1);
			if (sNameRemote.size() > 0 && sNameRemote[0] == _T('\\'))
				sNameRemote = sNameRemote.substr(1);

			//Setup the item data for the new item
			FolderTreeItemInfo* pItem = new FolderTreeItemInfo;
			pItem->m_pNetResource = new NETRESOURCE;
			memzero(pItem->m_pNetResource, sizeof(NETRESOURCE));
			*pItem->m_pNetResource = lpnrDrv[i];
			
			if (lpnrDrv[i].lpLocalName)
				pItem->m_pNetResource->lpLocalName	= _tcsdup(lpnrDrv[i].lpLocalName);
			if (lpnrDrv[i].lpRemoteName)
				pItem->m_pNetResource->lpRemoteName = _tcsdup(lpnrDrv[i].lpRemoteName);
			if (lpnrDrv[i].lpComment)
				pItem->m_pNetResource->lpComment	= _tcsdup(lpnrDrv[i].lpComment);
			if (lpnrDrv[i].lpProvider)
				pItem->m_pNetResource->lpProvider	= _tcsdup(lpnrDrv[i].lpProvider);
			if (lpnrDrv[i].lpRemoteName)
				pItem->m_sFQPath = lpnrDrv[i].lpRemoteName;
			else
				pItem->m_sFQPath = sNameRemote;
			
			pItem->m_sRelativePath = sNameRemote;
			pItem->m_bNetworkNode = true;

			//Display a share and the appropiate icon
			if (lpnrDrv[i].dwDisplayType == RESOURCEDISPLAYTYPE_SHARE)
			{
				//Display only the share name
				int nPos = pItem->m_sRelativePath.find(_T('\\'));
				if (nPos >= 0)
					pItem->m_sRelativePath = pItem->m_sRelativePath.substr(nPos+1);

				//Now add the item into the control
				InsertFileItem(hParent, pItem, m_bShowSharedUsingDifferentIcon, GetIconIndex(pItem->m_sFQPath),
							GetSelIconIndex(pItem->m_sFQPath), TRUE);
			}
			else if (lpnrDrv[i].dwDisplayType == RESOURCEDISPLAYTYPE_SERVER)
			{
				//Now add the item into the control
				tstring sServer = _T("\\\\");
				sServer += pItem->m_sRelativePath;
				InsertFileItem(hParent, pItem, false, GetIconIndex(sServer), GetSelIconIndex(sServer), false);
			}
			else
			{
				//Now add the item into the control
				//Just use the generic Network Neighborhood icons for everything else
				LPITEMIDLIST lpNNPidl;
				int nIcon = 0xFFFF;
				int nSelIcon = nIcon;
				if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_NETWORK, &lpNNPidl)))
				{
					nIcon = GetIconIndex(lpNNPidl);
					nSelIcon = GetSelIconIndex(lpNNPidl);

					//Free up the pidl now that we are finished with it
					//ASSERT(m_pMalloc);
					m_pMalloc->Free(lpNNPidl);
					m_pMalloc->Release();
				}

				InsertFileItem(hParent, pItem, false, nIcon, nSelIcon, false);
			}
			bGotChildren = true;
		}
	}
/*	else
		TRACE(_T("Cannot complete network drive enumeration, Error:%d\n"), dwResult);
*/
	//Clean up the enumeration handle
	WNetCloseEnum(hEnum);

	//Free up the heap memory we have used
	delete [] lpnrDrv;

	//Return whether or not we added any items
	return bGotChildren;
}

int FolderTree::DeleteChildren(HTREEITEM hItem, bool bUpdateChildIndicator)
{
	int nCount = 0;
	HTREEITEM hChild = GetChildItem(hItem);
	while (hChild)
	{
		//Get the next sibling before we delete the current one
		HTREEITEM hNextItem = GetNextSiblingItem(hChild);

		//Delete the current child
		DeleteItem(hChild);

		//Get ready for the next loop
		hChild = hNextItem;
		++nCount;
	}

	//Also update its indicator to suggest that their is children
	if (bUpdateChildIndicator)
		SetHasPlusButton(hItem, (nCount != 0));

	return nCount;
}

BOOL FolderTree::GetSerialNumber(const tstring &sDrive, DWORD &dwSerialNumber)
{
	return GetVolumeInformation(sDrive.c_str(), NULL, 0, &dwSerialNumber, NULL, NULL, NULL, 0);
}

LRESULT FolderTree::OnSelChanged(int /*idCtrl*/, LPNMHDR pnmh, BOOL &bHandled)
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pnmh;

	//Nothing selected
	if (pNMTreeView->itemNew.hItem == NULL)
	{
		bHandled = FALSE;
		return 0;
	}

	//Check to see if the current item is valid, if not then delete it (Exclude network items from this check)
	FolderTreeItemInfo* pItem = (FolderTreeItemInfo*) GetItemData(pNMTreeView->itemNew.hItem);
	//ASSERT(pItem);
	tstring sPath = pItem->m_sFQPath;
	if ((pNMTreeView->itemNew.hItem != m_hNetworkRoot) && (pItem->m_pNetResource == NULL) &&
		(pNMTreeView->itemNew.hItem != m_hMyComputerRoot) && !IsDrive(sPath) && (GetFileAttributes(sPath.c_str()) == 0xFFFFFFFF))
	{
		//Before we delete it see if we are the only child item
		HTREEITEM hParent = GetParentItem(pNMTreeView->itemNew.hItem);

		//Delete the item
		DeleteItem(pNMTreeView->itemNew.hItem);

		//Remove all the child items from the parent
		SetHasPlusButton(hParent, false);

		bHandled = FALSE; //Allow the message to be reflected again
		return 1;
	}

	//Remeber the serial number for this item (if it is a drive)
	if (IsDrive(sPath))
	{
		int nDrive = sPath[0] - _T('A');
		GetSerialNumber(sPath, m_dwMediaID[nDrive]);
	}
	
	bHandled = FALSE; //Allow the message to be reflected again
	
	return 0;	
}

LRESULT FolderTree::OnItemExpanding(int /*idCtrl*/, LPNMHDR pnmh, BOOL &bHandled)
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pnmh;
	if (pNMTreeView->action == TVE_EXPAND)
	{
		bool bHasPlus = HasPlusButton(pNMTreeView->itemNew.hItem);
		bool bHasChildren = (GetChildItem(pNMTreeView->itemNew.hItem) != NULL);

		if (bHasPlus && !bHasChildren)
			DoExpand(pNMTreeView->itemNew.hItem);
	}
	else if(pNMTreeView->action == TVE_COLLAPSE)
	{
		FolderTreeItemInfo* pItem = (FolderTreeItemInfo*) GetItemData(pNMTreeView->itemNew.hItem);
		//ASSERT(pItem);

		//Display an hour glass as this may take some time
		//CWaitCursor wait;

		tstring sPath = ItemToPath(pNMTreeView->itemNew.hItem);
		
		//Collapse the node and remove all the child items from it
		Expand(pNMTreeView->itemNew.hItem, TVE_COLLAPSE);

		//Never uppdate the child indicator for a network node which is not a share
		bool bUpdateChildIndicator = true;
		if (pItem->m_bNetworkNode)
		{
			if (pItem->m_pNetResource)
				bUpdateChildIndicator = (pItem->m_pNetResource->dwDisplayType == RESOURCEDISPLAYTYPE_SHARE);
			else
				bUpdateChildIndicator = false;
		}
		DeleteChildren(pNMTreeView->itemNew.hItem, bUpdateChildIndicator);
	}

	bHandled = FALSE; //Allow the message to be reflected again
	return 0;
}

LRESULT FolderTree::OnDeleteItem(int /*idCtrl*/, LPNMHDR pnmh, BOOL &bHandled)
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pnmh;
	if (pNMTreeView->itemOld.hItem != TVI_ROOT)
	{
		FolderTreeItemInfo* pItem = (FolderTreeItemInfo*) pNMTreeView->itemOld.lParam;
		if (pItem->m_pNetResource)
		{
			free(pItem->m_pNetResource->lpLocalName);
			free(pItem->m_pNetResource->lpRemoteName);
			free(pItem->m_pNetResource->lpComment);
			free(pItem->m_pNetResource->lpProvider);
			delete pItem->m_pNetResource;
		}
		delete pItem;
	}
	
	bHandled = FALSE; //Allow the message to be reflected again
	return 0;	
}

bool FolderTree::GetChecked(HTREEITEM hItem) const
{
	UINT u = GetItemState(hItem, TVIS_STATEIMAGEMASK);
	return ((u >> 12) - 1) > 0;
}

BOOL FolderTree::SetChecked(HTREEITEM hItem, bool fCheck)
{
	TVITEM item;
	item.mask = TVIF_HANDLE | TVIF_STATE;
	item.hItem = hItem;
	item.stateMask = TVIS_STATEIMAGEMASK;

	/*
	Since state images are one-based, 1 in this macro turns the check off, and
	2 turns it on.
	*/
	item.state = INDEXTOSTATEIMAGEMASK((fCheck ? 2 : 1));

	return SetItem(&item);
}

LRESULT FolderTree::OnChecked(HTREEITEM hItem, BOOL &bHandled)
{
	FolderTreeItemInfo* pItem = (FolderTreeItemInfo*) GetItemData(hItem);
	if(!Util::validatePath(Text::fromT(pItem->m_sFQPath)))
	{
		// no checking myComp or network
		bHandled = TRUE;
		return 1;
	}
	
	HTREEITEM hSharedParent = HasSharedParent(hItem);
	// if a parent is checked then this folder should be removed from the ex list
	if(hSharedParent != NULL)
	{
		ShareParentButNotSiblings(hItem);
	}
	else
	{
        // if no parent folder is checked then this is a new root dir
		try {
			LineDlg virt;
			virt.title = TSTRING(VIRTUAL_NAME);
			virt.description = TSTRING(VIRTUAL_NAME_LONG);

			tstring path = pItem->m_sFQPath;
			if( path[ path.length() -1 ] != '\\' )
				path += '\\';
			virt.line = Text::toT(ShareManager::getInstance()->validateVirtual(
				Util::getLastDir(Text::fromT(path))));

			if(virt.DoModal() == IDOK) {
				ShareManager::getInstance()->addDirectory(Text::fromT(path), Text::fromT(virt.line));
			}
			HashProgressDlg(true).DoModal();
			UpdateParentItems(hItem);
		} catch(const ShareException& e) {
			MessageBox(Text::toT(e.getError()).c_str(), _T(APPNAME) _T(" ") _T(VERSIONSTRING), MB_ICONSTOP | MB_OK);
			bHandled = TRUE;
			return 1;
		}
	}
	
	UpdateStaticCtrl();
	
	UpdateChildItems(hItem, true);
	
	return 0;
}

LRESULT FolderTree::OnUnChecked(HTREEITEM hItem, BOOL& /*bHandled*/)
{
	FolderTreeItemInfo* pItem = (FolderTreeItemInfo*) GetItemData(hItem);

	HTREEITEM hSharedParent = HasSharedParent(hItem);
	// if no parent is checked remove this root folder from share
	if(hSharedParent == NULL)
	{
		string path = Text::fromT(pItem->m_sFQPath);
		if( path[ path.length() -1 ] != PATH_SEPARATOR )
			path += PATH_SEPARATOR;

		int64_t temp = ShareManager::getInstance()->removeExcludeFolder(path);
		/* fun with math
		futureShareSize = currentShareSize + currentOffsetSize - (sizeOfDirToBeRemoved - sizeOfSubDirsWhichWhereAlreadyExcluded) */
		int64_t futureShareSize = ShareManager::getInstance()->getShareSize() + m_nShareSizeDiff - (Util::getDirSize(Text::fromT(pItem->m_sFQPath)) - temp);
		ShareManager::getInstance()->removeDirectory(path);
		/* more fun with math
		theNewOffset = whatWeKnowTheCorrectNewSizeShouldBe - whatTheShareManagerThinksIsTheNewShareSize */
		m_nShareSizeDiff = futureShareSize - ShareManager::getInstance()->getShareSize();
		UpdateParentItems(hItem);
	}
	else if(GetChecked(GetParentItem(hItem)))
	{
		// if the parent is checked add this folder to excludes
		string path = Text::fromT(pItem->m_sFQPath);
		if(	path[ path.length() -1 ] != PATH_SEPARATOR )
			path += PATH_SEPARATOR;
		m_nShareSizeDiff -= ShareManager::getInstance()->addExcludeFolder(path);
	}
	
	UpdateStaticCtrl();
	
	UpdateChildItems(hItem, false);

	return 0;
}

bool FolderTree::GetHasSharedChildren(HTREEITEM hItem)
{
	StringPairList Dirs = ShareManager::getInstance()->getDirectories();
	tstring searchStr;
	int startPos = 0;

	if(hItem == m_hMyComputerRoot)
	{
		startPos = 1;
		searchStr = _T(":\\");
	}
	else if(hItem == m_hNetworkRoot)
		searchStr = _T("\\\\");
	else if(hItem != NULL)
	{
		FolderTreeItemInfo* pItem  = (FolderTreeItemInfo*) GetItemData(hItem);
		searchStr = pItem->m_sFQPath;

		if(searchStr.empty())
			return false;

	}
	else
        return false;

	for(StringPairIter i = Dirs.begin(); i != Dirs.end(); ++i)
	{
		if((i->second).size() > (searchStr.size() + startPos))
		{
			if(stricmp((i->second).substr(startPos, searchStr.size()), Text::fromT(searchStr)) == 0) {
				if(searchStr.size() <= 3) {
				return Util::fileExists(i->second + PATH_SEPARATOR);
				} else {
					if((i->second).substr(searchStr.size()).substr(0,1) == "\\")
						return Util::fileExists(i->second + PATH_SEPARATOR);
					else
						return false;
				}
			}
		}
	}

	return false;
}

void FolderTree::SetHasSharedChildren(HTREEITEM hItem)
{
	SetHasSharedChildren(hItem, GetHasSharedChildren(hItem));
}

void FolderTree::SetHasSharedChildren(HTREEITEM hItem, bool bHasSharedChildren)
{
	if(bHasSharedChildren)
		SetItemState(hItem, TVIS_BOLD, TVIS_BOLD);
	else
		SetItemState(hItem, 0, TVIS_BOLD);
}

void FolderTree::UpdateChildItems(HTREEITEM hItem, bool bChecked)
{
	HTREEITEM hChild = GetChildItem(hItem);
	while(hChild)
	{
		SetChecked(hChild, bChecked);
		UpdateChildItems(hChild, bChecked);
		hChild = GetNextSiblingItem(hChild);
	}
}

void FolderTree::UpdateParentItems(HTREEITEM hItem)
{
	HTREEITEM hParent = GetParentItem(hItem);
	if(hParent != NULL && HasSharedParent(hParent) == NULL)
	{
		SetHasSharedChildren(hParent);
		UpdateParentItems(hParent);
	}
}

HTREEITEM FolderTree::HasSharedParent(HTREEITEM hItem)
{
	HTREEITEM hParent = GetParentItem(hItem);
	while(hParent != NULL)
	{
		if(GetChecked(hParent))
			return hParent;

		hParent = GetParentItem(hParent);
	}

	return NULL;
}

void FolderTree::ShareParentButNotSiblings(HTREEITEM hItem)
{
	FolderTreeItemInfo* pItem;
	HTREEITEM hParent = GetParentItem(hItem);
	if(!GetChecked(hParent))
	{
		SetChecked(hParent, true);
		pItem = (FolderTreeItemInfo*) GetItemData(hParent);
		string path = Text::fromT(pItem->m_sFQPath);
		if( path[ path.length() -1 ] != PATH_SEPARATOR )
			path += PATH_SEPARATOR;
		m_nShareSizeDiff += ShareManager::getInstance()->removeExcludeFolder(path);

		ShareParentButNotSiblings(hParent);
		
		HTREEITEM hChild = GetChildItem(hParent);
		while(hChild)
		{
			HTREEITEM hNextItem = GetNextSiblingItem(hChild);
			if(!GetChecked(hChild))
			{
				pItem = (FolderTreeItemInfo*) GetItemData(hChild);
				if(hChild != hItem) {
					string path = Text::fromT(pItem->m_sFQPath);
					if( path[ path.length() -1 ] != PATH_SEPARATOR )
						path += PATH_SEPARATOR;
					m_nShareSizeDiff -= ShareManager::getInstance()->addExcludeFolder(path);
				}
			}
			hChild = hNextItem;
		}
	}
	else
	{
		pItem = (FolderTreeItemInfo*) GetItemData(hItem);
		string path = Text::fromT(pItem->m_sFQPath);
		if( path[ path.length() -1 ] != PATH_SEPARATOR )
			path += PATH_SEPARATOR;
		m_nShareSizeDiff += ShareManager::getInstance()->removeExcludeFolder(path);
	}
}

void FolderTree::SetStaticCtrl(CStatic *staticCtrl)
{
	m_pStaticCtrl = staticCtrl;
}

void FolderTree::UpdateStaticCtrl()
{
	m_bDirty = true;
	if(m_pStaticCtrl != NULL)
	{
		/* display theActualSizeOfTheShareAfterRefresh = WhatTheShareManagerThinksIsTheCorrectSize - theOffsetCreatedByPlayingAroundWithExcludeFolders */
		m_pStaticCtrl->SetWindowText((Util::formatBytesW(ShareManager::getInstance()->getShareSize() + m_nShareSizeDiff) + _T("*")).c_str());
	}
}

bool FolderTree::IsDirty()
{
	return m_bDirty;
}
