MFC CTreeCtrl 树结构在SQLite 中的存储和还原


MFC  CTreeCtrl 树节点是在插入时确定位置,用hParent, hInsertAfter 这两个HTREEITEM 可以唯一确定节点位置。

这样增,减,移动节点后,只有两个数据变化,涉及的其他节点数据的变化也很少,比较适合存储。随机存储的节点只要有两个相关节点数据就能还原出树。

实际还原很麻烦,因为存储的是两个节点的id,相当于索引,但是还原需要的是两个插入时才生成的句柄,一边要遍历树,另一边要查表,还得按顺序,脑袋嗡嗡的,不搞了。

简单起见,用一个层数据,一个遍历时的顺序数,这两个数据,配合一个节点的标识id,来存储和还原树结构。

id 是唯一标识节点,在数据库中是自动增长的唯一非空整数。每插入一个节点,数据库自动产生唯一的增加的id。这个值始终不变,除非节点删掉。

lev,idno 一个是层,一个是遍历序号,在保存时计算出来,并存入数据库,还原时用这两个数据来还原树结构,树节点中要保存id,用来对应数据库中相关业务数据。保存是遍历树,update 节点数据。

这样插入节点,移动节点不用改变其他节点的位置数据,因为id不变,唯一确定树节点,树的结构在保存时用遍历重新计算。

 

MFC  CTreeCtrl 树结构在SQLite 中的存储和还原

 

 MFC  默认的MDI框架,主要的存取,删除,插入程序段在File View中,用POP菜单做测试。

FileView.h

#pragma once

#include "ViewTree.h"
#include "re2.h"

#include "CSQLite.h"


class CFileViewToolBar : public CMFCToolBar
{
    virtual void OnUpdateCmdUI(CFrameWnd* /*pTarget*/, BOOL bDisableIfNoHndler)
    {
        CMFCToolBar::OnUpdateCmdUI((CFrameWnd*) GetOwner(), bDisableIfNoHndler);
    }

    virtual BOOL AllowShowOnList() const { return FALSE; }
};

class CFileView : public CDockablePane
{
// Construction
public:
    CFileView();

    void AdjustLayout();
    void OnChangeVisualStyle();
            
    CSQLite m_SQLite;

    HTREEITEM m_hNodeSrc;
    HTREEITEM m_hNodeFile;

    FileTable* m_pFileTable;

    //返回值是中间过程
    void FindNode(HTREEITEM hItem, int id, HTREEITEM& hPItem);
    
    //XGZ 这2个变量在递归中全程累计
    int m_NodeNo;
    int m_NodeLev;
        
    void SaveNode(HTREEITEM hItem);
    void UpdateNode(HTREEITEM hItem);
    void ShowAllNodes(HTREEITEM hItem);

    void InsertNode(HTREEITEM hItem);
    void DeleteNode(HTREEITEM hItem);
    void DeleteAllNodes(HTREEITEM hItem);
    CString m_strSave;
    
    HTREEITEM AddFileName(LPCTSTR strFileName, DWORD_PTR dwData);
    BOOL DelFileName(HTREEITEM& hFileNode);

    HTREEITEM AddNote(LPCTSTR Name, DWORD_PTR dwData);

// Attributes
protected:

    CViewTree m_wndFileView;
    CImageList m_FileViewImages;
    CFileViewToolBar m_wndToolBar;

protected:
    void FillFileView();

// Implementation
public:
    virtual ~CFileView();

protected:
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnSize(UINT nType, int cx, int cy);
    afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
    afx_msg void OnProperties();
    afx_msg void OnFileOpen();
    afx_msg void OnFileOpenWith();
    afx_msg void OnDummyCompile();
    afx_msg void OnEditCut();
    afx_msg void OnEditCopy();
    afx_msg void OnEditClear();
    afx_msg void OnPaint();
    afx_msg void OnSetFocus(CWnd* pOldWnd);

    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnPopExpTest();
    afx_msg void OnPopExpTest2();
    afx_msg void OnPopExpInsertchild();
    afx_msg void OnPopExpInsertsibling();
    afx_msg void OnPopExpDelete();
    afx_msg void OnPopExpSave();
    afx_msg void OnPopExpUpdate();
    afx_msg void OnPopExpShowall();
};

FileView.cpp

#include "stdafx.h"
#include "mainfrm.h"
#include "FileView.h"
#include "Resource.h"
#include "RE2.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
/////////////////////////////////////////////////////////////////////////////
// CFileView
CFileView::CFileView()
{
m_pFileTable = NULL;
}
CFileView::~CFileView()
{
if (NULL != m_pFileTable)
{
delete[] m_pFileTable;
m_pFileTable = NULL;
}
}
BEGIN_MESSAGE_MAP(CFileView, CDockablePane)
ON_WM_CREATE()
ON_WM_SIZE()
ON_WM_CONTEXTMENU()
ON_COMMAND(ID_PROPERTIES, OnProperties)
ON_COMMAND(ID_OPEN, OnFileOpen)
ON_COMMAND(ID_OPEN_WITH, OnFileOpenWith)
ON_COMMAND(ID_DUMMY_COMPILE, OnDummyCompile)
ON_COMMAND(ID_EDIT_CUT, OnEditCut)
ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
ON_COMMAND(ID_EDIT_CLEAR, OnEditClear)
ON_WM_PAINT()
ON_WM_SETFOCUS()
ON_COMMAND(ID_POP_EXP_TEST, &CFileView::OnPopExpTest)
ON_COMMAND(ID_POP_EXP_TEST2, &CFileView::OnPopExpTest2)
ON_COMMAND(ID_POP_EXP_INSERTCHILD, &CFileView::OnPopExpInsertchild)
ON_COMMAND(ID_POP_EXP_INSERTSIBLING, &CFileView::OnPopExpInsertsibling)
ON_COMMAND(ID_POP_EXP_DELETE, &CFileView::OnPopExpDelete)
ON_COMMAND(ID_POP_EXP_SAVE, &CFileView::OnPopExpSave)
ON_COMMAND(ID_POP_EXP_UPDATE, &CFileView::OnPopExpUpdate)
ON_COMMAND(ID_POP_EXP_SHOWALL, &CFileView::OnPopExpShowall)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CWorkspaceBar message handlers
int CFileView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDockablePane::OnCreate(lpCreateStruct) == -1)
return -1;
CRect rectDummy;
rectDummy.SetRectEmpty();
// Create view:
const DWORD dwViewStyle = WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS;
if (!m_wndFileView.Create(dwViewStyle, rectDummy, this, 4))
{
TRACE0("Failed to create file view/n");
return -1;      // fail to create
}
// Load view images:
m_FileViewImages.Create(IDB_FILE_VIEW, 16, 0, RGB(255, 0, 255));
m_wndFileView.SetImageList(&m_FileViewImages, TVSIL_NORMAL);
m_wndToolBar.Create(this, AFX_DEFAULT_TOOLBAR_STYLE, IDR_EXPLORER);
m_wndToolBar.LoadToolBar(IDR_EXPLORER, 0, 0, TRUE /* Is locked */);
OnChangeVisualStyle();
m_wndToolBar.SetPaneStyle(m_wndToolBar.GetPaneStyle() | CBRS_TOOLTIPS | CBRS_FLYBY);
m_wndToolBar.SetPaneStyle(m_wndToolBar.GetPaneStyle() & ~(CBRS_GRIPPER | CBRS_SIZE_DYNAMIC | CBRS_BORDER_TOP | CBRS_BORDER_BOTTOM | CBRS_BORDER_LEFT | CBRS_BORDER_RIGHT));
m_wndToolBar.SetOwner(this);
// All commands will be routed via this control , not via the parent frame:
m_wndToolBar.SetRouteCommandsViaFrame(FALSE);
// Fill in some static tree view data (dummy code, nothing magic here)
FillFileView();
AdjustLayout();
m_wndFileView.ModifyStyle(0, TVS_HASLINES | TVS_HASBUTTONS | TVS_EDITLABELS);
return 0;
}
void CFileView::OnSize(UINT nType, int cx, int cy)
{
CDockablePane::OnSize(nType, cx, cy);
AdjustLayout();
}
void CFileView::FillFileView()
{
HTREEITEM hRoot = m_wndFileView.InsertItem(_T("FakeApp files"), 0, 0);
m_wndFileView.SetItemState(hRoot, TVIS_BOLD, TVIS_BOLD);
HTREEITEM hSrc = m_wndFileView.InsertItem(_T("FakeApp Source Files"), 0, 0, hRoot);
m_wndFileView.InsertItem(_T("FakeApp.cpp"), 1, 1, hSrc);
HTREEITEM hSrc1 = m_wndFileView.InsertItem(_T("FakeApp.rc"), 1, 1, hSrc);
m_wndFileView.InsertItem(_T("FakeAppDoc.cpp"), 1, 1, hSrc);
m_wndFileView.InsertItem(_T("FakeAppView.cpp"), 1, 1, hSrc);
m_wndFileView.InsertItem(_T("MainFrm.cpp"), 1, 1, hSrc);
m_wndFileView.InsertItem(_T("StdAfx.cpp"), 1, 1, hSrc, hSrc1); //插在hSrc1后面
HTREEITEM hInc = m_wndFileView.InsertItem(_T("FakeApp Header Files"), 0, 0, hRoot);
m_wndFileView.InsertItem(_T("FakeApp.h"), 2, 2, hInc);
m_wndFileView.InsertItem(_T("FakeAppDoc.h"), 2, 2, hInc);
m_wndFileView.InsertItem(_T("FakeAppView.h"), 2, 2, hInc);
m_wndFileView.InsertItem(_T("Resource.h"), 2, 2, hInc);
m_wndFileView.InsertItem(_T("MainFrm.h"), 2, 2, hInc);
m_wndFileView.InsertItem(_T("StdAfx.h"), 2, 2, hInc);
HTREEITEM hRes = m_wndFileView.InsertItem(_T("FakeApp Resource Files"), 0, 0, hRoot);
m_wndFileView.InsertItem(_T("FakeApp.ico"), 2, 2, hRes);
m_wndFileView.InsertItem(_T("FakeApp.rc2"), 2, 2, hRes);
m_wndFileView.InsertItem(_T("FakeAppDoc.ico"), 2, 2, hRes);
m_wndFileView.InsertItem(_T("FakeToolbar.bmp"), 2, 2, hRes);
m_wndFileView.Expand(hRoot, TVE_EXPAND);
m_wndFileView.Expand(hSrc, TVE_EXPAND);
m_wndFileView.Expand(hInc, TVE_EXPAND);
m_hNodeSrc = hRoot;
}
void CFileView::OnContextMenu(CWnd* pWnd, CPoint point)
{
CTreeCtrl* pWndTree = (CTreeCtrl*) &m_wndFileView;
ASSERT_VALID(pWndTree);
if (pWnd != pWndTree)
{
CDockablePane::OnContextMenu(pWnd, point);
return;
}
if (point != CPoint(-1, -1))
{
// Select clicked item:
CPoint ptTree = point;
pWndTree->ScreenToClient(&ptTree);
UINT flags = 0;
HTREEITEM hTreeItem = pWndTree->HitTest(ptTree, &flags);
if (hTreeItem != NULL)
{
pWndTree->SelectItem(hTreeItem);
}
}
pWndTree->SetFocus();
theApp.GetContextMenuManager()->ShowPopupMenu(IDR_POPUP_EXPLORER, point.x, point.y, this, TRUE);
}
void CFileView::AdjustLayout()
{
if (GetSafeHwnd() == NULL)
{
return;
}
CRect rectClient;
GetClientRect(rectClient);
int cyTlb = m_wndToolBar.CalcFixedLayout(FALSE, TRUE).cy;
m_wndToolBar.SetWindowPos(NULL, rectClient.left, rectClient.top, rectClient.Width(), cyTlb, SWP_NOACTIVATE | SWP_NOZORDER);
m_wndFileView.SetWindowPos(NULL, rectClient.left + 1, rectClient.top + cyTlb + 1, rectClient.Width() - 2, rectClient.Height() - cyTlb - 2, SWP_NOACTIVATE | SWP_NOZORDER);
}
void CFileView::OnProperties()
{
AfxMessageBox(_T("Properties...."));
}
//1.树是遍历存储的,实际时update了一个序号idno,读出是按存储的顺序(idno)
//2.创建树是通过层(lev)的变化数量来寻找父节点,sibling顺序遍历存储时就确定了。
//  lev不变时是连续插入sibling节点。父节点不变
//  lev增加(只可能增加1)是在当前节点下插入子节点,父节点就是变成当前节点。
//  lev减小(按差值循环上溯父节点),在找到的父节点下插入子节点,父节点更新。
//
void CFileView::OnFileOpen()
{
CString fileName;
CString sql;
int nrow, ncolum;
int i, j;
HTREEITEM hItem;
HTREEITEM hPItem;
HTREEITEM hRoot;
CFileDialog dlgFile(TRUE, "DataBase File(*.db)|*.DB", "Test.db",
OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
"DataBase File(*.db)|*.db|RTF Files (*.rtf)|*.rtf|All Files (*.*)|*.*||", NULL);
if (IDOK == dlgFile.DoModal())
{
fileName = dlgFile.GetFileName();
if (!m_SQLite.Open(fileName.GetBuffer()))
{
PRINT("OPEN FAILED");
return;
}
PRINT("OPEN %s OK", fileName.GetBuffer());
m_wndFileView.DeleteAllItems();
sql = "select id,lev,idno,name from notesid order by idno";//lev asc, levno asc; ";
m_SQLite.Query(sql.GetBuffer(), nrow, ncolum);
PRINT("OPEN OK nrow=%d,ncloum=%d", nrow, ncolum);
if (NULL != m_pFileTable)
{
delete[] m_pFileTable;
m_pFileTable = NULL;
}
m_pFileTable = new FileTable[nrow];
for (i = 1; i < nrow + 1; i++)    //第一行是字段名称
{
//wSQLTable.Cell[i] = new SQLCell[ncolum];
m_pFileTable[i - 1].id = atoi(m_SQLite.m_sresult[i * ncolum + 0]);
m_pFileTable[i - 1].lev = atoi(m_SQLite.m_sresult[i * ncolum + 1]);
m_pFileTable[i - 1].idno = atoi(m_SQLite.m_sresult[i * ncolum + 2]);
memset(m_pFileTable[i - 1].name, 0, 256);
memcpy(m_pFileTable[i - 1].name, m_SQLite.m_sresult[i * ncolum + 3], 255);
PRINT(m_pFileTable[i - 1].name);
}
int levNow = 0;
HTREEITEM  hParent = NULL;
HTREEITEM  hCurrent = NULL;
for (i = 0; i < nrow; i++)
{
if(m_pFileTable[i].lev == levNow)
{
hCurrent = m_wndFileView.InsertItem(m_pFileTable[i].name, 0, 0, hParent);
m_wndFileView.SetItemData(hCurrent, (DWORD_PTR) & (m_pFileTable[i]));
m_pFileTable[i].hItem = hCurrent;
if(i == 0) hRoot = hCurrent;  //第一个节点肯定时根节点
}
if(m_pFileTable[i].lev > levNow)  //子节点优先
{
hParent = hCurrent;  //上个节点
hCurrent = m_wndFileView.InsertItem(m_pFileTable[i].name, 0, 0, hParent);
m_wndFileView.SetItemData(hCurrent, (DWORD_PTR) & (m_pFileTable[i]));
m_pFileTable[i].hItem = hCurrent;
levNow = m_pFileTable[i].lev;
}
if(m_pFileTable[i].lev < levNow)
{
int gap = levNow - m_pFileTable[i].lev;
for (int j = 0; j < gap; j++)
{
hParent = m_wndFileView.GetParentItem(hParent);  //上个节点
}
hCurrent = m_wndFileView.InsertItem(m_pFileTable[i].name, 0, 0, hParent);
m_wndFileView.SetItemData(hCurrent, (DWORD_PTR) & (m_pFileTable[i]));
m_pFileTable[i].hItem = hCurrent;
levNow = m_pFileTable[i].lev;
}
}
m_wndFileView.Expand(hRoot, TVE_EXPAND);
}
}
void CFileView::FindNode(HTREEITEM hItem, int id, HTREEITEM& hFindItem)
{
if (!hItem)    return ;
FileTable* pFileTable;
pFileTable = (FileTable*)m_wndFileView.GetItemData(hItem);
if (pFileTable == NULL)
{
PRINT(_T("<ERR> data fail! item = %s"),
m_wndFileView.GetItemText(hItem));
return;
}
if (pFileTable->id == id)
{
PRINT( _T("Find id=%d,Name=%s"), 
pFileTable->id, m_wndFileView.GetItemText(hItem).GetBuffer());
hFindItem = hItem;
return ;
}
HTREEITEM hChild = m_wndFileView.GetChildItem(hItem);
FindNode(hChild, id, hFindItem);
HTREEITEM hSibling = m_wndFileView.GetNextSiblingItem(hItem);
FindNode(hSibling, id, hFindItem);
return ;
}
void CFileView::OnFileOpenWith()
{
// TODO: Add your command handler code here
HTREEITEM htItem = m_wndFileView.GetSelectedItem();
FileTable* pFileTable;
pFileTable = (FileTable*)m_wndFileView.GetItemData(htItem);
PRINT("id:%d-pid:%d-sid:%d-lev:%d-lno:%d",
pFileTable->id, pFileTable->pid, pFileTable->sid,
pFileTable->lev, pFileTable->levno);
}
void CFileView::OnDummyCompile()
{
// TODO: Add your command handler code here
}
void CFileView::OnEditCut()
{
// TODO: Add your command handler code here
}
void CFileView::OnEditCopy()
{
// TODO: Add your command handler code here
}
void CFileView::OnEditClear()
{
m_wndFileView.DeleteAllItems();
}
void CFileView::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect rectTree;
m_wndFileView.GetWindowRect(rectTree);
ScreenToClient(rectTree);
rectTree.InflateRect(1, 1);
dc.Draw3dRect(rectTree, ::GetSysColor(COLOR_3DSHADOW), ::GetSysColor(COLOR_3DSHADOW));
}
void CFileView::OnSetFocus(CWnd* pOldWnd)
{
CDockablePane::OnSetFocus(pOldWnd);
m_wndFileView.SetFocus();
}
void CFileView::OnChangeVisualStyle()
{
m_wndToolBar.CleanUpLockedImages();
m_wndToolBar.LoadBitmap(theApp.m_bHiColorIcons ? IDB_EXPLORER_24 : IDR_EXPLORER, 0, 0, TRUE /* Locked */);
m_FileViewImages.DeleteImageList();
UINT uiBmpId = theApp.m_bHiColorIcons ? IDB_FILE_VIEW_24 : IDB_FILE_VIEW;
CBitmap bmp;
if (!bmp.LoadBitmap(uiBmpId))
{
TRACE(_T("Can't load bitmap: %x/n"), uiBmpId);
ASSERT(FALSE);
return;
}
BITMAP bmpObj;
bmp.GetBitmap(&bmpObj);
UINT nFlags = ILC_MASK;
nFlags |= (theApp.m_bHiColorIcons) ? ILC_COLOR24 : ILC_COLOR4;
m_FileViewImages.Create(16, bmpObj.bmHeight, nFlags, 0, 0);
m_FileViewImages.Add(&bmp, RGB(255, 0, 255));
m_wndFileView.SetImageList(&m_FileViewImages, TVSIL_NORMAL);
}
HTREEITEM CFileView::AddFileName(LPCTSTR strFileName, DWORD_PTR dwData)
{
HTREEITEM  hFileNode;
hFileNode = m_wndFileView.InsertItem(strFileName, 0, 0, m_hNodeSrc);
m_wndFileView.SetItemData(hFileNode, dwData);
m_hNodeFile = hFileNode;
return hFileNode;
}
HTREEITEM CFileView::AddNote(LPCTSTR Name, DWORD_PTR dwData)
{
HTREEITEM  hNode;
hNode = m_wndFileView.InsertItem(Name, 2, 2, m_hNodeFile);
m_wndFileView.SetItemData(hNode, dwData);
return hNode;
}
BOOL CFileView::DelFileName(HTREEITEM& hFileNode)
{
return m_wndFileView.DeleteItem(hFileNode);
}
void CFileView::OnPopExpTest()
{
// TODO: Add your command handler code here
m_wndFileView.Test();
}
void CFileView::OnPopExpTest2()
{
// TODO: Add your command handler code here
m_wndFileView.Test2();
}
//子节点的插入
void CFileView::OnPopExpInsertchild()
{
// TODO: Add your command handler code here
HTREEITEM hItem = m_wndFileView.GetSelectedItem();
HTREEITEM hItemNew = NULL;
if (NULL == hItem)
{
PRINT("No selected");
hItemNew = m_wndFileView.InsertItem("NewChild", 0, 0, TVI_ROOT, TVI_FIRST);
}
else
{
PRINT("Select = %s", m_wndFileView.GetItemText(hItem).GetBuffer());
hItemNew = m_wndFileView.InsertItem("NewChild", 0, 0, hItem, TVI_FIRST);
m_wndFileView.Expand(hItem, TVE_EXPAND);
}
InsertNode(hItemNew);
}
//sibling节点插入时先找到父节点,在父节点下插入子节点。
void CFileView::OnPopExpInsertsibling()
{
// TODO: Add your command handler code here
HTREEITEM hItem = m_wndFileView.GetSelectedItem();
HTREEITEM hItemNew = NULL;
HTREEITEM hPItem = NULL;
if (NULL == hItem)
{
hItemNew = m_wndFileView.InsertItem("NewSibling", 0, 0, TVI_ROOT, TVI_FIRST);
}
else
{
hPItem = m_wndFileView.GetParentItem(hItem);
hItemNew = m_wndFileView.InsertItem("NewSibling", 0, 0, hPItem, hItem);
m_wndFileView.Expand(hPItem, TVE_EXPAND);
}
InsertNode(hItemNew);
}
//插入节点,直接在数据库中插入一个节点的name,
//id的类型是唯一的自动增长型,由数据自动生成。
//因为是自动增长的,所以查找最大id,读出来放在节点id数据中,用来更新
//id是唯一标识这个节点的。不能用name,name是可以相同的。
void CFileView::InsertNode(HTREEITEM hItem)
{
if (!hItem)    return;
CString sql;
int nrow, ncolum;
sql.Format(_T("INSERT INTO notesid(name) values('%s');"),
m_wndFileView.GetItemText(hItem).GetBuffer());
if (!m_SQLite.OnSqlExec(sql.GetBuffer()))
{
PRINT("OnSqlExec FAILED");
return;
}
sql.Format(_T("SELECT max(id) FROM notesid;"));
if (!m_SQLite.Query(sql.GetBuffer(), nrow, ncolum))
{
PRINT("Query FAILED");
return;
}
FileTable* pData = new FileTable;
int i, j;
for (i = 1; i < nrow + 1; i++)    //第一行是字段名称
{
pData->id = atoi(m_SQLite.m_sresult[i * ncolum + 0]);
}
m_wndFileView.SetItemData(hItem, (DWORD_PTR)pData);
pData->hItem = hItem;
return;
}
//先删除子节点再删除当前节点,避免删掉当前节点的sibling节点
void CFileView::OnPopExpDelete()
{
// TODO: Add your command handler code here
PRINT("OnPopExpDelete");
HTREEITEM hItem = m_wndFileView.GetSelectedItem();
HTREEITEM hChild = m_wndFileView.GetChildItem(hItem);
DeleteAllNodes(hChild);
DeleteNode(hItem);
}
//通过id删除数据库中的节点
void CFileView::DeleteNode(HTREEITEM hItem)
{
if (!hItem)    return;
FileTable* pData = (FileTable * )m_wndFileView.GetItemData(hItem);
CString sql;
sql.Format(_T("DELETE FROM notesid WHERE id = %d;"), pData->id);
if (!m_SQLite.OnSqlExec(sql.GetBuffer()))
{
PRINT("OnSqlExec FAILED");
return;
}
m_wndFileView.DeleteItem(hItem);
return;
}
//遍历时不能在遍历sibling节点前就删除当前节点了。
void CFileView::DeleteAllNodes(HTREEITEM hItem)
{
if (!hItem)    return;
HTREEITEM hChild = m_wndFileView.GetChildItem(hItem);
DeleteAllNodes(hChild);
//DeleteNode(hChild);
HTREEITEM hSibling = m_wndFileView.GetNextSiblingItem(hItem);
DeleteAllNodes(hSibling);
DeleteNode(hItem);
PRINT(m_wndFileView.GetItemText(hItem).GetBuffer());
return;
}
//保存前先备份和删除旧表,然后新建新表并批量插入 
void CFileView::OnPopExpSave()
{
if (NULL == m_SQLite.m_db)
{
PRINT("Fail no datebase");
return;
}
CString sql;
sql = "PRAGMA foreign_keys = 0;";
sql += "DROP TABLE notesidbak;";
sql += "CREATE TABLE notesidbak AS SELECT * FROM notesid;";
sql += "DROP TABLE notesid;";
sql += "CREATE TABLE notesid (id INTEGER,lev INTEGER,levno INTEGER,name CHAR(256));";
m_strSave = "";
HTREEITEM hItem = m_wndFileView.GetRootItem();
m_NodeNo = 0;
m_NodeLev = 0;
SaveNode(hItem);  //遍历创建插入语句
sql += m_strSave;
sql += "PRAGMA foreign_keys = 1;";
if (!m_SQLite.OnSqlExec(sql.GetBuffer()))
{
PRINT("OnSqlExec FAILED");
}
PRINT(m_strSave.GetBuffer());
}
//然后遍历插入节点,id是唯一不变的,其他都是读取树的结构数据
void CFileView::SaveNode(HTREEITEM hItem)
{
if (!hItem)    return;
m_NodeNo++;
CString str;
FileTable* pdata = (FileTable*)m_wndFileView.GetItemData(hItem);
str.Format(_T("INSERT INTO notesid(id,lev,idno,name) values(%d,%d,%d,'%s');"),
pdata->id, m_NodeLev, m_NodeNo,
m_wndFileView.GetItemText(hItem).GetBuffer());
m_strSave += str;
HTREEITEM hChild = m_wndFileView.GetChildItem(hItem);
if (hChild)
{
m_NodeLev++;
if (m_NodeLev > 255)
{
PRINT(_T("<ERR> m_NodeLev>255(%d)!"), m_NodeLev);
return;
}
}
SaveNode(hChild);
HTREEITEM hSibling = m_wndFileView.GetNextSiblingItem(hItem);
if (!hSibling)    m_NodeLev--;
SaveNode(hSibling);
return;
}
//更新只是批量更新旧表,不删除
void CFileView::OnPopExpUpdate()
{
CString sql;
if (NULL == m_SQLite.m_db)
{
PRINT("Fail no datebase");
return;
}
sql = "";
m_strSave = "";
HTREEITEM hItem = m_wndFileView.GetRootItem();
m_NodeNo = 0;
m_NodeLev = 0;
UpdateNode(hItem);
sql += m_strSave;
if (!m_SQLite.OnSqlExec(sql.GetBuffer()))
{
PRINT("OnSqlExec FAILED");
}
PRINT(m_strSave.GetBuffer());
}
//
//更新数据库时,m_NodeNo是节点的遍历顺序,也就是idno字段。
//确定了sibling的顺序,可按顺序还原。确保父节点存在。
//
void CFileView::UpdateNode(HTREEITEM hItem)
{
if (!hItem)    return;
m_NodeNo++;
CString str;
FileTable* pdata = (FileTable*)m_wndFileView.GetItemData(hItem);
str.Format(_T("update notesid set lev=%d,idno=%d, name='%s' where id=%d;"),
m_NodeLev, m_NodeNo, m_wndFileView.GetItemText(hItem).GetBuffer(),
pdata->id);
m_strSave += str;
HTREEITEM hChild = m_wndFileView.GetChildItem(hItem);
if (hChild)
{
m_NodeLev++;
if (m_NodeLev > 255)
{
PRINT(_T("<ERR> m_NodeLev>255(%d)!"), m_NodeLev);
return;
}
}
UpdateNode(hChild);
HTREEITEM hSibling = m_wndFileView.GetNextSiblingItem(hItem);
if (!hSibling) m_NodeLev--;
UpdateNode(hSibling);
return;
}
void CFileView::ShowAllNodes(HTREEITEM hItem)
{
if (!hItem)    return;
m_NodeNo++;
PRINT(_T("%d:%d-%s "), 
m_NodeNo, m_NodeLev, m_wndFileView.GetItemText(hItem).GetBuffer());
HTREEITEM hChild = m_wndFileView.GetChildItem(hItem);
if (hChild)
{
m_NodeLev++;
if (m_NodeLev > 255)
{
PRINT(_T("<ERR> m_NodeLev>255(%d)!"), m_NodeLev);
return;
}
}
ShowAllNodes(hChild);
HTREEITEM hSibling = m_wndFileView.GetNextSiblingItem(hItem);
if (hSibling) m_NodeLev--;
ShowAllNodes(hSibling);
return;
}
void CFileView::OnPopExpShowall()
{
m_NodeNo = 0;
m_NodeLev = 0;
HTREEITEM hItem = m_wndFileView.GetRootItem();
ShowAllNodes(hItem);
}

 

原创文章,作者:745907710,如若转载,请注明出处:https://blog.ytso.com/272036.html

(0)
上一篇 2022年7月8日
下一篇 2022年7月8日

相关推荐

发表回复

登录后才能评论