⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 shelltreectrl.cpp

📁 类似 windows explorer的工具
💻 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 + -