// CJShellTree.cpp : implementation file
//
// This file is part of the CJLibrary for Visual C++ / MFC
// Copyright C 1998-1999 COdejock Software, All Rights Reserved.

// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 1, or (at your option)
// any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// Based upon the MFCENUM example. MFCENUM is a companion sample application 
// for Chapter 14 in "Programming the Windows 95 User Interface" by Nancy Cluts,
// available in the Microsoft(R) Development Library.
//
/////////////////////////////////////////////////////////////////////////////
/****************************************************************************
 *
 * $Date: 2005/08/09 00:22:44 $
 * $Revision: 1.3 $
 * $Archive: /CodeJock/CJLibrary/CJShellTree.cpp $
 *
 * $History: CJShellTree.cpp $
 * 
 * *****************  Version 11  *****************
 * User: Kirk Stowell Date: 10/30/99   Time: 1:59p
 * Updated in $/CodeJock/CJLibrary
 * Changed base class from CTreeCtrl to CCJTreeCtrl for flicker free
 * drawing.
 * 
 * *****************  Version 10  *****************
 * User: Kirk Stowell Date: 9/13/99    Time: 5:52a
 * Updated in $/CodeJockey/CJLibrary
 * Fixed build errors which occured with UNICODE builds.
 * 
 * *****************  Version 9  *****************
 * User: Kirk Stowell Date: 8/31/99    Time: 4:39a
 * Updated in $/CodeJockey/CJLibrary
 * Updated copyright and contact information.
 * 
 * *****************  Version 8  *****************
 * User: Kirk Stowell Date: 7/25/99    Time: 12:30a
 * Updated in $/CodeJockey/CJLibrary
 * 
 * *****************  Version 7  *****************
 * User: Kirk Stowell Date: 6/23/99    Time: 12:33a
 * Updated in $/CodeJockey/CJLibrary
 * 
 * *****************  Version 6  *****************
 * User: Kirk Stowell Date: 6/11/99    Time: 10:14p
 * Updated in $/CodeJockey/CJ60Lib
 * Cleaned up system image list initialization.
 * 
 * *****************  Version 5  *****************
 * User: Kirk Stowell Date: 5/12/99    Time: 5:32a
 * Updated in $/CodeJockey/CJ60Lib
 * Added support for vc5 compatibility
 * 
 * *****************  Version 4  *****************
 * User: Kirk Stowell Date: 5/12/99    Time: 1:41a
 * Updated in $/CodeJockey/CJ60Lib
 * Added wait cursor for lengthly processing.
 * 
 * *****************  Version 3  *****************
 * User: Kirk Stowell Date: 5/02/99    Time: 3:38p
 * Updated in $/CodeJockey/CJ60Lib
 * Added version macro  _WIN32_IE, you will need to have at IE4 or greater
 * installed to use this class. This will ensure that the version of
 * SHELL32.DLL is compatible.
 * 
 * *****************  Version 2  *****************
 * User: Kirk Stowell Date: 3/19/99    Time: 11:05p
 * Updated in $/CodeJockey/CJ60Lib
 * Initial re-write and release.
 * 
 * *****************  Version 1  *****************
 * User: Kirk Stowell Date: 2/24/99    Time: 11:05p
 * Created in $/CodeJockey/CJ60Lib
 * Overloaded PopulateTree(LPCTSTR lpszPath) in the class CCJShellTree to
 * fill the tree based upon path. Takehiko Mizoguti
 * [mizoguti@m2.sys.to.casio.co.jp]
 *
 ***************************************************************************/
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "CJShellTree.h"
#include "CJShellList.h"
#include "CJShell.h"
#include "CJFlatComboBox.h"
#include <direct.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#if _WIN32_IE >= 0x0400 // Need to have at IE4 or greater installed.
#include <Shlwapi.h>

/////////////////////////////////////////////////////////////////////////////
// CCJShellTree

CCJShellTree::CCJShellTree()
{
	m_pListCtrl		= NULL;
	m_pComboBox		= NULL;
	m_bRefresh		= false;
    m_bOutaHere		= false;
	m_htiCurrent	= NULL;
}

CCJShellTree::~CCJShellTree()
{
}

IMPLEMENT_DYNAMIC(CCJShellTree, CCJTreeCtrl)

BEGIN_MESSAGE_MAP(CCJShellTree, CCJTreeCtrl)
	//{{AFX_MSG_MAP(CCJShellTree)
	ON_NOTIFY_REFLECT(NM_RCLICK, OnRclick)
	ON_NOTIFY_REFLECT(TVN_DELETEITEM, OnDeleteitem)
	ON_NOTIFY_REFLECT(TVN_SELCHANGED, OnSelchanged)
	ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING, OnItemexpanding)
	ON_WM_DESTROY()
	ON_WM_CREATE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCJShellTree message handlers

void CCJShellTree::OnFill(bool bRefresh/*=false*/)
{
    LPSHELLFOLDER lpsf=NULL;
    HRESULT hr;
    TV_SORTCB      tvscb;

	m_bRefresh = bRefresh;

    // Get a pointer to the desktop folder.
    hr=SHGetDesktopFolder(&lpsf);
	
    if (SUCCEEDED(hr))
    {
		// Initialize the tree view to be empty.
		DeleteAllItems();
		
		// Fill in the tree view from the root.
		FillTreeView(lpsf,
			NULL,
			TVI_ROOT);
		// Release the folder pointer.
		lpsf->Release();
    }
    tvscb.hParent     = TVI_ROOT;
    tvscb.lParam      = 0;
    tvscb.lpfnCompare = TreeViewCompareProc;
	
    // Sort the items in the tree view
	SortChildrenCB(&tvscb/*, FALSE*/);
	
	Expand(GetRootItem(), TVE_EXPAND);
}

void CCJShellTree::FillTreeView(LPSHELLFOLDER lpsf, LPITEMIDLIST  lpifq, HTREEITEM hParent)
{
    TV_ITEM         tvi;                          // TreeView Item.
    TV_INSERTSTRUCT tvins;                        // TreeView Insert Struct.
    HTREEITEM       hPrev = NULL;                 // Previous Item Added.
    LPENUMIDLIST    lpe=0;
    LPITEMIDLIST    lpi=0, lpifqThisItem=0;
    LPTVITEMDATA    lptvid=0;
    LPMALLOC        lpMalloc=0;
    ULONG           ulFetched;
    HRESULT         hr;
    TCHAR           szBuff[MAX_PATH];
    HWND            hwnd=GetParent()->GetSafeHwnd();

    hr=SHGetMalloc(&lpMalloc);
    if (FAILED(hr))
       return;

    // Hourglass on!

    SetCapture();
    CWaitCursor wait;   // display wait cursor

    if (SUCCEEDED(hr))
    {
		SetRedraw(FALSE);

        hr=lpsf->EnumObjects(hwnd, 
			SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &lpe);

        if (SUCCEEDED(hr))
        {
            while (S_OK==lpe->Next(1, &lpi, &ulFetched))
            {
                //Create a fully qualified path to the current item
                //The SH* shell api's take a fully qualified path pidl,
                //(see GetIcon above where I call SHGetFileInfo) whereas the
                //interface methods take a relative path pidl.

                ULONG ulAttrs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER;

                lpsf->GetAttributesOf(1, (const struct _ITEMIDLIST **)&lpi, &ulAttrs);

                if (ulAttrs & (SFGAO_HASSUBFOLDER | SFGAO_FOLDER))
                {
                   //We need this next if statement so that we don't add things like
                   //the MSN to our tree.  MSN is not a folder, but according to the
                   //shell is has subfolders....

                   if (ulAttrs & SFGAO_FOLDER)
                   {
                      tvi.mask            = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE |
                                            TVIF_PARAM;

                      if (ulAttrs & SFGAO_HASSUBFOLDER)
                      {
                         //This item has sub-folders, so let's put the + in the TreeView.
                         //The first time the user clicks on the item, we'll populate the
                         //sub-folders then.

                         tvi.cChildren=1;
                         tvi.mask |= TVIF_CHILDREN;
                      }
                        
                      //OK, let's get some memory for our ITEMDATA struct

                      lptvid = (LPTVITEMDATA)lpMalloc->Alloc(sizeof(TVITEMDATA));
                      if (!lptvid)
                         goto Done;
   
                      //Now get the friendly name that we'll put in the treeview...

                      if( m_shell.GetName(lpsf, lpi, SHGDN_NORMAL, szBuff) == FALSE ) {
                         goto Done;
					  }

                      tvi.pszText    = szBuff;
                      tvi.cchTextMax = MAX_PATH;
    
                      lpifqThisItem=m_shell.ConcatPidls(lpifq, lpi);
      
                      //Now, make a copy of the ITEMIDLIST
      
                      lptvid->lpi=m_shell.CopyITEMID(lpMalloc, lpi);
   
                      m_shell.GetNormalAndSelectedIcons(lpifqThisItem, &tvi);
   
                      lptvid->lpsfParent=lpsf;    //Store the parent folders SF
                      lpsf->AddRef();

                      //So, you ask, what's the story here?  Why do we have 2
                      //functions that apparently do the same thing?  Well, 
                      //ParseDisplayName() (used in GetFullyQualPidl) does not 
                      //work well for non-file system objects such as the My 
                      //Computer and Net Hood.  So, since we know that these 
                      //guys will only show up when we are enumerating the root 
                      //of the namespace (as identified by an hParent of TVI_ROOT), 
                      //we special case this here.  You *could* use ConcatPidls() 
                      //in ALL cases, but the GetFullyQualPidl() is shown for 
                      //demonstration purposes, since it's the more intuative way.

//Commented out for now, since we need ConcatPidls for *any* non-file system object.
//This includes things like Control Panel, et al.

//                      if (hParent==TVI_ROOT)
                         lptvid->lpifq=m_shell.ConcatPidls(lpifq, lpi);
//                      else
//                         lptvid->lpifq=GetFullyQualPidl(lpsf, lpi);
   
                      tvi.lParam = (LPARAM)lptvid;
   
                      // Populate the TreeVeiw Insert Struct
                      // The item is the one filled above.
                      // Insert it after the last item inserted at this level.
                      // And indicate this is a root entry.
   				   
                      tvins.item         = tvi;
                      tvins.hInsertAfter = hPrev;
                      tvins.hParent      = hParent;
   
                      // Add the item to the tree
   
                      hPrev = InsertItem(&tvins);
                   }

                   lpMalloc->Free(lpifqThisItem);  
                   lpifqThisItem=0;
                }

                lpMalloc->Free(lpi);  //Finally, free the pidl that the shell gave us...
                lpi=0;
            }
        }

    }
    else {
		SetRedraw();
		return;
	}

Done:
 
    // Hourglass off!

    ReleaseCapture();

    if (lpe)  lpe->Release();

    //The following 2 if statements will only be TRUE if we got here on an
    //error condition from the "goto" statement.  Otherwise, we free this memory
    //at the end of the while loop above.
    if (lpi && lpMalloc)           lpMalloc->Free(lpi);
    if (lpifqThisItem && lpMalloc) lpMalloc->Free(lpifqThisItem);  

    if (lpMalloc) lpMalloc->Release();
	SetRedraw();
}

void CCJShellTree::OnRclick(NMHDR* /*pNMHDR*/, LRESULT* pResult) 
{
	*pResult = 0;

	POINT			pt;
	LPTVITEMDATA	lptvid;  //Long pointer to TreeView item data
	static char		szBuff[MAX_PATH];
	TV_HITTESTINFO	tvhti;
	TV_ITEM			tvi;
	
	::GetCursorPos((LPPOINT)&pt);
	::ScreenToClient(m_hWnd, &pt);
	tvhti.pt=pt;
	HitTest(&tvhti);
	SelectItem(tvhti.hItem);
	if (tvhti.flags & TVHT_ONITEMLABEL)
	{
		::ClientToScreen(m_hWnd, &pt);
		tvi.mask=TVIF_PARAM;
		tvi.hItem=tvhti.hItem;
		
		if (!GetItem(&tvi))
			return;
		
		lptvid=(LPTVITEMDATA)tvi.lParam;
		
		m_shell.DoTheMenuThing(::GetParent(m_hWnd),
			lptvid->lpsfParent, lptvid->lpi, &pt);
	}
}

void CCJShellTree::OnDeleteitem(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
	*pResult = 0;

	HRESULT        hr;
	LPMALLOC       lpMalloc;
	LPTVITEMDATA   lptvid;  //Long pointer to TreeView item data

	//Let's free the memory for the TreeView item data...
	hr=SHGetMalloc(&lpMalloc);
	if (FAILED(hr))
		return;
	
	lptvid=(LPTVITEMDATA)pNMTreeView->itemOld.lParam;
	lptvid->lpsfParent->Release();
	lpMalloc->Free(lptvid->lpi);  
	lpMalloc->Free(lptvid->lpifq);  
	lpMalloc->Free(lptvid);  
	lpMalloc->Release();
}

void CCJShellTree::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
	LPTVITEMDATA	lptvid;  //Long pointer to TreeView item data
	LPSHELLFOLDER	lpsf2=NULL;
	HRESULT			hr;

	SetRedraw(FALSE);
	//Do this only if we are not exiting the application...
	if (!m_bOutaHere)
	{
		lptvid=(LPTVITEMDATA)pNMTreeView->itemNew.lParam;
		if (lptvid)
		{
			hr=lptvid->lpsfParent->BindToObject(lptvid->lpi,
				0,IID_IShellFolder,(LPVOID *)&lpsf2);
			
			if (SUCCEEDED(hr) && (m_bRefresh == false))
			{
				// get the current directory.
				CString strFolderPath;
				GetSelectedFolderPath(strFolderPath);
				
				// get a handle to the currently selected item and set the
				// working directory.
				m_htiCurrent = pNMTreeView->itemNew.hItem;
				SetCurrentDirectory( strFolderPath );
				
				if (m_pListCtrl->GetSafeHwnd())
				{
					m_pListCtrl->PopulateListView(lptvid,lpsf2);
					lpsf2->Release();
				}
				if( m_pComboBox->GetSafeHwnd( ))
				{
					int nFound = m_pComboBox->FindString(-1, strFolderPath);
					if (nFound == CB_ERR) {
						m_pComboBox->SetCurSel( m_pComboBox->AddString( strFolderPath ));
					}
					else {
						m_pComboBox->SetCurSel(nFound);
					}
				}
			}
		}
	}

	SetRedraw();
	*pResult = 0;
}

void CCJShellTree::OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
	*pResult = 0;

	LPTVITEMDATA   lptvid;  //Long pointer to TreeView item data
	HRESULT        hr;
	LPSHELLFOLDER  lpsf2=NULL;
	static char    szBuff[MAX_PATH];
	TV_SORTCB      tvscb;

	if ((pNMTreeView->itemNew.state & TVIS_EXPANDEDONCE))
		return;
	
	SetRedraw(FALSE);
	lptvid=(LPTVITEMDATA)pNMTreeView->itemNew.lParam;
	if (lptvid)
	{
		hr=lptvid->lpsfParent->BindToObject(lptvid->lpi,
			0, IID_IShellFolder,(LPVOID *)&lpsf2);
		
		if (SUCCEEDED(hr))
		{
			FillTreeView(lpsf2,
				lptvid->lpifq,
				pNMTreeView->itemNew.hItem);
		}
		
		tvscb.hParent     = pNMTreeView->itemNew.hItem;
		tvscb.lParam      = 0;
		tvscb.lpfnCompare = TreeViewCompareProc;
		
		SortChildrenCB(&tvscb/*, FALSE*/);
	}
	SetRedraw();
}

int CALLBACK CCJShellTree::TreeViewCompareProc(LPARAM lparam1, LPARAM lparam2, LPARAM /*lparamSort*/)
{
    LPTVITEMDATA lptvid1=(LPTVITEMDATA)lparam1;
    LPTVITEMDATA lptvid2=(LPTVITEMDATA)lparam2;
    HRESULT   hr;

    hr = lptvid1->lpsfParent->CompareIDs(0, lptvid1->lpi, lptvid2->lpi);

    if (FAILED(hr))
       return 0;

    return (short)SCODE_CODE(GetScode(hr));
}

BOOL CCJShellTree::InitSystemImageLists()
{
    SHFILEINFO sfi;

    HIMAGELIST hImageList = (HIMAGELIST)::SHGetFileInfo( _T("C:\\"), 0, &sfi, 
		sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON );

    if( hImageList ) {
        SetImageList( CImageList::FromHandle( hImageList ), TVSIL_NORMAL  );
		return TRUE;
    }

    return FALSE;
}

void CCJShellTree::OnDestroy() 
{
	m_bOutaHere	= false;
	CCJTreeCtrl::OnDestroy();
}

void CCJShellTree::TunnelTree(CString strFindPath)
{
	HTREEITEM subNode = GetRootItem();
	CString szPathHop;
	char drive[_MAX_DRIVE];
	char dir[_MAX_DIR];
	char fname[_MAX_FNAME];
	char ext[_MAX_EXT];
	char delimiter[]="\\";

	m_bRefresh = false;
	
	if(!m_shell.Exist(strFindPath))
	{
		if (strFindPath.GetLength() == 3)
		{
		}

		else
		{
			MessageBox(strFindPath,_T("Folder not found"),MB_ICONERROR);
			return;
		}
	}
	
	if(strFindPath.ReverseFind(_T('\\')) != strFindPath.GetLength()-1)
	{
		strFindPath += _T("\\");
	}
	
	m_shell.SplitPath(strFindPath,drive,dir,fname,ext);
	
	//search the drive first
	szPathHop=drive;
	subNode=GetChildItem(subNode);

	if(subNode)
	{
		if(SearchTree(subNode,szPathHop, CCJShellTree::type_drive))
		{
			SetRedraw(FALSE);
			//break down subfolders and search
			char *p=strtok(dir,delimiter);
			while(p)
			{
				subNode = GetSelectedItem();
				Expand(subNode, TVE_EXPAND);
				subNode = GetChildItem(subNode);

				if(SearchTree(subNode,p,CCJShellTree::type_folder))
					p=strtok(NULL,delimiter);
				else
					p=NULL;
			}
			SetRedraw();
		}
	}
}

BOOL CCJShellTree::SearchTree(HTREEITEM treeNode, CString szSearchName, FindAttribs attr)
{
	LPTVITEMDATA	lptvid;  //Long pointer to TreeView item data
	LPSHELLFOLDER	lpsf2=NULL;
	char	drive[_MAX_DRIVE];
	char	dir[_MAX_DIR];
	char	fname[_MAX_FNAME];
	char	ext[_MAX_EXT];
	BOOL	bRet=FALSE;
	HRESULT	hr;
	CString	strCompare;
	
	szSearchName.MakeUpper();
	while(treeNode && bRet==FALSE)
	{
		lptvid=(LPTVITEMDATA)GetItemData(treeNode);
		if (lptvid && lptvid->lpsfParent && lptvid->lpi)
		{
			hr=lptvid->lpsfParent->BindToObject(lptvid->lpi,
				0,IID_IShellFolder,(LPVOID *)&lpsf2);
			if (SUCCEEDED(hr))
			{
				ULONG ulAttrs = SFGAO_FILESYSTEM;
				lptvid->lpsfParent->GetAttributesOf(1, (const struct _ITEMIDLIST **)&lptvid->lpi, &ulAttrs);
				if (ulAttrs & (SFGAO_FILESYSTEM))
				{
					if(SHGetPathFromIDList(lptvid->lpifq,strCompare.GetBuffer(MAX_PATH)))
					{
						switch(attr)
						{
						case type_drive:
							m_shell.SplitPath(strCompare,drive,dir,fname,ext);
							strCompare=drive;
							break;
						case type_folder:
							strCompare = GetItemText(treeNode);
							break;
						}
						strCompare.MakeUpper();
						if(strCompare == szSearchName)
						{
							EnsureVisible(treeNode);
							SelectItem(treeNode);
							bRet=TRUE;
						}
					}
				}
				lpsf2->Release();
			}
		}
		treeNode = GetNextSiblingItem(treeNode);
	}
	return bRet;
}

BOOL CCJShellTree::GetSelectedFolderPath(CString &strFolderPath)
{
	LPTVITEMDATA	lptvid;  //Long pointer to TreeView item data
	LPSHELLFOLDER	lpsf2=NULL;
	static TCHAR	szBuff[MAX_PATH];
	HTREEITEM		hItem = GetSelectedItem();
	HRESULT			hr;
	BOOL			bRet=FALSE;
	
	if( hItem != NULL )
	{
		lptvid=(LPTVITEMDATA)GetItemData(hItem);
		
		if (lptvid && lptvid->lpsfParent && lptvid->lpi)
		{
			hr=lptvid->lpsfParent->BindToObject(lptvid->lpi,
				0,IID_IShellFolder,(LPVOID *)&lpsf2);
			
			if (SUCCEEDED(hr))
			{
				ULONG ulAttrs = SFGAO_FOLDER;
				
				// Determine what type of object we have.
				lptvid->lpsfParent->GetAttributesOf(1,
					(const struct _ITEMIDLIST **)&lptvid->lpi, &ulAttrs);
				
				if (ulAttrs & (SFGAO_FOLDER))
				{
					if(SHGetPathFromIDList(lptvid->lpifq,szBuff)){
						strFolderPath = szBuff;
						bRet = TRUE;
					}
				}
			}
			
		}
		if(lpsf2)
			lpsf2->Release();
	}
	
	return bRet;
}

BOOL CCJShellTree::GetFolderItemPath(HTREEITEM hItem, CString &strFolderPath)
{
	LPTVITEMDATA	lptvid;  //Long pointer to TreeView item data
	LPSHELLFOLDER	lpsf2=NULL;
	static TCHAR	szBuff[MAX_PATH];
	HRESULT			hr;
	BOOL			bRet=FALSE;
	
	lptvid=(LPTVITEMDATA)GetItemData(hItem);
	
	if (lptvid && lptvid->lpsfParent && lptvid->lpi)
	{
		hr=lptvid->lpsfParent->BindToObject(lptvid->lpi,
			0,IID_IShellFolder,(LPVOID *)&lpsf2);
		
		if (SUCCEEDED(hr))
		{
			ULONG ulAttrs = SFGAO_FILESYSTEM;
			
			// Determine what type of object we have.
			lptvid->lpsfParent->GetAttributesOf(1, (const struct _ITEMIDLIST **)&lptvid->lpi, &ulAttrs);
			
			if (ulAttrs & (SFGAO_FILESYSTEM))
			{
				if(SHGetPathFromIDList(lptvid->lpifq,szBuff)){
					strFolderPath = szBuff;
					bRet = TRUE;
				}
			}
		}

		if(lpsf2)
			lpsf2->Release();
	}
	
	return bRet;
}

void CCJShellTree::PopulateTree(LPCTSTR lpszPath)
{
	LPSHELLFOLDER lpsf=NULL,lpsf2=NULL;
    LPITEMIDLIST  lpi=NULL;
    HRESULT hr;
    TV_SORTCB      tvscb;
	LPTSTR			lpFolder = (LPTSTR)lpszPath;
	LPTSTR			lpNextFolder;
	TCHAR			strPath[_MAX_PATH];
	
	LPMALLOC pMalloc;
	if (::SHGetMalloc(&pMalloc) == NOERROR)
	{
		
		// Get a pointer to the desktop folder.
		hr=SHGetDesktopFolder(&lpsf);
		
		if (SUCCEEDED(hr))
		{
			USES_CONVERSION;
			
			// Initialize the tree view to be empty.
			DeleteAllItems();
			
			do{
				
				// Get the Next Component
				lpNextFolder = PathFindNextComponent( lpFolder );
				if( lpNextFolder && *lpNextFolder ){
					memcpy( strPath, lpFolder, ( lpNextFolder - lpFolder ) );
					strPath[lpNextFolder - lpFolder] = _T('\0');
				}
				else{
					_tcscpy( strPath, lpFolder );
					lpNextFolder = NULL;
				}
				
				// Get ShellFolder Pidl
				ULONG eaten;
				hr = lpsf->ParseDisplayName( NULL, NULL, T2OLE(strPath), &eaten, &lpi, NULL );
				if( FAILED( hr ) ){
					break;
				}
				hr=lpsf->BindToObject(lpi, 0, IID_IShellFolder,(LPVOID *)&lpsf2);
				if( FAILED( hr ) ){
					break;
				}
				
				pMalloc->Free( lpi );
				
				// Release the Parent Folder pointer.
				lpsf->Release();
				
				// Chenge Folder Info
				lpsf = lpsf2;
				lpFolder = lpNextFolder;
			}
			while( lpNextFolder );
			
			FillTreeView(lpsf,NULL,TVI_ROOT);
			
		}
	}
    tvscb.hParent     = TVI_ROOT;
    tvscb.lParam      = 0;
    tvscb.lpfnCompare = TreeViewCompareProc;
	
    // Sort the items in the tree view
	SortChildrenCB(&tvscb/*, FALSE*/);
    
	HTREEITEM hItem;
	hItem = GetRootItem();
	Expand(hItem,TVE_EXPAND);
	Select(GetRootItem(),TVGN_CARET);
}

int CCJShellTree::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CCJTreeCtrl::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	// Attach the system image list, and build the tree.
	InitSystemImageLists();
	OnFill();
	
	return 0;
}

BOOL CCJShellTree::PreCreateWindow(CREATESTRUCT& cs) 
{
	if( !CCJTreeCtrl::PreCreateWindow(cs))
		return FALSE;

	cs.style |= TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|TVS_SHOWSELALWAYS;

	return TRUE;
}

#endif // (_WIN32_IE >= 0x0400)
