📄 shelltreectrl.cpp
字号:
// ShellTreeCtrl.cpp : implementation file
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2000-2001 by Paolo Messina
// (http://www.geocities.com/ppescher - ppescher@yahoo.com)
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// If you find this code useful, credits would be nice!
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ShellTreeCtrl.h"
#include "MMXShellGenelal.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#ifdef _DEBUG
//int CShellTreeCtrl::TVITEMDATA::s_constructNum = 0;
//int CShellTreeCtrl::TVITEMDATA::s_destructNum = 0;
int TVITEMDATA::s_constructNum = 0;
int TVITEMDATA::s_destructNum = 0;
#endif
/////////////////////////////////////////////////////////////////////////////
// CShellTreeCtrl
// 初始化静态成员
SDesktopFolderPtr CShellTreeCtrl::m_pDesktopFolder;
CShellTreeCtrl::CShellTreeCtrl()
{
m_nCallbackMask = 0;
}
CShellTreeCtrl::~CShellTreeCtrl()
{
#ifdef _DEBUG
CString strDebug;
strDebug.Format("TVITEMDATA 构造 %d 次,析构 %d 次\n",
//CShellTreeCtrl::TVITEMDATA::s_constructNum,
//CShellTreeCtrl::TVITEMDATA::s_destructNum);
TVITEMDATA::s_constructNum,
TVITEMDATA::s_destructNum);
OutputDebugString(strDebug);
//OutputDebugString("Leave CShellObject::~CShellObject()\n");
#endif
}
BEGIN_MESSAGE_MAP(CShellTreeCtrl, CWaitingTreeCtrl)
//{{AFX_MSG_MAP(CShellTreeCtrl)
ON_NOTIFY_REFLECT(TVN_DELETEITEM, OnDeleteItem)
ON_NOTIFY_REFLECT(TVN_GETDISPINFO, OnGetDispInfo)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CShellTreeCtrl message handlers
// [virtual]
// 展开节点 hParent(当点了'+'后,执行实际的展开动作)
BOOL CShellTreeCtrl::PopulateItem(HTREEITEM hParent)
{
if (hParent == TVI_ROOT)
{
// not handled yet, do nothing in Release builds
ASSERT(FALSE);
return TRUE;
}
TVITEMDATA* pData = (TVITEMDATA*)GetItemData(hParent);
if (pData == NULL)
return TRUE; // invalid shell item, ignore it silently
// get parent pidl
ASSERT(pData->IsValid());
CShellPidl& pidlParent = pData->pidlAbs;
if (!EnumFolderItems(hParent, pidlParent, pData->nFlags))
return TRUE; // failed, won't try anymore!
// TODO: change this method!!
// do not check for children if parent is a removable media
// (just try: if it's a filesystem object, it has a path)
TCHAR path[MAX_PATH];
if (SHGetPathFromIDList(pidlParent, path))
{
path[3] = 0;
UINT type = GetDriveType(path);
if (type != DRIVE_FIXED)
return FALSE;
}
// added by truezq
//::SendMessage(GetParent()->GetSafeHwnd(), WM_EXPANDEDTREEITEM,
// (WPARAM)hParent, 0);
return TRUE;
}
void CShellTreeCtrl::PreSubclassWindow()
{
InitializeControl();
CWaitingTreeCtrl::PreSubclassWindow();
}
void CShellTreeCtrl::InitializeControl()
{
// Attach to the system image list
CShellPidl pidl((UINT)CSIDL_DESKTOP, m_hWnd);
SHFILEINFO sfi;
ZeroMemory(&sfi, sizeof(SHFILEINFO));
HIMAGELIST hSysImageList = (HIMAGELIST) SHGetFileInfo((LPCTSTR)(LPCITEMIDLIST)pidl,
0, &sfi, sizeof(SHFILEINFO), SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
//TreeView_SetImageList(m_hWnd, hSysImageList, TVSIL_NORMAL);
// postpone imagelist attaching
// (seems it doesn't like a sendmessage when dynamically created
// maybe because it has not received the WM_CREATE message yet?)
PostMessage(TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)hSysImageList);
}
// pidlFolder = NULL,显示桌面上所有对象
void CShellTreeCtrl::AddRootFolderContent(LPCITEMIDLIST pidlFolder, UINT nFlags)
{
if (pidlFolder == NULL)
{
CShellPidl pidl((UINT)CSIDL_DESKTOP, m_hWnd);
InsertSubItem(TVI_ROOT, m_pDesktopFolder, NULL, pidl, nFlags);
return;
}
SetRedraw(FALSE);
EnumFolderItems(TVI_ROOT, pidlFolder, nFlags);
SetRedraw(TRUE);
}
int CALLBACK CShellTreeCtrl::CompareFunc(LPARAM lParam1,
LPARAM lParam2, LPARAM /*lParamSort*/)
{
TVITEMDATA* pData1 = (TVITEMDATA*)lParam1;
TVITEMDATA* pData2 = (TVITEMDATA*)lParam2;
ASSERT(pData1->IsValid() && pData2->IsValid());
// TODO: parent folders should be checked some day
SShellFolderPtr pParentFolder = pData2->pParentFolder;
HRESULT hr = pParentFolder->CompareIDs(0,
pData1->pidlAbs.GetLastChild(),
pData2->pidlAbs.GetLastChild() );
if (FAILED(hr))
return 0; // error, don't sort
short ret = (short)HRESULT_CODE(hr);
if (ret < 0)
return -1;
if (ret > 0)
return 1;
return 0;
}
void CShellTreeCtrl::FillItem(TVITEM& item)
{
DWORD dwAttributes;
// get item data
TVITEMDATA* pData = (TVITEMDATA*)item.lParam;
ASSERT(pData->IsValid());
// get a relative pidl
LPCITEMIDLIST pidlRel = pData->pidlAbs.GetLastChild();
if (item.mask & TVIF_TEXT)
{
// get display name
CString sName;
CShellString str;
if (pData->nFlags & STCF_SHOWPATH)
{
// use an absolute or relative path, if possible
sName = pData->pidlAbs.GetPath();
if (!sName.IsEmpty() && !(pData->nFlags & STCF_SHOWFULLNAME))
sName = sName.Right(sName.ReverseFind(_T('\\')));
}
if (sName.IsEmpty())
{
// use a global or contextual displayname
DWORD uDisplayFlags = SHGDN_INFOLDER;
if (pData->nFlags & STCF_SHOWFULLNAME)
uDisplayFlags = SHGDN_NORMAL;
pData->pParentFolder->GetDisplayNameOf(pidlRel, uDisplayFlags
| SHGDN_INCLUDE_NONFILESYS, str.GetPointer(pidlRel));
sName = str; // copy to string
}
// set item text
lstrcpyn(item.pszText, (LPCTSTR)sName, item.cchTextMax);
}
if (item.mask & (TVIF_IMAGE | TVIF_SELECTEDIMAGE))
{
// get some attributes
dwAttributes = SFGAO_FOLDER | SFGAO_LINK | SFGAO_SHARE | SFGAO_GHOSTED;
pData->pParentFolder->GetAttributesOf(1, &pidlRel, &dwAttributes);
// set correct icon
if (dwAttributes & SFGAO_GHOSTED)
{
item.mask |= LVIF_STATE;
item.stateMask |= LVIS_CUT;
item.state |= LVIS_CUT;
}
if (dwAttributes & SFGAO_SHARE)
{
item.mask |= LVIF_STATE;
item.state &= ~LVIS_OVERLAYMASK;
item.state |= INDEXTOOVERLAYMASK(1);
item.stateMask |= LVIS_OVERLAYMASK;
}
else if (dwAttributes & SFGAO_LINK)
{
item.mask |= LVIF_STATE;
item.state &= ~LVIS_OVERLAYMASK;
item.state |= INDEXTOOVERLAYMASK(2);
item.stateMask |= LVIS_OVERLAYMASK;
}
if (item.mask & TVIF_IMAGE)
{
item.iImage = pData->pidlAbs.GetIconIndex(SHGFI_SMALLICON);
item.iSelectedImage = item.iImage;
}
if ((item.mask & TVIF_SELECTEDIMAGE)
&& (dwAttributes & SFGAO_FOLDER))
{
item.iSelectedImage = pData->pidlAbs.GetIconIndex(SHGFI_SMALLICON
|SHGFI_OPENICON);
}
}
if (item.mask & TVIF_CHILDREN)
{
// get some attributes
dwAttributes = SFGAO_FOLDER;
pData->pParentFolder->GetAttributesOf(1, &pidlRel, &dwAttributes);
// get children
item.cChildren = 0;
if (dwAttributes & SFGAO_FOLDER)
{
if (pData->nFlags & STCF_INCLUDEFILES)
item.cChildren = 1;
else if (dwAttributes & SFGAO_REMOVABLE)
item.cChildren = 1;
else
{
dwAttributes = SFGAO_HASSUBFOLDER;
pData->pParentFolder->GetAttributesOf(1, &pidlRel, &dwAttributes);
item.cChildren = (dwAttributes & SFGAO_HASSUBFOLDER) ? 1 : 0;
}
}
}
}
void CShellTreeCtrl::OnDeleteItem(NMHDR* pNMHDR, LRESULT* pResult)
{
TVITEM& item = ((LPNMTREEVIEW)pNMHDR)->itemOld;
// free item data, ignore invalid shell items
if (item.lParam != 0)
{
// 在这里释放分配的节点附加信息
delete (TVITEMDATA*)item.lParam;
}
*pResult = 0;
}
void CShellTreeCtrl::OnGetDispInfo(NMHDR* pNMHDR, LRESULT* pResult)
{
TVITEM& item = ((LPNMTVDISPINFO)pNMHDR)->item;
// use the provided buffer for text
FillItem(item);
*pResult = 0;
}
CShellPidl CShellTreeCtrl::GetItemIDList(HTREEITEM hItem)
{
TVITEMDATA* pData = (TVITEMDATA*)GetItemData(hItem);
if (pData != NULL)
{
ASSERT(pData->IsValid());
return pData->pidlAbs;
}
return CShellPidl(); // invalid pidl
}
// 插入一个树节点
void CShellTreeCtrl::InsertSubItem(HTREEITEM hParent, LPSHELLFOLDER pParentFolder, LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidl, UINT nFlags)
{
TVINSERTSTRUCT tvis;
ZeroMemory(&tvis, sizeof(TVINSERTSTRUCT));
tvis.hParent = hParent;
tvis.hInsertAfter = TVI_LAST;
// provide a buffer for the item text
TCHAR szText[MAX_PATH];
tvis.item.pszText = szText;
tvis.item.cchTextMax = MAX_PATH;
// used fields
const UINT nTVIFlags = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE
| TVIF_CHILDREN | TVIF_PARAM;
// prepare item data
TVITEMDATA* pData = new TVITEMDATA;
pData->pidlAbs.Combine(pidlParent, pidl);
pData->pParentFolder = pParentFolder;
pData->nFlags = nFlags;
// set item data
ASSERT(pData->IsValid());
tvis.item.lParam = (LPARAM)pData;
// fill with pidl, text, icons and children - handle callbacks
tvis.item.mask = nTVIFlags & ~m_nCallbackMask;
FillItem(tvis.item); // 填充节点详细信息
if (m_nCallbackMask & TVIF_IMAGE)
tvis.item.iImage = I_IMAGECALLBACK;
if (m_nCallbackMask & TVIF_SELECTEDIMAGE)
tvis.item.iSelectedImage = I_IMAGECALLBACK;
if (m_nCallbackMask & TVIF_TEXT)
tvis.item.pszText = LPSTR_TEXTCALLBACK;
if (m_nCallbackMask & TVIF_CHILDREN)
tvis.item.cChildren = I_CHILDRENCALLBACK;
tvis.item.mask |= nTVIFlags;
// then insert new item
InsertItem(&tvis);
}
void CShellTreeCtrl::AddRootItem(LPCITEMIDLIST pidlRoot, UINT nFlags)
{
// not needed if pParentFolder is an argument
CShellPidl pidlParent;
pidlParent.CloneLastParent(pidlRoot);
SShellFolderPtr pParentFolder(m_pDesktopFolder, pidlParent);
InsertSubItem(TVI_ROOT, pParentFolder, NULL, pidlRoot, nFlags);
}
// 枚举 pidlParent 下面的子对象,然后插入到节点 hParent 下面
BOOL CShellTreeCtrl::EnumFolderItems(HTREEITEM hParent, LPCITEMIDLIST pidlParent, UINT nFlags)
{
// get parent shell folder
SShellFolderPtr pParentFolder(m_pDesktopFolder, pidlParent);
// not a valid folder object
if (!pParentFolder.IsValid())
return FALSE; // failed!
// enum child pidls
UINT nEnumFlags;
nEnumFlags = SHCONTF_FOLDERS
| ((nFlags & STCF_INCLUDEFILES) ? SHCONTF_NONFOLDERS : 0)
| ((nFlags & STCF_INCLUDEHIDDEN) ? SHCONTF_INCLUDEHIDDEN : 0);
SEnumIDListPtr pEnumIDList(pParentFolder, nEnumFlags, m_hWnd);
if (pEnumIDList.IsValid())
{
SetPopulationCount(0);
// 列举桌面上各个对象的子对象
CShellPidl pidl;
while (NOERROR == pEnumIDList->Next(1, pidl.GetPointer(), NULL))
{
if (!CMMXShellGenelal::IsShowVirtualObject(m_bIsUDisk, pParentFolder,
pidlParent,pidl))
{
continue;
}
// add child item, inherit some flags (inclusion)
InsertSubItem(hParent, pParentFolder, pidlParent, pidl,
nFlags & STCF_INCLUDEMASK);
// notify progress
IncreasePopulation();
}
}
if (GetPopulationCount() > 0)
{
// sort items
TVSORTCB tvscb;
tvscb.hParent = hParent;
tvscb.lpfnCompare = CompareFunc;
// tvscb.lParam = 0; // not meaningful yet
SortChildrenCB(&tvscb);
}
// notify progress
SetPopulationCount(1,1);
// success!
return TRUE;
}
BOOL CShellTreeCtrl::GetItemContextMenu(HTREEITEM hItem, CShellContextMenu& rCtxMenu)
{
TVITEMDATA* pData = (TVITEMDATA*)GetItemData(hItem);
if (!pData->IsValid())
return FALSE;
return rCtxMenu.Create(pData->pParentFolder,
pData->pidlAbs.GetLastChild());
}
void CShellTreeCtrl::SetCallbackMask(UINT nMask)
{
m_nCallbackMask = nMask &
(TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN);
}
UINT CShellTreeCtrl::GetCallbackMask()
{
return m_nCallbackMask;
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// 定位到某个子文件夹
void CShellTreeCtrl::GotoSubFolder(const CString& strFolderName)
{
// 展开树节点
HTREEITEM hTreeItem = GetSelectedItem();
Expand(hTreeItem,TVE_EXPAND);
// 选择节点
HTREEITEM hChildItem = GetChildItem(hTreeItem);
while (hChildItem)
{
if (GetItemText(hChildItem) == strFolderName)
{
SelectItem(hChildItem);
break;
}
hChildItem = GetNextSiblingItem(hChildItem);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -