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

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

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

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

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

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

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

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

 

 

 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);
}

 

posted @ 2022-07-07 18:40  XGZ21  阅读(190)  评论(0编辑  收藏  举报