/** * Copyright (C) 2014-2050 SOUI团队 * All rights reserved. * * @file stree.hpp * @brief 树模板--提供对一般的数据类型实现树结构 * @version v1.0 * @author soui * @date 2014-07-13 * * Describe 此类是下拉窗口的父类 只需要派生该类即可 * * version: 1.0 2003-10-24 实现基本功能 * 2.0 2004-12-29 增加两个遍历接口,修改内存释放部分可能存在的bug * 2.1 2006-10-17 为节点增加一个hChildLast数据,以加快在数据插入时的速度 * 2.2 2008-10-16 修改一个遍历接口的问题 * 2.3 2011-10-17 将数据释放的接口从回调函数改成虚函数 */ #if !defined(AFX_STREE_H__D2332B4E_2C7E_4357_BE22_EC55BF496C1C__INCLUDED_) #define AFX_STREE_H__D2332B4E_2C7E_4357_BE22_EC55BF496C1C__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #ifndef SASSERT #define SASSERT(x) #endif //typedef ULONG_PTR HSTREEITEM; #define STVI_ROOT ((HSTREEITEM)0xFFFF0000)//=TVI_ROOT #define STVI_FIRST ((HSTREEITEM)0xFFFF0001)//=TVI_FIRST #define STVI_LAST ((HSTREEITEM)0xFFFF0002)//=TVI_LAST #define STVN_ROOT ((HSTREENODE)(ULONG_PTR)0xFFFF0000)//=STVN_ROOT #define STVN_FIRST ((HSTREENODE)(ULONG_PTR)0xFFFF0001)//=STVN_FIRST #define STVN_LAST ((HSTREENODE)(ULONG_PTR)0xFFFF0002)//=STVN_LAST #define STVL_ROOT ((PSTREELINK)(ULONG_PTR)0xFFFF0000) /** * @class CSTree 模板类 * @brief CSTree 模板类 * * Describe CSTree 模板类 */ template class CSTree { /** * @class _STREENODE * @brief 节点结构体 * * Describe 节点结构体 */ typedef struct _STREENODE{ struct _STREENODE *hParent; /**< 父节点 */ struct _STREENODE *hChildFirst; /**< 第一个子节点 */ struct _STREENODE *hChildLast; /**< 最后一个子节点 */ struct _STREENODE *hPrevSibling; /**< 兄节点 */ struct _STREENODE *hNextSibling; /**< 弟节点 */ T data; /**< 数据 */ }STREENODE,*HSTREENODE; /** * @class _STREELINK * @brief 树结点的链接 * * Describe 此结构体用于连接树节点 */ typedef struct _STREELINK{ HSTREENODE hParent; /**< 父节点 */ HSTREENODE hChildFirst; /**< 第一个子节点 */ HSTREENODE hChildLast; /**< 最后一个子节点 */ HSTREENODE hPrevSibling; /**< 兄节点 */ HSTREENODE hNextSibling; /**< 弟节点 */ }STREELINK,*PSTREELINK; /** * 回调函数 * @brief 下拉窗口鼠标左键按下事件 * @param T *:当前遍历到的结点的数据 * @param LPARAM:回调时使用的参数 * @return FALSE-继续,TRUE-中断遍历 * * Describe 遍历一个树结点的回调函数原型, */ typedef BOOL (*CBTRAVERSING)(T*,LPARAM); public: /** * CSTree::CSTree * @brief 构造函数 * * Describe 构造函数 */ CSTree() { m_hRootFirst=NULL; m_hRootLast=NULL; } /** * CSTree::~CSTree * @brief 析构函数 * * Describe 析构函数 */ virtual ~CSTree(){DeleteAllItems();} /** * CSTree::DeleteAllItems * @brief 删除所有节点 * * Describe 删除所有节点 */ void DeleteAllItems(){ if(m_hRootFirst) { FreeNode(STVN_ROOT); m_hRootFirst=NULL; m_hRootLast=NULL; } } /** * CSTree::GetNextSiblingItem * @brief 获取下一个兄弟 * @param HSTREEITEM hItem -- 节点 * @return 返回HSTREEITEM * * Describe 获取下一个兄弟 */ static HSTREEITEM GetNextSiblingItem(HSTREEITEM hItem){ PSTREELINK pLink= (PSTREELINK)hItem; SASSERT(pLink&&pLink!=STVL_ROOT); return (HSTREEITEM)pLink->hNextSibling; } /** * CSTree::GetPrevSiblingItem * @brief 获取上一个兄弟 * @param HSTREEITEM hItem -- 节点 * @return 返回HSTREEITEM * * Describe 获取上一个兄弟 */ static HSTREEITEM GetPrevSiblingItem(HSTREEITEM hItem) { PSTREELINK pLink= (PSTREELINK)hItem; SASSERT(pLink&&pLink!=STVL_ROOT); return (HSTREEITEM)pLink->hPrevSibling; } /** * CSTree::GetParentItem * @brief 获取父结点 * @param HSTREEITEM hItem -- 节点 * @return 返回HSTREEITEM * * Describe 获取父结点 */ static HSTREEITEM GetParentItem(HSTREEITEM hItem) { PSTREELINK pLink= (PSTREELINK)hItem; SASSERT(pLink&&pLink!=STVL_ROOT); return (HSTREEITEM)pLink->hParent; } /** * CSTree::GetItemLevel * @brief 获取结点层数 * @param HSTREEITEM hItem -- 节点 * @return 返回int * * Describe 获取结点层数 */ static int GetItemLevel(HSTREEITEM hItem) { int nRet=-1; if(hItem==STVI_ROOT) hItem=NULL; while(hItem) { nRet++; hItem=GetParentItem(hItem); } return nRet; } /** * GetRootItem * @brief 获取指定节点的根节点 * @param HSTREEITEM hItem -- 当前节点 * @return HSTREEITEM 根节点 * * Describe */ static HSTREEITEM GetRootItem(HSTREEITEM hItem) { HSTREEITEM hParent=hItem; while(GetParentItem(hParent)) { hParent = GetParentItem(hParent); } return hParent; } /** * CSTree::GetChildItem * @brief 获取第一个子结点 * @param HSTREEITEM hItem -- 节点 * @param BOOL bFirst -- 是否第一个节点 * @return 返回HSTREEITEM * * Describe 获取第一个子结点 */ HSTREEITEM GetChildItem(HSTREEITEM hItem,BOOL bFirst=TRUE) const { HSTREENODE hsNode= (HSTREENODE)hItem; SASSERT(hsNode); if(hsNode==STVN_ROOT) { if(bFirst) return (HSTREEITEM)m_hRootFirst; else return (HSTREEITEM)m_hRootLast; } else { if(bFirst) return (HSTREEITEM)hsNode->hChildFirst; else return (HSTREEITEM)hsNode->hChildLast; } } /** * CSTree::GetChildrenCount * @brief 获取子结点数量 * @param HSTREEITEM hItem -- 节点 * @return 返回int * * Describe 获取子结点数量 */ int GetChildrenCount(HSTREEITEM hItem) const { int nRet=0; HSTREEITEM hChild=GetChildItem(hItem); while(hChild) { nRet++; hChild=GetNextSiblingItem(hChild); } return nRet; } /** * CSTree::DeleteItem * @brief 删除一个节点,可以被派生类重载 * @param HSTREEITEM hItem -- 节点 * * Describe 删除一个节点,可以被派生类重载 */ virtual void DeleteItem(HSTREEITEM hItem) { HSTREENODE hsNode= (HSTREENODE)hItem; SASSERT(hsNode); if(hsNode==STVN_ROOT) { FreeNode(STVN_ROOT); m_hRootFirst=NULL; m_hRootLast=NULL; return; } STREENODE nodeCopy=*hsNode; BOOL bRootFirst=hsNode==m_hRootFirst; BOOL bRootLast=hsNode==m_hRootLast; FreeNode(hsNode); if(nodeCopy.hPrevSibling)//has prevsibling nodeCopy.hPrevSibling->hNextSibling=nodeCopy.hNextSibling; else if(nodeCopy.hParent)//parent's first child nodeCopy.hParent->hChildFirst=nodeCopy.hNextSibling; if(nodeCopy.hNextSibling)// update next sibling's previous sibling nodeCopy.hNextSibling->hPrevSibling=nodeCopy.hPrevSibling; else if(nodeCopy.hParent)//parent's last child nodeCopy.hParent->hChildLast=nodeCopy.hPrevSibling; //update root item if(bRootFirst) m_hRootFirst=nodeCopy.hNextSibling; if(bRootLast) m_hRootLast=nodeCopy.hPrevSibling; } /** * CSTree::DeleteItemEx * @brief 删除一个结点分枝,如果该结点的父结点没有其它子节点则一起删除 * @param HSTREEITEM hItem -- 节点 * @return 返回BOOL * * Describe 删除一个结点分枝,如果该结点的父结点没有其它子节点则一起删除 */ BOOL DeleteItemEx(HSTREEITEM hItem) { if(GetChildItem(hItem)) return FALSE; while(hItem && !GetChildItem(hItem)) { HSTREEITEM hParent=GetParentItem(hItem); DeleteItem(hItem); hItem=hParent; } return TRUE; } /** * CSTree::GetItem * @brief 获取结点中保存的数据 * @param HSTREEITEM hItem -- 节点 * @return 返回T * * Describe 获取结点中保存的数据 */ static T GetItem(HSTREEITEM hItem){ SASSERT(hItem!=STVI_ROOT); HSTREENODE hsNode= (HSTREENODE)hItem; SASSERT(hsNode); return hsNode->data; } /** * CSTree::GetItem * @brief 获取结点中保存的数据 * @param HSTREEITEM hItem -- 节点 * @return 返回T * * Describe 获取结点中保存的数据 */ static T& GetItemRef(HSTREEITEM hItem){ SASSERT(hItem!=STVI_ROOT); HSTREENODE hsNode= (HSTREENODE)hItem; SASSERT(hsNode); return hsNode->data; } /** * CSTree::GetItemPt * @brief 获取结点中保存的数据 * @param HSTREEITEM hItem -- 节点 * @return 返回T* * * Describe 获取结点中保存的数据 */ static T *GetItemPt(HSTREEITEM hItem){ SASSERT(hItem!=STVI_ROOT); HSTREENODE hsNode= (HSTREENODE)hItem; SASSERT(hsNode); return &hsNode->data; } /** * CSTree::InsertItem * @brief 插入一个新结点 * @param const T &data -- 结点数据 * @param HSTREEITEM hParent -- 新结点的父结点 * @param HSTREEITEM hInsertAfter -- 新结点的前一个兄弟结点 * @return 返回HSTREEITEM 新结点的指针 * * Describe 获取结点中保存的数据 */ HSTREEITEM InsertItem(const T &data,HSTREEITEM hParent=STVI_ROOT,HSTREEITEM hInsertAfter=STVI_LAST){ HSTREENODE hParentNode=(HSTREENODE) hParent; HSTREENODE hInsertAfterNode=(HSTREENODE) hInsertAfter; if(hParentNode==STVN_ROOT) hParentNode=NULL; SASSERT(hInsertAfter); if(hInsertAfterNode!=STVN_FIRST && hInsertAfterNode!=STVN_LAST) { if(hInsertAfterNode->hParent!=hParentNode) return NULL; if(hInsertAfterNode->hNextSibling==NULL) hInsertAfterNode=STVN_LAST; } HSTREENODE hInserted=new STREENODE; hInserted->data=data; hInserted->hParent=hParentNode; hInserted->hChildFirst=NULL; hInserted->hChildLast=NULL; if(hInsertAfterNode==STVN_FIRST) { hInserted->hPrevSibling=NULL; if(hParentNode==NULL)//root { hInserted->hNextSibling=m_hRootFirst; if(m_hRootFirst) m_hRootFirst->hPrevSibling=hInserted; m_hRootFirst=hInserted; if(m_hRootLast==NULL) m_hRootLast=hInserted; }else //has parent { hInserted->hNextSibling=hParentNode->hChildFirst; if(hInserted->hNextSibling) { hInserted->hNextSibling->hPrevSibling=hInserted; hParentNode->hChildFirst=hInserted; }else { hParentNode->hChildLast=hParentNode->hChildFirst=hInserted; } } }else if(hInsertAfterNode==STVN_LAST) { hInserted->hNextSibling=NULL; if(hParentNode==NULL)//root { hInserted->hPrevSibling=m_hRootLast; if(m_hRootLast) m_hRootLast->hNextSibling=hInserted; m_hRootLast=hInserted; if(!m_hRootFirst) m_hRootFirst=hInserted; }else { hInserted->hPrevSibling=hParentNode->hChildLast; if(hParentNode->hChildLast) { hInserted->hPrevSibling->hNextSibling=hInserted; hParentNode->hChildLast=hInserted; }else { hParentNode->hChildLast=hParentNode->hChildFirst=hInserted; } } }else { HSTREENODE hNextSibling=hInsertAfterNode->hNextSibling; hInserted->hPrevSibling=hInsertAfterNode; hInserted->hNextSibling=hNextSibling; hNextSibling->hPrevSibling = hInsertAfterNode->hNextSibling = hInserted; } return (HSTREEITEM)hInserted; } /** * CSTree::TraversingRecursion * @brief 采用递归方式遍历一个树结点 * @param HSTREEITEM hItem -- 起始结点 * @param CBTRAVERSING funTraversing -- 执行实际操作的回调函数 * @param LPARAM lParam -- 回调时使用的参数 * @return 返回HSTREEITEM * * Describe 采用递归方式遍历一个树结点 */ HSTREEITEM TraversingRecursion(HSTREEITEM hItem,CBTRAVERSING funTraversing,LPARAM lParam) { SASSERT(hItem); if(hItem!=STVI_ROOT) { if(funTraversing(GetItemPt(hItem),lParam)) return hItem; } HSTREEITEM hChild=GetChildItem(hItem); while(hChild) { HSTREEITEM hTmp=GetChildItem(hChild); if(hTmp) { HSTREEITEM hRet=TraversingRecursion(hTmp,funTraversing,lParam); if(hRet) return hRet; }else { if(funTraversing(GetItemPt(hChild),lParam)) return hChild; } hChild=GetNextSiblingItem(hChild); } return NULL; } /** * CSTree::TraversingSequence * @brief 按顺序方式从指定结点开始查找后面的结点,包括自己的子节点及自己向下的兄弟结点 * @param HSTREEITEM hItem -- 起始结点 * @param CBTRAVERSING funTraversing -- 执行实际操作的回调函数 * @param LPARAM lParam -- 回调时使用的参数 * @return 返回HSTREEITEM * * Describe 按顺序方式从指定结点开始查找后面的结点,包括自己的子节点及自己向下的兄弟结点 */ HSTREEITEM TraversingSequence(HSTREEITEM hItem,CBTRAVERSING funTraversing,LPARAM lParam) { if(!m_hRootFirst) return NULL; if(hItem!=STVI_ROOT) { if(funTraversing(GetItemPt(hItem),lParam)) return hItem; } HSTREEITEM hNext=GetNextItem(hItem); while(hNext) { if(funTraversing(GetItemPt(hNext),lParam)) return hNext; hNext=GetNextItem(hNext); } return NULL; } /** * CSTree::GetRootItem * @brief 获取根节点 * @param BOOL bFirst -- 是否从根节点 * @return 返回HSTREEITEM * * Describe 获取根节点 */ HSTREEITEM GetRootItem(BOOL bFirst=TRUE){ return (HSTREEITEM)(bFirst?m_hRootFirst:m_hRootLast); } /** * CSTree::GetDesendants * @brief 获取结点的子孙结点数 * @param HSTREEITEM hItem -- 起始结点 * @return 返回int * * Describe 获取结点的子孙结点数 */ int GetDesendants(HSTREEITEM hItem) { int nRet=0; HSTREEITEM hChild=GetChildItem(hItem); while(hChild) { nRet += 1+GetDesendants(hChild); hChild=GetNextSiblingItem(hChild); } return nRet; } /** * CSTree::GetNextItem * @brief 获取当前结点的下一个结点 * @param HSTREEITEM hItem -- 当前结点 * @return 返回HSTREEITEM 当前结点的下一个结点 * * Describe 获取当前结点的下一个结点 * 如果当前结点有子结点,则返回自己的第一个子结点, * 否则如果有向下的兄弟结点,则返回自己向下兄弟结点、 * 否则搜索自己的父结点的向下兄弟结点 */ HSTREEITEM GetNextItem(HSTREEITEM hItem) const { if(hItem==STVI_ROOT) return (HSTREEITEM)m_hRootFirst; HSTREEITEM hRet=GetChildItem(hItem); if(hRet) return hRet; HSTREEITEM hParent=hItem; while(hParent) { hRet=GetNextSiblingItem(hParent); if(hRet) return hRet; hParent=GetParentItem(hParent); } return NULL; } /** * CSTree::GetNextItem * @brief 获取当前结点的下一个结点 * @param HSTREEITEM hItem -- 当前结点 * @param int &nLevel -- 当前结点(hItem)与目标结点(return)的层次关系, * 1-父子关系,0-兄弟关系,-n-子->父的兄弟 * @return 返回HSTREEITEM 当前结点的下一个结点 * * Describe 获取当前结点的下一个结点 * 如果当前结点有子结点,则返回自己的第一个子结点, * 否则如果有向下的兄弟结点,则返回自己向下兄弟结点、 * 否则搜索自己的父结点的向下兄弟结点 */ HSTREEITEM GetNextItem(HSTREEITEM hItem,int &nLevel) const { if(hItem==STVI_ROOT) { nLevel=1; return (HSTREEITEM)m_hRootFirst; } HSTREEITEM hRet=GetChildItem(hItem); if(hRet) { nLevel=1; return hRet; } HSTREEITEM hParent=hItem; nLevel=0; while(hParent) { hRet=GetNextSiblingItem(hParent); if(hRet) return hRet; nLevel--; hParent=GetParentItem(hParent); } return NULL; } void SortChildren(HSTREEITEM hItem,int (__cdecl *funSort)(void *,const void *,const void *), void * pCtx) { int nChilds = GetChildrenCount(hItem); if(nChilds>1) { HSTREEITEM *pChilds = new HSTREEITEM[nChilds]; HSTREEITEM hChild = GetChildItem(hItem); pChilds[0] = hChild; for(int i=1;ihNextSibling = (HSTREENODE)pChilds[i+1]; } for(int i=1;ihPrevSibling = (HSTREENODE)pChilds[i-1]; } HSTREENODE node = (HSTREENODE)pChilds[0]; node->hPrevSibling = NULL; node = (HSTREENODE)pChilds[nChilds-1]; node->hNextSibling = NULL; if(hItem!=STVI_ROOT) { HSTREENODE parent = (HSTREENODE)hItem; parent->hChildFirst = (HSTREENODE)pChilds[0]; parent->hChildLast = (HSTREENODE)pChilds[nChilds-1]; }else { m_hRootFirst = (HSTREENODE)pChilds[0]; m_hRootLast = (HSTREENODE)pChilds[nChilds-1]; } delete []pChilds; } //子节点排序 HSTREEITEM hChild = GetChildItem(hItem); while(hChild) { HSTREENODE node = (HSTREENODE)hChild; if(node->hChildFirst && node->hChildLast && node->hChildFirst != node->hChildLast) { SortChildren(hChild,funSort,pCtx); } hChild = GetNextSiblingItem(hChild); } } private: /** * CSTree::FreeNode * @brief 采用后序遍历的方式释放结点占用的空间 * @param HSTREENODE hsNode -- 当前结点 * * Describe 采用后序遍历的方式释放结点占用的空间 */ void FreeNode(HSTREENODE hsNode) { SASSERT(hsNode); HSTREENODE hSibling=(HSTREENODE)GetChildItem((HSTREEITEM)hsNode); while(hSibling) { HSTREENODE hNextSibling=hSibling->hNextSibling; FreeNode(hSibling); hSibling=hNextSibling; } if(hsNode!=STVN_ROOT) { OnNodeFree(hsNode->data); delete hsNode; } } protected: /** * CSTree::OnNodeFree * @brief 在派生类中实现数据的释放操作 * @param T & data -- 数据 * * Describe 在派生类中实现数据的释放操作 */ virtual void OnNodeFree(T & data){} HSTREENODE m_hRootFirst; /**< 第一个根节点 */ HSTREENODE m_hRootLast; /**< 最后一个根节点 */ }; #endif // !defined(AFX_STREE_H__D2332B4E_2C7E_4357_BE22_EC55BF496C1C__INCLUDED_)