You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1229 lines
29 KiB
C++

// XTPShellTreeView.cpp : implementation file
//
// This file is a part of the XTREME CONTROLS MFC class library.
// (c)1998-2012 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 "Common/XTPSystemHelpers.h"
#include "Common/XTPWinThemeWrapper.h"
#include "XTPDropSource.h"
#include "../Tree/XTPTreeBase.h"
#include "XTPShellSettings.h"
#include "XTPShellPidl.h"
#include "XTPShellTreeBase.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CXTPShellTreeBase
CXTPShellTreeBase::CXTPShellTreeBase()
: m_bTunneling(false)
{
m_bContextMenu = TRUE;
m_pComboBox = NULL;
m_uFlags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
m_ulSFGAOFlags = SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_DISPLAYATTRMASK | SFGAO_REMOVABLE | SFGAO_COMPRESSED | SFGAO_ENCRYPTED;
if (m_shSettings.ShowAllFiles() && !m_shSettings.ShowSysFiles())
{
m_uFlags |= SHCONTF_INCLUDEHIDDEN;
}
m_nRootFolder = CSIDL_DESKTOP;
m_bShowFiles = FALSE;
m_bShowShellLinkIcons = FALSE;
m_bExplorerStyle = FALSE;
}
CXTPShellTreeBase::~CXTPShellTreeBase()
{
}
/////////////////////////////////////////////////////////////////////////////
// CXTPShellTreeBase message handlers
HTREEITEM CXTPShellTreeBase::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
XTP_TVITEMDATA* lptvid = new XTP_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 CXTPShellTreeBase::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);
}
// get the item path.
CString strFolderPath;
GetFolderItemPath(hItemChild, strFolderPath);
// call virtual change notify method.
SelectionChanged(hItemChild, strFolderPath);
}
else
{
m_pTreeCtrl->Select(hItemRoot, TVGN_CARET);
// get the item path.
CString strFolderPath;
GetFolderItemPath(hItemRoot, strFolderPath);
// call virtual change notify method.
SelectionChanged(hItemRoot, strFolderPath);
}
// turn on redraw and refresh tree.
m_pTreeCtrl->SetRedraw(TRUE);
m_pTreeCtrl->RedrawWindow();
m_pTreeCtrl->SetFocus();
}
void CXTPShellTreeBase::SetAttributes(HTREEITEM hItem, DWORD dwAttributes)
{
if (hItem != NULL)
{
// set the display attributes for the tree item.
if (dwAttributes != 0)
{
MapShellFlagsToItemAttributes(m_pTreeCtrl, hItem, dwAttributes);
// the item is compress or encrypted, show alt color.
if (m_shSettings.ShowCompColor())
{
if (dwAttributes & SFGAO_COMPRESSED)
SetItemColor(hItem, m_shSettings.m_crCompColor);
else if (dwAttributes & SFGAO_ENCRYPTED)
SetItemColor(hItem, m_shSettings.m_crEncrColor);
}
}
}
}
BOOL CXTPShellTreeBase::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;
if (lpe == NULL)
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.
// Determine what type of object we have.
ULONG ulAttrs = GetAttributes(lpsf, lpi, m_ulSFGAOFlags);
// 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;
XTP_TVITEMDATA* lptvid = new XTP_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 CXTPShellTreeBase::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 CXTPShellTreeBase::OnFolderExpanding(NM_TREEVIEW* pNMTreeView)
{
if (!(pNMTreeView->itemNew.state & TVIS_EXPANDEDONCE))
{
// Long pointer to TreeView item data
XTP_TVITEMDATA* lptvid = (XTP_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 CXTPShellTreeBase::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
XTP_TVITEMDATA* lptvid = (XTP_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 CXTPShellTreeBase::SortChildren(HTREEITEM hParent)
{
TV_SORTCB tvscb;
tvscb.hParent = hParent;
tvscb.lParam = 1;
tvscb.lpfnCompare = TreeViewCompareProc;
m_pTreeCtrl->SortChildrenCB(&tvscb);
}
BOOL CXTPShellTreeBase::OnFolderSelected(NM_TREEVIEW* pNMTreeView, CString &strFolderPath)
{
// check tree item handle.
if (!pNMTreeView || !pNMTreeView->itemNew.hItem)
return FALSE;
// set a reference to the tree item struct.
TVITEM& tvi = pNMTreeView->itemNew;
// Long pointer to TreeView item data
XTP_TVITEMDATA* lptvid = (XTP_TVITEMDATA*)m_pTreeCtrl->GetItemData(tvi.hItem);
if (lptvid && lptvid->lpi && lptvid->lpsfParent)
{
LPSHELLFOLDER lpsf = NULL;
if (SUCCEEDED(lptvid->lpsfParent->BindToObject(lptvid->lpi,
0, IID_IShellFolder, (LPVOID*)&lpsf)))
{
if (m_pTreeCtrl->ItemHasChildren(tvi.hItem) && (tvi.state & TVIS_EXPANDEDONCE) == 0)
{
InitTreeViewItems(lpsf, lptvid->lpifq, tvi.hItem);
SortChildren(tvi.hItem);
m_pTreeCtrl->SetItemState(tvi.hItem, TVIS_EXPANDEDONCE, TVIS_EXPANDEDONCE);
}
if (lpsf)
{
lpsf->Release();
}
}
}
return GetFolderItemPath(tvi.hItem, strFolderPath);
}
BOOL CXTPShellTreeBase::InitSystemImageLists()
{
HIMAGELIST himlSmall = GetSystemImageList(SHGFI_SMALLICON);
if (himlSmall)
{
TreeView_SetImageList(m_pTreeCtrl->GetSafeHwnd(), himlSmall, TVSIL_NORMAL);
return TRUE;
}
return FALSE;
}
BOOL CXTPShellTreeBase::GetSelectedFolderPath(CString &strFolderPath)
{
HTREEITEM hItem = m_pTreeCtrl->GetSelectedItem();
return GetFolderItemPath(hItem, strFolderPath);
}
BOOL CXTPShellTreeBase::FindTreeItem(HTREEITEM hItem, XTP_LVITEMDATA* lplvid, BOOL bRecursively)
{
if (lplvid == NULL)
{
return FALSE;
}
if (!bRecursively)
{
hItem = m_pTreeCtrl->GetChildItem(hItem);
}
while (hItem)
{
// Long pointer to TreeView item data
XTP_TVITEMDATA* lptvid = (XTP_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 CXTPShellTreeBase::OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
OnFolderExpanding(pNMTreeView);
*pResult = 0;
}
void CXTPShellTreeBase::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(CComboBoxEx)))
{
CComboBoxEx* pComboBoxEx = DYNAMIC_DOWNCAST(CComboBoxEx, 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);
COMBOBOXEXITEM cbItem = { 0 };
cbItem.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_INDENT;
cbItem.iItem = 0;
cbItem.pszText = (LPTSTR)(LPCTSTR)strFolderPath;
cbItem.iImage = nImage;
cbItem.iSelectedImage = nImage;
cbItem.iIndent = 0;
pComboBoxEx->InsertItem(&cbItem);
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 CXTPShellTreeBase::OnDeleteTreeItem(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
XTP_TVITEMDATA* lptvid = (XTP_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 CXTPShellTreeBase::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
if (pNMTreeView)
{
// get a reference to the tree item struct.
TVITEM& tvi = pNMTreeView->itemNew;
// initialize the tree node.
CString strFolderPath;
if (!OnFolderSelected(pNMTreeView, strFolderPath))
{
// could not determine path from IDL, make sure
// path is empty.
strFolderPath.Empty();
}
// call virtual change notify method.
SelectionChanged(tvi.hItem, strFolderPath);
// mimic modern Windows Explorer behavior.
if (XTPSystemVersion()->IsWinXPOrGreater())
{
// expand selected item.
if (tvi.hItem && m_pTreeCtrl->ItemHasChildren(tvi.hItem))
{
m_pTreeCtrl->Expand(tvi.hItem, TVE_EXPAND);
}
// collapse siblings.
if (tvi.hItem != TVI_ROOT)
{
HTREEITEM hItem = m_pTreeCtrl->GetChildItem(
m_pTreeCtrl->GetParentItem(tvi.hItem));
while (hItem != NULL)
{
if (hItem != tvi.hItem)
{
m_pTreeCtrl->Expand(hItem, TVE_COLLAPSE);
}
hItem = m_pTreeCtrl->GetNextSiblingItem(hItem);
}
}
}
}
*pResult = 0;
}
void CXTPShellTreeBase::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 CXTPShellTreeBase::InitializeTree(DWORD dwStyle/*= TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT*/)
{
if (m_pTreeCtrl->GetImageList(TVSIL_NORMAL) == NULL)
{
// Initialize the system image list for the tree control.
VERIFY(InitSystemImageLists());
// Set the style for the tree control, remove styles that change depending on OS.
m_pTreeCtrl->ModifyStyle(TVS_HASLINES | TVS_LINESATROOT | TVS_TRACKSELECT, dwStyle);
// Make sure multi-select mode is disabled.
EnableMultiSelect(FALSE);
return TRUE;
}
return FALSE;
}
#ifndef TVM_SETEXTENDEDSTYLE
#define TVM_SETEXTENDEDSTYLE (TV_FIRST + 44)
#endif
#ifndef TVS_EX_DOUBLEBUFFER
#define TVS_EX_DOUBLEBUFFER 0x0004
#define TVS_EX_FADEINOUTEXPANDOS 0x0040
#endif
void CXTPShellTreeBase::SetExplorerStyle()
{
if (XTPSystemVersion()->IsWinVistaOrGreater())
{
m_bExplorerStyle = TRUE;
CXTPWinThemeWrapper().SetWindowTheme(m_pTreeCtrl->GetSafeHwnd(), L"EXPLORER", NULL);
m_pTreeCtrl->ModifyStyle(TVS_HASLINES, 0);
DWORD dwExStyle = TVS_EX_DOUBLEBUFFER | TVS_EX_FADEINOUTEXPANDOS;
::SendMessage(m_pTreeCtrl->m_hWnd, TVM_SETEXTENDEDSTYLE, (WPARAM)dwExStyle, (LPARAM)dwExStyle);
}
}
void CXTPShellTreeBase::InitTreeNode(HTREEITEM hItem, XTP_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 CXTPShellTreeBase::SearchTree(HTREEITEM hItem, LPCITEMIDLIST pABSPidl)
{
XTP_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 = (XTP_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, (XTP_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 CXTPShellTreeBase::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, (XTP_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, (XTP_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, (XTP_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 CXTPShellTreeBase::GetFolderItemPath(HTREEITEM hItem, CString& strFolderPath)
{
if (hItem == NULL)
return FALSE;
XTP_TVITEMDATA* lptvid = (XTP_TVITEMDATA*)m_pTreeCtrl->GetItemData(hItem);
if (lptvid && lptvid->lpi)
{
if (lptvid->lpsfParent)
{
// Determine what type of object we have.
ULONG ulAttrs = SFGAO_FILESYSTEM;
lptvid->lpsfParent->GetAttributesOf(1, (const struct _ITEMIDLIST **)&lptvid->lpi, &ulAttrs);
// if the object is a file, folder or root set the path.
if (ulAttrs & SFGAO_FILESYSTEM)
{
TCHAR szFolderPath[_MAX_PATH];
if (::SHGetPathFromIDList(lptvid->lpifq, szFolderPath))
{
strFolderPath = szFolderPath;
return TRUE;
}
}
}
else
{
// Determine what type of object we have.
SHFILEINFO sfi;
::ZeroMemory(&sfi, sizeof(SHFILEINFO));
::SHGetFileInfo((TCHAR*)lptvid->lpi, 0, &sfi,
sizeof(SHFILEINFO), SHGFI_PIDL | SHGFI_ATTRIBUTES);
// if the object is a file, folder or root set the path.
if (sfi.dwAttributes & SFGAO_FILESYSTEM)
{
TCHAR szFolderPath[_MAX_PATH];
if (::SHGetPathFromIDList(lptvid->lpi, szFolderPath))
{
strFolderPath = szFolderPath;
return TRUE;
}
}
}
}
return FALSE;
}
CString CXTPShellTreeBase::PathFindNextComponent(const CString& pszPath)
{
// Find the path delimiter
int nIndex = pszPath.Find(_T('\\'));
if (nIndex == -1)
return _T("");
return pszPath.Mid(nIndex + 1);
}
void CXTPShellTreeBase::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
XTP_TVITEMDATA* lptvid = new XTP_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 CXTPShellTreeBase::AssociateCombo(CWnd* pWnd)
{
ASSERT_VALID(pWnd); // must be a valid window.
if (::IsWindow(pWnd->GetSafeHwnd()))
{
m_pComboBox = pWnd;
if (m_pComboBox->IsKindOf(RUNTIME_CLASS(CComboBoxEx)))
{
HIMAGELIST hImageList = GetSystemImageList(SHGFI_SMALLICON);
if (hImageList != NULL)
{
CComboBoxEx* pComboBoxEx = DYNAMIC_DOWNCAST(CComboBoxEx, m_pComboBox);
ASSERT_VALID(pComboBoxEx);
pComboBoxEx->SetImageList(CImageList::FromHandle(hImageList));
}
}
}
}
BOOL CXTPShellTreeBase::OnEraseBkgnd(CDC* /*pDC*/)
{
return TRUE;
}
void CXTPShellTreeBase::OnPaint()
{
CPaintDC dc(m_pTreeCtrl);
DoPaint(dc, !m_bExplorerStyle);
}
void CXTPShellTreeBase::BeginDrag(NM_TREEVIEW* pNMTreeView)
{
// Long pointer to ListView item data
XTP_TVITEMDATA* lplvid = (XTP_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 CXTPDropSource();
ASSERT(lpds != NULL);
DWORD dwEffect;
::DoDragDrop(lpdo, lpds,
DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK, &dwEffect);
lpdo->Release();
lpds->Release();
}
}
}