1167 lines
28 KiB
C++
1167 lines
28 KiB
C++
// XTShellTreeView.cpp : implementation file
|
|
//
|
|
// This file is a part of the XTREME CONTROLS MFC class library.
|
|
// (c)1998-2008 Codejock Software, All Rights Reserved.
|
|
//
|
|
// THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
|
|
// RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
|
|
// CONSENT OF CODEJOCK SOFTWARE.
|
|
//
|
|
// THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
|
|
// IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
|
|
// YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
|
|
// SINGLE COMPUTER.
|
|
//
|
|
// CONTACT INFORMATION:
|
|
// support@codejock.com
|
|
// http://www.codejock.com
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h"
|
|
#include "Common/XTPVC80Helpers.h" // Visual Studio 2005 helper functions
|
|
#include "Common/XTPResourceManager.h"
|
|
|
|
#include "XTFunctions.h"
|
|
#include "XTDropSource.h"
|
|
|
|
#include "XTTreeBase.h"
|
|
#include "XTShellSettings.h"
|
|
#include "XTShellPidl.h"
|
|
#include "XTShellTreeBase.h"
|
|
#include "XTComboBoxEx.h"
|
|
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CXTShellTreeBase
|
|
|
|
CXTShellTreeBase::CXTShellTreeBase()
|
|
: m_bTunneling(false)
|
|
{
|
|
m_bContextMenu = TRUE;
|
|
m_pComboBox = NULL;
|
|
m_uFlags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
|
|
|
|
if (m_shSettings.ShowAllFiles() && !m_shSettings.ShowSysFiles())
|
|
{
|
|
m_uFlags |= SHCONTF_INCLUDEHIDDEN;
|
|
}
|
|
|
|
m_nRootFolder = CSIDL_DESKTOP;
|
|
m_bShowFiles = FALSE;
|
|
m_bShowShellLinkIcons = FALSE;
|
|
}
|
|
|
|
CXTShellTreeBase::~CXTShellTreeBase()
|
|
{
|
|
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CXTShellTreeBase message handlers
|
|
|
|
HTREEITEM CXTShellTreeBase::InsertDesktopItem(int nFolder /*= CSIDL_DESKTOP*/)
|
|
{
|
|
HTREEITEM hItem = TVI_ROOT;
|
|
|
|
CShellMalloc lpMalloc;
|
|
|
|
if (!lpMalloc)
|
|
return NULL;
|
|
|
|
// Get ShellFolder Pidl
|
|
LPITEMIDLIST pidlDesktop = NULL;
|
|
if (FAILED(::SHGetSpecialFolderLocation(NULL, nFolder, &pidlDesktop)))
|
|
{
|
|
pidlDesktop = NULL;
|
|
}
|
|
|
|
// insert the desktop.
|
|
if (pidlDesktop)
|
|
{
|
|
SHFILEINFO fileInfo;
|
|
::ZeroMemory(&fileInfo, sizeof(fileInfo));
|
|
|
|
::SHGetFileInfo((LPCTSTR)pidlDesktop, NULL, &fileInfo, sizeof(fileInfo),
|
|
SHGFI_PIDL | SHGFI_ATTRIBUTES | SHGFI_DISPLAYNAME);
|
|
|
|
TV_ITEM tvi;
|
|
tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
|
|
|
|
// Allocate memory for ITEMDATA struct
|
|
XT_TVITEMDATA* lptvid = new XT_TVITEMDATA;
|
|
if (lptvid != NULL)
|
|
{
|
|
GetNormalAndSelectedIcons(pidlDesktop, &tvi);
|
|
|
|
// Now, make a copy of the ITEMIDLIST and store the parent folders SF.
|
|
lptvid->lpi = DuplicateItem(lpMalloc, pidlDesktop);
|
|
lptvid->lpsfParent = NULL;
|
|
lptvid->lpifq = ConcatPidls(lpMalloc, NULL, pidlDesktop);
|
|
|
|
TCHAR szBuff[MAX_PATH];
|
|
STRCPY_S(szBuff, MAX_PATH, fileInfo.szDisplayName);
|
|
|
|
tvi.lParam = (LPARAM)lptvid;
|
|
tvi.pszText = szBuff;
|
|
tvi.cchTextMax = MAX_PATH;
|
|
|
|
// 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.
|
|
TV_INSERTSTRUCT tvins;
|
|
tvins.item = tvi;
|
|
tvins.hInsertAfter = hItem;
|
|
tvins.hParent = hItem;
|
|
|
|
// Add the item to the tree
|
|
hItem = m_pTreeCtrl->InsertItem(&tvins);
|
|
}
|
|
|
|
if (pidlDesktop)
|
|
{
|
|
lpMalloc.Free(pidlDesktop);
|
|
}
|
|
}
|
|
|
|
return hItem;
|
|
}
|
|
|
|
|
|
void CXTShellTreeBase::PopulateTreeView()
|
|
{
|
|
// Get a pointer to the desktop folder.
|
|
CShellSpecialFolder lpsfFolder(m_nRootFolder);
|
|
if (!lpsfFolder)
|
|
return;
|
|
|
|
// turn off redraw and remove all tree items.
|
|
m_pTreeCtrl->SetRedraw(FALSE);
|
|
m_pTreeCtrl->DeleteAllItems();
|
|
|
|
HTREEITEM hItemDesktop = InsertDesktopItem(m_nRootFolder);
|
|
|
|
LPITEMIDLIST pidlRoot = NULL;
|
|
if (FAILED(::SHGetSpecialFolderLocation(NULL, m_nRootFolder, &pidlRoot)))
|
|
{
|
|
pidlRoot = NULL;
|
|
}
|
|
|
|
// Fill in the tree view from the root.
|
|
InitTreeViewItems(lpsfFolder, pidlRoot, hItemDesktop);
|
|
|
|
if (pidlRoot)
|
|
{
|
|
CShellMalloc malloc;
|
|
malloc.Free(pidlRoot);
|
|
}
|
|
|
|
// Sort the items in the tree view
|
|
SortChildren(hItemDesktop);
|
|
|
|
HTREEITEM hItemRoot = m_pTreeCtrl->GetRootItem();
|
|
m_pTreeCtrl->Expand(hItemRoot, TVE_EXPAND);
|
|
|
|
if (hItemDesktop != TVI_ROOT)
|
|
{
|
|
HTREEITEM hItemChild = m_pTreeCtrl->GetChildItem(hItemDesktop);
|
|
m_pTreeCtrl->Select(hItemChild, TVGN_CARET);
|
|
|
|
if ((::GetWindowLong(m_pTreeCtrl->m_hWnd, GWL_STYLE) & TVS_SINGLEEXPAND) == 0)
|
|
{
|
|
m_pTreeCtrl->Expand(hItemChild, TVE_EXPAND);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_pTreeCtrl->Select(hItemRoot, TVGN_CARET);
|
|
}
|
|
|
|
// turn on redraw and refresh tree.
|
|
m_pTreeCtrl->SetRedraw(TRUE);
|
|
m_pTreeCtrl->RedrawWindow();
|
|
m_pTreeCtrl->SetFocus();
|
|
}
|
|
|
|
void CXTShellTreeBase::SetAttributes(HTREEITEM hItem, DWORD dwAttributes)
|
|
{
|
|
MapShellFlagsToItemAttributes(m_pTreeCtrl, hItem, dwAttributes);
|
|
}
|
|
|
|
BOOL CXTShellTreeBase::InitTreeViewItems(LPSHELLFOLDER lpsf, LPITEMIDLIST lpifq, HTREEITEM hParent)
|
|
{
|
|
CWaitCursor wait; // show wait cursor.
|
|
|
|
// Allocate a shell memory object.
|
|
CShellMalloc lpMalloc;
|
|
|
|
if (!lpMalloc)
|
|
return FALSE;
|
|
|
|
// Get the IEnumIDList object for the given folder.
|
|
LPENUMIDLIST lpe = NULL;
|
|
if (FAILED(lpsf->EnumObjects(::GetParent(m_pTreeCtrl->m_hWnd), m_uFlags, &lpe)))
|
|
return FALSE;
|
|
|
|
ULONG ulFetched = 0;
|
|
HTREEITEM hPrev = NULL;
|
|
LPITEMIDLIST lpi = NULL;
|
|
|
|
// Enumerate through the list of folder and non-folder objects.
|
|
while (lpe->Next(1, &lpi, &ulFetched) == S_OK)
|
|
{
|
|
// 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 | SFGAO_DISPLAYATTRMASK | SFGAO_REMOVABLE;
|
|
|
|
// Determine what type of object we have.
|
|
lpsf->GetAttributesOf(1, (const struct _ITEMIDLIST **)&lpi, &ulAttrs);
|
|
|
|
// 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 it has subfolders.
|
|
if ((ulAttrs & SFGAO_FOLDER) || m_bShowFiles)
|
|
{
|
|
TV_ITEM tvi;
|
|
tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
|
|
|
|
if ((ulAttrs & SFGAO_HASSUBFOLDER) || (m_bShowFiles && (ulAttrs & SFGAO_FOLDER)))
|
|
{
|
|
//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.
|
|
tvi.cChildren = 1;
|
|
tvi.mask |= TVIF_CHILDREN;
|
|
}
|
|
|
|
// Allocate memory for ITEMDATA struct
|
|
CString szBuff;
|
|
XT_TVITEMDATA* lptvid = new XT_TVITEMDATA;
|
|
if (lptvid == NULL || GetName(lpsf, lpi, SHGDN_NORMAL, szBuff) == FALSE)
|
|
{
|
|
if (lptvid)
|
|
{
|
|
lpMalloc.Free(lptvid);
|
|
}
|
|
if (lpe)
|
|
{
|
|
lpe->Release();
|
|
}
|
|
if (lpi)
|
|
{
|
|
lpMalloc.Free(lpi);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// Now, make a copy of the ITEMIDLIST and store the parent folders SF.
|
|
lptvid->lpi = DuplicateItem(lpMalloc, lpi);
|
|
lptvid->lpsfParent = lpsf;
|
|
lptvid->lpifq = ConcatPidls(lpMalloc, lpifq, lpi);
|
|
lpsf->AddRef();
|
|
|
|
GetNormalAndSelectedIcons(lptvid->lpifq, &tvi);
|
|
|
|
tvi.lParam = (LPARAM)lptvid;
|
|
tvi.pszText = (LPTSTR)(LPCTSTR)szBuff;
|
|
tvi.cchTextMax = 0;
|
|
|
|
// 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.
|
|
TV_INSERTSTRUCT tvins;
|
|
tvins.item = tvi;
|
|
tvins.hInsertAfter = hPrev;
|
|
tvins.hParent = hParent;
|
|
|
|
// Add the item to the tree
|
|
hPrev = m_pTreeCtrl->InsertItem(&tvins);
|
|
SetAttributes(hPrev, ulAttrs);
|
|
}
|
|
|
|
// Free the pidl that the shell gave us.
|
|
if (lpi)
|
|
{
|
|
lpMalloc.Free(lpi);
|
|
lpi = 0;
|
|
}
|
|
}
|
|
if (lpi)
|
|
{
|
|
lpMalloc.Free(lpi);
|
|
}
|
|
|
|
if (lpe)
|
|
{
|
|
lpe->Release();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void CXTShellTreeBase::GetNormalAndSelectedIcons(LPITEMIDLIST lpifq, LPTV_ITEM lptvitem)
|
|
{
|
|
// Note that we don't check the return value here because if GetIcon()
|
|
// fails, then we're in big trouble...
|
|
lptvitem->iImage = GetItemIcon(lpifq,
|
|
SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
|
|
|
|
lptvitem->iSelectedImage = GetItemIcon(lpifq,
|
|
SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_OPENICON);
|
|
}
|
|
|
|
void CXTShellTreeBase::OnFolderExpanding(NM_TREEVIEW* pNMTreeView)
|
|
{
|
|
if (!(pNMTreeView->itemNew.state & TVIS_EXPANDEDONCE))
|
|
{
|
|
// Long pointer to TreeView item data
|
|
XT_TVITEMDATA* lptvid = (XT_TVITEMDATA*)pNMTreeView->itemNew.lParam;
|
|
if (lptvid != NULL && lptvid->lpsfParent != NULL)
|
|
{
|
|
LPSHELLFOLDER lpsf = NULL;
|
|
if (SUCCEEDED(lptvid->lpsfParent->BindToObject(lptvid->lpi,
|
|
0, IID_IShellFolder, (LPVOID *)&lpsf)))
|
|
{
|
|
InitTreeViewItems(lpsf, lptvid->lpifq, pNMTreeView->itemNew.hItem);
|
|
}
|
|
|
|
SortChildren(pNMTreeView->itemNew.hItem);
|
|
|
|
m_pTreeCtrl->SetItemState(pNMTreeView->itemNew.hItem, TVIS_EXPANDEDONCE, TVIS_EXPANDEDONCE);
|
|
}
|
|
}
|
|
}
|
|
|
|
HTREEITEM CXTShellTreeBase::GetContextMenu()
|
|
{
|
|
CPoint point;
|
|
::GetCursorPos(&point);
|
|
m_pTreeCtrl->ScreenToClient(&point);
|
|
|
|
TV_HITTESTINFO tvhti;
|
|
tvhti.pt = point;
|
|
m_pTreeCtrl->HitTest(&tvhti);
|
|
|
|
if (tvhti.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))
|
|
{
|
|
// Long pointer to TreeView item data
|
|
XT_TVITEMDATA* lptvid = (XT_TVITEMDATA*)m_pTreeCtrl->GetItemData(tvhti.hItem);
|
|
|
|
m_pTreeCtrl->ClientToScreen(&point);
|
|
|
|
if (lptvid->lpsfParent == NULL)
|
|
{
|
|
LPSHELLFOLDER lpShellFolder;
|
|
if (FAILED(::SHGetDesktopFolder(&lpShellFolder)))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ShowContextMenu(m_pTreeCtrl->m_hWnd,
|
|
lpShellFolder, lptvid->lpi, &point);
|
|
|
|
if (lpShellFolder)
|
|
{
|
|
lpShellFolder->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ShowContextMenu(m_pTreeCtrl->m_hWnd,
|
|
lptvid->lpsfParent, lptvid->lpi, &point);
|
|
}
|
|
|
|
return tvhti.hItem;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CXTShellTreeBase::SortChildren(HTREEITEM hParent)
|
|
{
|
|
TV_SORTCB tvscb;
|
|
tvscb.hParent = hParent;
|
|
tvscb.lParam = 0;
|
|
tvscb.lpfnCompare = TreeViewCompareProc;
|
|
m_pTreeCtrl->SortChildrenCB(&tvscb);
|
|
}
|
|
|
|
BOOL CXTShellTreeBase::OnFolderSelected(NM_TREEVIEW* pNMTreeView, CString &strFolderPath)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
HTREEITEM hItem = pNMTreeView->itemNew.hItem;
|
|
if (hItem == NULL)
|
|
return FALSE;
|
|
|
|
LPSHELLFOLDER lpsf = NULL;
|
|
|
|
// Long pointer to TreeView item data
|
|
XT_TVITEMDATA* lptvid = (XT_TVITEMDATA*)m_pTreeCtrl->GetItemData(hItem);
|
|
if (lptvid && lptvid->lpsfParent && lptvid->lpi)
|
|
{
|
|
if (SUCCEEDED(lptvid->lpsfParent->BindToObject(lptvid->lpi,
|
|
0, IID_IShellFolder, (LPVOID*)&lpsf)))
|
|
{
|
|
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))
|
|
{
|
|
TCHAR szBuff[MAX_PATH];
|
|
if (::SHGetPathFromIDList(lptvid->lpifq, szBuff))
|
|
{
|
|
strFolderPath = szBuff;
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
|
|
if (m_pTreeCtrl->ItemHasChildren(pNMTreeView->itemNew.hItem) && !(pNMTreeView->itemNew.state & TVIS_EXPANDEDONCE))
|
|
{
|
|
InitTreeViewItems(lpsf, lptvid->lpifq, pNMTreeView->itemNew.hItem);
|
|
|
|
SortChildren(pNMTreeView->itemNew.hItem);
|
|
|
|
m_pTreeCtrl->SetItemState(pNMTreeView->itemNew.hItem, TVIS_EXPANDEDONCE, TVIS_EXPANDEDONCE);
|
|
}
|
|
|
|
if (lpsf)
|
|
{
|
|
lpsf->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
BOOL CXTShellTreeBase::InitSystemImageLists()
|
|
{
|
|
HIMAGELIST himlSmall = GetSystemImageList(SHGFI_SMALLICON);
|
|
|
|
if (himlSmall)
|
|
{
|
|
TreeView_SetImageList(m_pTreeCtrl->GetSafeHwnd(), himlSmall, TVSIL_NORMAL);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CXTShellTreeBase::GetSelectedFolderPath(CString &strFolderPath)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
HTREEITEM hItem = m_pTreeCtrl->GetSelectedItem();
|
|
if (hItem != NULL)
|
|
{
|
|
// Long pointer to TreeView item data
|
|
XT_TVITEMDATA* lptvid = (XT_TVITEMDATA*)m_pTreeCtrl->GetItemData(hItem);
|
|
if (lptvid && lptvid->lpsfParent && lptvid->lpi)
|
|
{
|
|
LPSHELLFOLDER lpsf = NULL;
|
|
if (SUCCEEDED(lptvid->lpsfParent->BindToObject(lptvid->lpi,
|
|
0, IID_IShellFolder, (LPVOID *)&lpsf)))
|
|
{
|
|
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))
|
|
{
|
|
TCHAR szBuff[MAX_PATH];
|
|
if (::SHGetPathFromIDList(lptvid->lpifq, szBuff))
|
|
{
|
|
strFolderPath = szBuff;
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
if (lpsf)
|
|
{
|
|
lpsf->Release();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
BOOL CXTShellTreeBase::FindTreeItem(HTREEITEM hItem, XT_LVITEMDATA* lplvid, BOOL bRecursively)
|
|
{
|
|
if (lplvid == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bRecursively)
|
|
{
|
|
hItem = m_pTreeCtrl->GetChildItem(hItem);
|
|
}
|
|
|
|
while (hItem)
|
|
{
|
|
// Long pointer to TreeView item data
|
|
XT_TVITEMDATA* lptvid = (XT_TVITEMDATA*)m_pTreeCtrl->GetItemData(hItem);
|
|
if (lptvid)
|
|
{
|
|
if (SCODE_CODE(GetScode(lplvid->lpsfParent->CompareIDs(
|
|
0, lplvid->lpi, lptvid->lpi))) == 0)
|
|
{
|
|
m_pTreeCtrl->EnsureVisible(hItem);
|
|
m_pTreeCtrl->SelectItem(hItem);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (bRecursively)
|
|
{
|
|
HTREEITEM hNextItem = m_pTreeCtrl->GetChildItem(hItem);
|
|
if (hNextItem)
|
|
{
|
|
if (FindTreeItem(hNextItem, lplvid))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
hItem = m_pTreeCtrl->GetNextSiblingItem(hItem);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void CXTShellTreeBase::OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult)
|
|
{
|
|
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
|
|
OnFolderExpanding(pNMTreeView);
|
|
*pResult = 0;
|
|
}
|
|
|
|
void CXTShellTreeBase::SelectionChanged(HTREEITEM hItem, CString strFolderPath)
|
|
{
|
|
if (hItem != NULL && !m_bTunneling)
|
|
{
|
|
if (!m_pComboBox || !::IsWindow(m_pComboBox->m_hWnd))
|
|
return;
|
|
|
|
// update combo box association.
|
|
if (m_pComboBox->IsKindOf(RUNTIME_CLASS(CXTComboBoxEx)))
|
|
{
|
|
CXTComboBoxEx* pComboBoxEx = DYNAMIC_DOWNCAST(CXTComboBoxEx, m_pComboBox);
|
|
ASSERT_VALID(pComboBoxEx);
|
|
|
|
int nFound = CB_ERR;
|
|
int nIndex;
|
|
for (nIndex = 0; nIndex < pComboBoxEx->GetCount(); ++nIndex)
|
|
{
|
|
CString strText;
|
|
pComboBoxEx->GetLBText(nIndex, strText);
|
|
|
|
if (strFolderPath.Compare(strText) == 0)
|
|
{
|
|
nFound = nIndex;
|
|
pComboBoxEx->SetCurSel(nIndex);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nFound == CB_ERR)
|
|
{
|
|
HTREEITEM hti = m_pTreeCtrl->GetSelectedItem();
|
|
ASSERT(hti);
|
|
|
|
if (strFolderPath.IsEmpty())
|
|
{
|
|
strFolderPath = m_pTreeCtrl->GetItemText(hti);
|
|
}
|
|
|
|
int nImage, nSelectedImage;
|
|
m_pTreeCtrl->GetItemImage(hti, nImage, nSelectedImage);
|
|
|
|
pComboBoxEx->InsertItem(0, strFolderPath, 0, nImage, nImage);
|
|
pComboBoxEx->SetCurSel(0);
|
|
pComboBoxEx->SetItemDataPtr(0, hti);
|
|
}
|
|
}
|
|
|
|
else if (m_pComboBox->IsKindOf(RUNTIME_CLASS(CComboBox)))
|
|
{
|
|
CComboBox* pComboBox = DYNAMIC_DOWNCAST(CComboBox, m_pComboBox);
|
|
ASSERT_VALID(pComboBox);
|
|
|
|
int nFound = pComboBox->FindStringExact(-1, strFolderPath);
|
|
if (nFound == CB_ERR)
|
|
{
|
|
HTREEITEM hti = m_pTreeCtrl->GetSelectedItem();
|
|
ASSERT(hti);
|
|
|
|
pComboBox->InsertString(0, strFolderPath);
|
|
pComboBox->SetCurSel(0);
|
|
pComboBox->SetItemDataPtr(0, (HTREEITEM)hti);
|
|
}
|
|
else
|
|
{
|
|
pComboBox->SetCurSel(nFound);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void CXTShellTreeBase::OnDeleteTreeItem(NMHDR* pNMHDR, LRESULT* pResult)
|
|
{
|
|
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
|
|
|
|
XT_TVITEMDATA* lptvid = (XT_TVITEMDATA*)pNMTreeView->itemOld.lParam;
|
|
if (lptvid != NULL)
|
|
{
|
|
CShellMalloc lpMalloc;
|
|
|
|
if (lptvid->lpi)
|
|
{
|
|
lpMalloc.Free(lptvid->lpi);
|
|
lptvid->lpi = NULL;
|
|
}
|
|
if (lptvid->lpsfParent)
|
|
{
|
|
lptvid->lpsfParent->Release();
|
|
lptvid->lpsfParent = NULL;
|
|
}
|
|
if (lptvid->lpifq)
|
|
{
|
|
lpMalloc.Free(lptvid->lpifq);
|
|
lptvid->lpifq = NULL;
|
|
}
|
|
|
|
delete lptvid;
|
|
}
|
|
|
|
*pResult = 0;
|
|
}
|
|
|
|
void CXTShellTreeBase::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult)
|
|
{
|
|
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
|
|
|
|
CString strFolderPath;
|
|
OnFolderSelected(pNMTreeView, strFolderPath);
|
|
|
|
if (strFolderPath.IsEmpty())
|
|
{
|
|
strFolderPath = m_pTreeCtrl->GetItemText(pNMTreeView->itemNew.hItem);
|
|
}
|
|
|
|
// currently selected TreeItem
|
|
SelectionChanged(m_pTreeCtrl->GetSelectedItem(), strFolderPath);
|
|
|
|
*pResult = 0;
|
|
}
|
|
|
|
void CXTShellTreeBase::OnRclick(NMHDR* /*pNMHDR*/, LRESULT* pResult)
|
|
{
|
|
// Display the shell context menu.
|
|
if (m_bContextMenu == TRUE)
|
|
{
|
|
HTREEITEM hItem = GetContextMenu();
|
|
if (hItem != NULL)
|
|
{
|
|
// TODO: Additional error handling.
|
|
}
|
|
}
|
|
|
|
*pResult = 0;
|
|
}
|
|
|
|
|
|
BOOL CXTShellTreeBase::InitializeTree(DWORD dwStyle/*= TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT*/)
|
|
{
|
|
if (m_pTreeCtrl->GetImageList(TVSIL_NORMAL) == NULL)
|
|
{
|
|
// Initialize the image list for the list view and populate it.
|
|
VERIFY(InitSystemImageLists());
|
|
|
|
// Set the style for the tree control.
|
|
m_pTreeCtrl->ModifyStyle(0, dwStyle);
|
|
|
|
// Make sure multi-select mode is disabled.
|
|
EnableMultiSelect(FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void CXTShellTreeBase::InitTreeNode(HTREEITEM hItem, XT_TVITEMDATA* lptvid)
|
|
{
|
|
m_pTreeCtrl->SetRedraw(FALSE);
|
|
if (lptvid)
|
|
{
|
|
LPSHELLFOLDER lpsf = NULL;
|
|
if (SUCCEEDED(lptvid->lpsfParent->BindToObject(lptvid->lpi,
|
|
0, IID_IShellFolder, (LPVOID *)&lpsf)))
|
|
{
|
|
InitTreeViewItems(lpsf, lptvid->lpifq, hItem);
|
|
}
|
|
|
|
SortChildren(hItem);
|
|
}
|
|
|
|
m_pTreeCtrl->SetRedraw(TRUE);
|
|
}
|
|
|
|
HTREEITEM CXTShellTreeBase::SearchTree(HTREEITEM hItem, LPCITEMIDLIST pABSPidl)
|
|
{
|
|
XT_TVITEMDATA *pItem = NULL;
|
|
HTREEITEM hChildItem = m_pTreeCtrl->GetChildItem(hItem);
|
|
|
|
CShellSpecialFolder pShellFolder;
|
|
if (!pShellFolder)
|
|
return NULL;
|
|
|
|
while (hChildItem)
|
|
{
|
|
// Get the pidl that is stored in the tree node
|
|
pItem = (XT_TVITEMDATA *)m_pTreeCtrl->GetItemData(hChildItem);
|
|
|
|
// See if it matches the one we're looking for
|
|
if (ComparePidls(pItem->lpifq, pABSPidl, pShellFolder))
|
|
{
|
|
m_pTreeCtrl->Select(hChildItem, TVGN_CARET);
|
|
|
|
// Ensure that we are expanded
|
|
UINT uState = m_pTreeCtrl->GetItemState(hChildItem, TVIS_EXPANDEDONCE);
|
|
if (!(uState & TVIS_EXPANDEDONCE))
|
|
{
|
|
InitTreeNode(hChildItem, (XT_TVITEMDATA*)m_pTreeCtrl->GetItemData(hChildItem));
|
|
m_pTreeCtrl->SetItemState(hChildItem, TVIS_EXPANDEDONCE, TVIS_EXPANDEDONCE);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Didn't compare... try next one
|
|
hChildItem = m_pTreeCtrl->GetNextSiblingItem(hChildItem);
|
|
}
|
|
|
|
return hChildItem;
|
|
}
|
|
|
|
BOOL CXTShellTreeBase::TunnelTree(CString strFindPath)
|
|
{
|
|
if (strFindPath.IsEmpty())
|
|
return false;
|
|
|
|
m_bTunneling = true;
|
|
|
|
BOOL bLock = m_pTreeCtrl->LockWindowUpdate();
|
|
BOOL bFound = false;
|
|
|
|
LPITEMIDLIST pidlPath;
|
|
|
|
// Attempt to get the folder's item list
|
|
pidlPath = IDLFromPath(strFindPath);
|
|
|
|
// If it is NULL then see if it is one of the special folders
|
|
if (pidlPath == NULL)
|
|
{
|
|
// These are the ones we care about.
|
|
const int nCSIDLMax = 0x001b;
|
|
for (int i = 0; i <= nCSIDLMax; i++)
|
|
{
|
|
LPITEMIDLIST pidlSpecialPath = NULL;
|
|
if (::SHGetSpecialFolderLocation(NULL, i, &pidlSpecialPath) != NOERROR)
|
|
continue;
|
|
|
|
SHFILEINFO fileInfo;
|
|
::ZeroMemory(&fileInfo, sizeof(fileInfo));
|
|
::SHGetFileInfo((LPCTSTR)pidlSpecialPath, NULL, &fileInfo, sizeof(fileInfo),
|
|
SHGFI_PIDL | SHGFI_DISPLAYNAME);
|
|
|
|
CString cs = fileInfo.szDisplayName;
|
|
if (cs.CompareNoCase(strFindPath) == 0)
|
|
{
|
|
// Found the pidl for the special folder
|
|
pidlPath = pidlSpecialPath;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pidlPath != NULL)
|
|
{
|
|
// Now work through the list and tree nodes until we compare
|
|
int nItems = GetPidlCount(pidlPath);
|
|
LPITEMIDLIST pPartPidl;
|
|
LPITEMIDLIST pABSPidl = NULL;
|
|
HTREEITEM hItem = m_pTreeCtrl->GetRootItem();
|
|
bFound = false;
|
|
|
|
// Loop through all the parts and see if we can find a match in the tree. It should
|
|
// be there unless something got added to the namespace after we built the tree last
|
|
// and we didn't catch it, but that's pretty unlikely.
|
|
for (int i = 0; i < nItems; i++)
|
|
{
|
|
pPartPidl = CopyPidlItem(pidlPath, i);
|
|
pABSPidl = ConcatPidls(pABSPidl, pPartPidl);
|
|
FreePidl(pPartPidl);
|
|
|
|
hItem = SearchTree(hItem, pABSPidl);
|
|
if (!hItem)
|
|
break; // Our partial path should still found
|
|
}
|
|
|
|
// If it was found the final path should compare to the full path entered
|
|
// and the hItem should be set to the place in the tree where the path ends
|
|
if (hItem && (nItems == 0 || ComparePidls(pidlPath, pABSPidl, NULL)))
|
|
{
|
|
m_pTreeCtrl->Select(hItem, TVGN_CARET);
|
|
// Ensure that we are expanded
|
|
UINT uState = m_pTreeCtrl->GetItemState(hItem, TVIS_EXPANDEDONCE);
|
|
if (!(uState & TVIS_EXPANDEDONCE))
|
|
{
|
|
InitTreeNode(hItem, (XT_TVITEMDATA*)m_pTreeCtrl->GetItemData(hItem));
|
|
m_pTreeCtrl->SetItemState(hItem, TVIS_EXPANDEDONCE, TVIS_EXPANDEDONCE);
|
|
}
|
|
bFound = true;
|
|
}
|
|
|
|
// Clean up
|
|
if (pidlPath)
|
|
FreePidl(pidlPath);
|
|
|
|
if (pABSPidl)
|
|
FreePidl(pABSPidl);
|
|
}
|
|
else
|
|
{
|
|
// Must not be a special folder (stand alone) or a path that ParseDisplayName() doesn't recognize so look through tree
|
|
// directly to see if we can find it that way.
|
|
// Start at Desktop ...
|
|
HTREEITEM hItemRoot = m_pTreeCtrl->GetRootItem();
|
|
TCHAR* pszNext = NULL;
|
|
TCHAR szBuff[MAX_PATH];
|
|
|
|
STRCPY_S(szBuff, MAX_PATH, strFindPath);
|
|
|
|
TCHAR* lpszContext = 0;
|
|
pszNext = STRTOK_S(szBuff, _T("\\/"), &lpszContext);
|
|
|
|
bFound = false;
|
|
|
|
if (pszNext != NULL)
|
|
{
|
|
CString strItemText(m_pTreeCtrl->GetItemText(hItemRoot));
|
|
|
|
// Are we looking from desktop?
|
|
if (strItemText.CompareNoCase(pszNext) == 0)
|
|
pszNext = STRTOK_S(NULL, _T("\\/"), &lpszContext);
|
|
|
|
hItemRoot = m_pTreeCtrl->GetChildItem(hItemRoot);
|
|
|
|
// Ensure that we are expanded
|
|
UINT uState = m_pTreeCtrl->GetItemState(hItemRoot, TVIS_EXPANDEDONCE);
|
|
if (!(uState & TVIS_EXPANDEDONCE))
|
|
{
|
|
InitTreeNode(hItemRoot, (XT_TVITEMDATA*)m_pTreeCtrl->GetItemData(hItemRoot));
|
|
m_pTreeCtrl->SetItemState(hItemRoot, TVIS_EXPANDEDONCE, TVIS_EXPANDEDONCE);
|
|
}
|
|
|
|
while (pszNext && hItemRoot)
|
|
{
|
|
strItemText = m_pTreeCtrl->GetItemText(hItemRoot);
|
|
if (strItemText.CompareNoCase(pszNext) == 0)
|
|
{
|
|
// Found it
|
|
// We know this was successful - expand at this new root
|
|
m_pTreeCtrl->Select(hItemRoot, TVGN_CARET);
|
|
|
|
// Ensure that we are expanded
|
|
uState = m_pTreeCtrl->GetItemState(hItemRoot, TVIS_EXPANDEDONCE);
|
|
if (!(uState & TVIS_EXPANDEDONCE))
|
|
{
|
|
InitTreeNode(hItemRoot, (XT_TVITEMDATA*)m_pTreeCtrl->GetItemData(hItemRoot));
|
|
m_pTreeCtrl->SetItemState(hItemRoot, TVIS_EXPANDEDONCE, TVIS_EXPANDEDONCE);
|
|
}
|
|
pszNext = STRTOK_S(NULL, _T("\\/"), &lpszContext);
|
|
if (pszNext)
|
|
{
|
|
// Move down a level
|
|
hItemRoot = m_pTreeCtrl->GetChildItem(hItemRoot);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hItemRoot = m_pTreeCtrl->GetNextSiblingItem(hItemRoot);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bLock)
|
|
{
|
|
m_pTreeCtrl->UnlockWindowUpdate();
|
|
}
|
|
|
|
m_bTunneling = false;
|
|
|
|
// make sure list gets updated.
|
|
SelectionChanged(m_pTreeCtrl->GetSelectedItem(), strFindPath);
|
|
return bFound;
|
|
}
|
|
|
|
|
|
|
|
BOOL CXTShellTreeBase::GetFolderItemPath(HTREEITEM hItem, CString &strFolderPath)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
// Long pointer to TreeView item data
|
|
XT_TVITEMDATA* lptvid = (XT_TVITEMDATA*)m_pTreeCtrl->GetItemData(hItem);
|
|
if (lptvid && lptvid->lpsfParent && lptvid->lpi)
|
|
{
|
|
LPSHELLFOLDER lpsf = NULL;
|
|
if (SUCCEEDED(lptvid->lpsfParent->BindToObject(lptvid->lpi,
|
|
0, IID_IShellFolder, (LPVOID *)&lpsf)))
|
|
{
|
|
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))
|
|
{
|
|
TCHAR szBuff[MAX_PATH];
|
|
if (::SHGetPathFromIDList(lptvid->lpifq, szBuff))
|
|
{
|
|
strFolderPath = szBuff;
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
}
|
|
if (lpsf)
|
|
{
|
|
lpsf->Release();
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// FUNCTION: XTFuncPathFindNextComponent()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CString CXTShellTreeBase::PathFindNextComponent(const CString& pszPath)
|
|
{
|
|
// Find the path delimiter
|
|
int nIndex = pszPath.Find(_T('\\'));
|
|
|
|
if (nIndex == -1)
|
|
return _T("");
|
|
|
|
return pszPath.Mid(nIndex + 1);
|
|
}
|
|
|
|
void CXTShellTreeBase::PopulateTree(LPCTSTR lpszPath)
|
|
{
|
|
CString strFolder = lpszPath;
|
|
CString strNextFolder;
|
|
CString strPath;
|
|
|
|
CShellMalloc lpMalloc;
|
|
if (!lpMalloc)
|
|
return;
|
|
|
|
// Get a pointer to the desktop folder.
|
|
LPSHELLFOLDER lpSF = NULL;
|
|
if (FAILED(::SHGetDesktopFolder(&lpSF)))
|
|
return;
|
|
|
|
LPITEMIDLIST lpIDL = NULL;
|
|
|
|
// turn off redraw and remove all tree items.
|
|
m_pTreeCtrl->SetRedraw(FALSE);
|
|
m_pTreeCtrl->DeleteAllItems();
|
|
|
|
do
|
|
{
|
|
// Get the Next Component
|
|
strNextFolder = PathFindNextComponent(strFolder);
|
|
if (!strNextFolder.IsEmpty())
|
|
{
|
|
strPath = strFolder.Left(strFolder.GetLength() -
|
|
strNextFolder.GetLength());
|
|
}
|
|
else
|
|
{
|
|
strPath = strFolder;
|
|
strNextFolder.Empty();
|
|
}
|
|
|
|
// Get ShellFolder Pidl
|
|
ULONG eaten;
|
|
if (FAILED(lpSF->ParseDisplayName(NULL, NULL, (LPOLESTR)XTP_CT2CW(strPath),
|
|
&eaten, &lpIDL, NULL)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
LPSHELLFOLDER lpSF2 = NULL;
|
|
if (FAILED(lpSF->BindToObject(lpIDL, 0, IID_IShellFolder, (LPVOID*)&lpSF2)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
lpMalloc.Free(lpIDL);
|
|
|
|
// Release the Parent Folder pointer.
|
|
lpSF->Release();
|
|
|
|
// Change Folder Info
|
|
lpSF = lpSF2;
|
|
strFolder = strNextFolder;
|
|
}
|
|
while (!strNextFolder.IsEmpty());
|
|
|
|
// get the base folders item ide list.
|
|
lpIDL = IDLFromPath(lpszPath);
|
|
|
|
SHFILEINFO fileInfo;
|
|
::ZeroMemory(&fileInfo, sizeof(fileInfo));
|
|
|
|
::SHGetFileInfo((LPCTSTR)lpIDL, NULL, &fileInfo, sizeof(fileInfo),
|
|
SHGFI_PIDL | SHGFI_ATTRIBUTES | SHGFI_DISPLAYNAME);
|
|
|
|
TV_ITEM tvi;
|
|
tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
|
|
|
|
// Allocate memory for ITEMDATA struct
|
|
XT_TVITEMDATA* lptvid = new XT_TVITEMDATA;
|
|
if (lptvid != NULL)
|
|
{
|
|
HTREEITEM hItem = TVI_ROOT;
|
|
|
|
// get the normal and selected icons for the path.
|
|
GetNormalAndSelectedIcons(lpIDL, &tvi);
|
|
|
|
// Now, make a copy of the ITEMIDLIST and store the parent folders SF.
|
|
lptvid->lpi = DuplicateItem(lpMalloc, lpIDL);
|
|
lptvid->lpsfParent = NULL;
|
|
lptvid->lpifq = ConcatPidls(lpMalloc, NULL, lpIDL);
|
|
|
|
TCHAR szBuff[MAX_PATH];
|
|
STRCPY_S(szBuff, MAX_PATH, fileInfo.szDisplayName);
|
|
|
|
tvi.lParam = (LPARAM)lptvid;
|
|
tvi.pszText = szBuff;
|
|
tvi.cchTextMax = MAX_PATH;
|
|
|
|
// Populate the TreeView 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.
|
|
TV_INSERTSTRUCT tvins;
|
|
tvins.item = tvi;
|
|
tvins.hInsertAfter = hItem;
|
|
tvins.hParent = hItem;
|
|
|
|
// Add the item to the tree
|
|
hItem = m_pTreeCtrl->InsertItem(&tvins);
|
|
|
|
// insert child items.
|
|
InitTreeViewItems(lpSF, lpIDL, hItem);
|
|
|
|
// Sort the items in the tree view
|
|
SortChildren(TVI_ROOT);
|
|
|
|
// expand parent.
|
|
m_pTreeCtrl->Expand(hItem, TVE_EXPAND);
|
|
}
|
|
|
|
// turn on redraw and refresh tree.
|
|
m_pTreeCtrl->SetRedraw(TRUE);
|
|
m_pTreeCtrl->RedrawWindow();
|
|
|
|
lpMalloc.Free(lpIDL);
|
|
|
|
if (lpSF)
|
|
{
|
|
lpSF->Release();
|
|
}
|
|
}
|
|
|
|
void CXTShellTreeBase::AssociateCombo(CWnd* pWnd)
|
|
{
|
|
ASSERT_VALID(pWnd); // must be a valid window.
|
|
|
|
if (::IsWindow(pWnd->GetSafeHwnd()))
|
|
{
|
|
m_pComboBox = pWnd;
|
|
|
|
if (m_pComboBox->IsKindOf(RUNTIME_CLASS(CXTComboBoxEx)))
|
|
{
|
|
HIMAGELIST hImageList = GetSystemImageList(SHGFI_SMALLICON);
|
|
|
|
if (hImageList != NULL)
|
|
{
|
|
CXTComboBoxEx* pComboBoxEx = DYNAMIC_DOWNCAST(CXTComboBoxEx, m_pComboBox);
|
|
ASSERT_VALID(pComboBoxEx);
|
|
pComboBoxEx->SetImageList(CImageList::FromHandle(hImageList));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL CXTShellTreeBase::OnEraseBkgnd(CDC* /*pDC*/)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
void CXTShellTreeBase::OnPaint()
|
|
{
|
|
CPaintDC dc(m_pTreeCtrl);
|
|
DoPaint(dc, FALSE);
|
|
}
|
|
|
|
void CXTShellTreeBase::BeginDrag(NM_TREEVIEW* pNMTreeView)
|
|
{
|
|
// Long pointer to ListView item data
|
|
XT_TVITEMDATA* lplvid = (XT_TVITEMDATA*)m_pTreeCtrl->GetItemData(pNMTreeView->itemNew.hItem);
|
|
ASSERT(lplvid);
|
|
|
|
if (lplvid && lplvid->lpsfParent)
|
|
{
|
|
LPDATAOBJECT lpdo;
|
|
|
|
HRESULT hResult = lplvid->lpsfParent->GetUIObjectOf(AfxGetMainWnd()->m_hWnd, 1,
|
|
(const struct _ITEMIDLIST**)&lplvid->lpi, IID_IDataObject, 0, (LPVOID*)&lpdo);
|
|
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
LPDROPSOURCE lpds = new CXTDropSource();
|
|
ASSERT(lpds != NULL);
|
|
|
|
DWORD dwEffect;
|
|
::DoDragDrop(lpdo, lpds,
|
|
DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK, &dwEffect);
|
|
|
|
lpdo->Release();
|
|
lpds->Release();
|
|
}
|
|
}
|
|
}
|