//------------------------------------------------------------------------------
// $Workfile: TreeController.cpp $
// $Header: /SbjDev/SbjCore/TreeController.cpp 15 11/12/08 2:22p Steve $
//
// Copyright � 2008 SbjCat
// All rights reserved.
//
//
// *** Authors ***
// Steve Johnson
//
// $Revision: 15 $
//
//-----------------------------------------------------------------------------
#include "StdAfx.h"
#include "resource.h"
#include "TreeController.h"
#include "TreeInserter.h"
#include "ControlledWndT.h"
#include "CmdMsgHandler.h"
#include "NotifyMsgHandler.h"
#include "WndMsgHandler.h"
#include "ItemDisplayModel.h"
#include "TreeItemController.h"
#include "EventT.h"
#include "TextSource.h"
#include "TextHandler.h"
#include "DropTarget.h"
#include "DocController.h"
#include "DocEvents.h"
#include "ModelEvents.h"
#include <map>
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
namespace localNS
{
// Event Handlers /////////////////////////////////////////////////////
class FileOpenEventHandler : public SbjCore::EventMgr::EventHandler
{
SbjCore::Mvc::TreeView::Controller* pTheCtrlr;
public:
FileOpenEventHandler() :
SbjCore::EventMgr::EventHandler(SbjCore::Mvc::Doc::Events::EVID_FILE_OPEN)
{
}
virtual ~FileOpenEventHandler()
{
}
void SetCtrlr(SbjCore::Mvc::TreeView::Controller* p)
{
pTheCtrlr = p;
}
private:
virtual void OnHandle(SbjCore::EventMgr::Event* pEvent)
{
ASSERT(pTheCtrlr != NULL);
if (pTheCtrlr != NULL)
{
SbjCore::Mvc::Doc::Events::FileOpen* pTheEvent = dynamic_cast<SbjCore::Mvc::Doc::Events::FileOpen*>(pEvent);
const SbjCore::Mvc::Doc::Controller* pDocCtrlr = pTheEvent->pCtrlr;
HANDLE hItemRoot = pTheEvent->hItemRoot;
(void)pTheCtrlr->LoadTree(pDocCtrlr, hItemRoot);
}
}
};
///////////////////////////////////////////////////////////////////////////////////////////////
class ItemInsertedEventHandler : public SbjCore::EventMgr::EventHandler
{
SbjCore::Mvc::TreeView::Controller* pTheCtrlr;
public:
ItemInsertedEventHandler() :
SbjCore::EventMgr::EventHandler(SbjCore::Mvc::Model::Events::EVID_ITEM_INSERTED)
{
}
void SetCtrlr(SbjCore::Mvc::TreeView::Controller* p)
{
pTheCtrlr = p;
}
private:
virtual void OnHandle(SbjCore::EventMgr::Event* pEvent)
{
ASSERT(pTheCtrlr != NULL);
if (pTheCtrlr != NULL)
{
SbjCore::Mvc::Model::Events::ItemInsert* pTheEvent = dynamic_cast<SbjCore::Mvc::Model::Events::ItemInsert*>(pEvent);
const SbjCore::Mvc::Model::Controller* pModelCtrlr = pTheEvent->pCtrlr;
HANDLE hChild = pTheEvent->hChild;
HANDLE hParent = pTheEvent->hParent;
HANDLE hAfter = pTheEvent->hAfter;
HTREEITEM htiParent = TVI_ROOT;
HTREEITEM htiAfter = TVI_LAST;
SbjCore::Mvc::TreeView::ItemController* pItemCtrlr = pTheCtrlr->GetItemController(hParent);
if (pItemCtrlr != NULL)
{
htiParent = pItemCtrlr->GetHandle();
}
if (hAfter != NULL)
{
CTreeCtrl* pTree = pTheCtrlr->GetTreeCtrl();
pItemCtrlr = pTheCtrlr->GetItemController(hAfter);
if (pItemCtrlr != NULL)
{
htiAfter = pItemCtrlr->GetHandle();
htiAfter = pTree->GetPrevSiblingItem(htiAfter);
}
}
(void)pTheCtrlr->InsertTree(pModelCtrlr, hChild, htiParent, htiAfter);
}
}
};
///////////////////////////////////////////////////////////////////////////////////////////////
class ItemRemovedEventHandler : public SbjCore::EventMgr::EventHandler
{
SbjCore::Mvc::TreeView::Controller* pTheCtrlr;
public:
ItemRemovedEventHandler() :
SbjCore::EventMgr::EventHandler(SbjCore::Mvc::Model::Events::EVID_ITEM_REMOVED),
pTheCtrlr(NULL)
{
}
void SetCtrlr(SbjCore::Mvc::TreeView::Controller* pCtrlr)
{
pTheCtrlr = pCtrlr;
}
private:
virtual void OnHandle(SbjCore::EventMgr::Event* pEvent)
{
ASSERT(pTheCtrlr != NULL);
SbjCore::Mvc::Model::Events::ItemRemove* pTheEvent = dynamic_cast<SbjCore::Mvc::Model::Events::ItemRemove*>(pEvent);
ASSERT(pTheEvent != NULL);
if (pTheEvent != NULL)
{
SbjCore::Mvc::TreeView::ItemController* pItem = pTheCtrlr->GetItemController(pTheEvent->hItem);
if (pItem != NULL)
{
pTheCtrlr->DeleteItem(pItem);
}
}
}
};
// Message Handlers /////////////////////////////////////////////////////
class NMClickHandler : public SbjCore::Mvc::NotifyMsgHandler
{
virtual bool OnHandleNotify(NMHDR* pNMHDR, LRESULT* pResult)
{
pNMHDR;
*pResult = 0;
bool bRslt = true;
SbjCore::Mvc::TreeView::Controller* pCtrlr = static_cast<SbjCore::Mvc::TreeView::Controller*>(GetController());
if (pCtrlr != NULL)
{
CTreeCtrl* pTree = pCtrlr->GetTreeCtrl();
DWORD pos = GetMessagePos();
CPoint pt(LOWORD(pos), HIWORD(pos));
pTree->ScreenToClient(&pt);
UINT flags = 0;
HTREEITEM hti = pTree->HitTest(pt, &flags);
if (hti != NULL)
{
SbjCore::Mvc::TreeView::ItemController* pItemCtrlr = pCtrlr->GetItem(hti);
ASSERT(pItemCtrlr != NULL);
if (pItemCtrlr != NULL)
{
// all we do at this level is add selection when the state is changed
if ((flags & TVHT_ONITEMSTATEICON) == TVHT_ONITEMSTATEICON)
{
pCtrlr->SelectItem(pItemCtrlr);
UINT nStateItemIndex = pItemCtrlr->GetStateImage();
nStateItemIndex = 0;
}
bRslt = pItemCtrlr->HandleNotifyMsg(pNMHDR, pResult);
}
}
}
*pResult = 0; // continue with default
return bRslt;
}
};
//////////////////////////////////////////////////////////////////////////
class NMRClickHandler : public SbjCore::Mvc::NotifyMsgHandler
{
virtual bool OnHandleNotify(NMHDR* pNMHDR, LRESULT* pResult)
{
pNMHDR;
*pResult = 0;
bool bRslt = false;
SbjCore::Mvc::TreeView::Controller* pTheCtrlr = (SbjCore::Mvc::TreeView::Controller*)GetController();
CTreeCtrl* pTree = pTheCtrlr->GetTreeCtrl();
if (pTree != NULL)
{
DWORD dwPos = GetMessagePos();
CPoint pt(LOWORD(dwPos), HIWORD(dwPos));
CPoint ptClient(pt);
pTree->ScreenToClient(&ptClient);
UINT nFlags = 0;
HTREEITEM hti = pTree->HitTest(ptClient, &nFlags);
if (hti != NULL)
{
pTree->Select(hti, TVGN_CARET);
}
*pResult = pTree->SendMessage(WM_CONTEXTMENU, (WPARAM)pTree->GetSafeHwnd(), MAKELPARAM(pt.x, pt.y));
bRslt = true;
}
return bRslt;
}
};
///////////////////////////////////////////////////////////////////////////////////////////////
class RenameItemHandler : public SbjCore::Mvc::CmdMsgHandler
{
virtual bool OnHandleCmd(UINT nID)
{
nID;
SbjCore::Mvc::TreeView::Controller* pTreeCtrlr = (SbjCore::Mvc::TreeView::Controller*)(GetController());
CTreeCtrl* pTree = pTreeCtrlr->GetTreeCtrl();
SbjCore::Mvc::TreeView::ItemController* pItemCtrlr = pTreeCtrlr->GetSelectedItem();
if ((pItemCtrlr != NULL) && (pItemCtrlr->CanEdit()))
{
pTree->EditLabel(pItemCtrlr->GetHandle());
}
return true;
}
virtual bool OnCmdUI(CCmdUI* pCmdUI)
{
SbjCore::Mvc::TreeView::Controller* pTreeCtrlr = (SbjCore::Mvc::TreeView::Controller*)(GetController());
CTreeCtrl* pTree = pTreeCtrlr->GetTreeCtrl();
DWORD dwStyle = pTree->GetStyle();
bool bCanEdit = ((dwStyle & TVS_EDITLABELS) == TVS_EDITLABELS);
if (bCanEdit)
{
SbjCore::Mvc::TreeView::ItemController* pItemCtrlr = pTreeCtrlr->GetSelectedItem();
bCanEdit = ((pItemCtrlr != NULL) && (pItemCtrlr->CanEdit()));
}
pCmdUI->Enable(bCanEdit);
return true;
}
};
///////////////////////////////////////////////////////////////////////////////////////////////
/// CEdit subclass for the CTreeCtrl label edit so it will handle return and escape
class LabelEdit : public SbjCore::Mvc::ControlledWndT<CEdit>
{
class EditController : public SbjCore::Mvc::WndController
{
CTreeCtrl* pTheTree;
friend class KeyDownHandler;
class KeyDownHandler : public SbjCore::Mvc::WndMsgHandler
{
public:
KeyDownHandler()
{
}
private:
virtual LRESULT OnHandleWndMsg(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
bool bRslt = false;
lParam;
*pResult = DLGC_WANTALLKEYS;
EditController* pEditCtrlr = (EditController*)GetController();
if ((wParam == VK_RETURN) || (wParam == VK_ESCAPE))
{
bool bCancel = (wParam == VK_ESCAPE);
pEditCtrlr->pTheTree->SendMessage(TVM_ENDEDITLABELNOW, 0, (LPARAM)bCancel);
}
return bRslt;
}
} theKeyDownHandler;
public:
EditController()
{
AddHandler(WM_GETDLGCODE, &theKeyDownHandler);
}
void SetTreeCtrl(CTreeCtrl* p)
{
pTheTree = p;
}
} theCtrlr;
public:
LabelEdit()
{
SetController(&theCtrlr);
}
void SetTreeCtrl(CTreeCtrl* pTree)
{
theCtrlr.SetTreeCtrl(pTree);
}
};
///////////////////////////////////////////////////////////////////////////////////////////////
class BeginLabelEditHandler : public SbjCore::Mvc::NotifyMsgHandler
{
virtual bool OnHandleNotify(NMHDR* pNMHDR, LRESULT* pResult)
{
pNMHDR;
*pResult = 1; // flag as non edit
static LabelEdit theLabelEdit;
bool bRslt = true;
SbjCore::Mvc::TreeView::Controller* pTreeCtrlr = (SbjCore::Mvc::TreeView::Controller*)GetController();
SbjCore::Mvc::TreeView::ItemController* pItemCtrlr = pTreeCtrlr->GetSelectedItem();
if (pItemCtrlr != NULL)
{
(void)pItemCtrlr->HandleNotifyMsg(pNMHDR, pResult);
// item set the flag
if (*pResult == 0)
{
CTreeCtrl* pTree = pTreeCtrlr->GetTreeCtrl();
// subclass the edit so it handles return and escape
CEdit* pEdit = pTree->GetEditControl();
theLabelEdit.SetTreeCtrl(pTree);
if (theLabelEdit.GetSafeHwnd() != NULL)
{
theLabelEdit.UnsubclassWindow();
}
theLabelEdit.SubclassWindow(pEdit->GetSafeHwnd());
}
}
return bRslt;
}
};
///////////////////////////////////////////////////////////////////////////////////////////////
class EndLabelEditHandler : public SbjCore::Mvc::NotifyMsgHandler
{
virtual bool OnHandleNotify(NMHDR* pNMHDR, LRESULT* pResult)
{
bool bRslt = true;
*pResult = 1;
SbjCore::Mvc::TreeView::Controller* pTreeCtrlr = dynamic_cast<SbjCore::Mvc::TreeView::Controller*>(GetController());
if (pTreeCtrlr != NULL)
{
SbjCore::Mvc::TreeView::ItemController* pItemCtrlr = pTreeCtrlr->GetSelectedItem();
bRslt = pItemCtrlr->HandleNotifyMsg(pNMHDR, pResult);
}
return bRslt;
}
};
///////////////////////////////////////////////////////////////////////////////////////////////
// Internal DragNDrop ///////////////////////////////////////////////////
class BeginDragHandler : public SbjCore::Mvc::NotifyMsgHandler
{
virtual bool OnHandleNotify(NMHDR* pNMHDR, LRESULT* pResult)
{
pNMHDR;
pResult;
bool bRslt = true;
SbjCore::Mvc::TreeView::Controller* pTreeCtrlr = (SbjCore::Mvc::TreeView::Controller*)GetController();
CTreeCtrl* pTree = pTreeCtrlr->GetTreeCtrl();
NMTREEVIEW* pNMTree = (NMTREEVIEW*)pNMHDR;
HTREEITEM hti = pNMTree->itemNew.hItem;
pTree->Select(hti, TVGN_CARET);
CString s;
s.Format(_T("%d"), hti);
SbjCore::DragNDrop::TextSource* pSrc = new SbjCore::DragNDrop::TextSource(CFHTREEITEM, s);
if (pSrc != NULL)
{
pSrc->DoDragDrop();
bRslt = true;
delete pSrc;
}
return bRslt;
}
} ;
///////////////////////////////////////////////////////////////////////////////////////////////
class TreeDropTarget : public SbjCore::DragNDrop::DropTarget
{
public:
TreeDropTarget()
{
}
virtual ~TreeDropTarget()
{
}
/*
virtual DROPEFFECT OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject,
DWORD dwKeyState, CPoint point)
{
}
virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject,
DWORD dwKeyState, CPoint point);
*/
virtual DROPEFFECT OnDragScroll(CWnd* pWnd, DWORD dwKeyState,
CPoint point)
{
dwKeyState;
CTreeCtrl* pTree = dynamic_cast<CTreeCtrl*>(pWnd);
if (NULL == pTree) // assume CTreeView
{
CTreeView* pView = dynamic_cast<CTreeView*>(pWnd);
if (pView != NULL)
{
pTree = &pView->GetTreeCtrl();
}
}
if (pTree != NULL) // either ctrl or view
{
// get client RectTracker of destination window
CRect rectClient;
pTree->GetClientRect(&rectClient);
CRect rect = rectClient;
rect.InflateRect(-nScrollInset, -nScrollInset);
if (rectClient.PtInRect(point) && !rect.PtInRect(point))
{
static int nPause = 0; // kludge to slow down the drag
nPause++;
if ((nPause % 5) == 0)
{
nPause = 0;
HTREEITEM hti = pTree->HitTest(point);
if (hti != NULL)
{
HTREEITEM htiNext = pTree->GetNextVisibleItem(hti);
HTREEITEM htiPrev = pTree->GetPrevVisibleItem(hti);
pTree->EnsureVisible(htiNext);
pTree->EnsureVisible(htiPrev);
}
}
}
}
return DROPEFFECT_NONE; // decided to let DragEnter and DragOver handle the effect
}
};
///////////////////////////////////////////////////////////////////////////////////////////////
class SelChangedHandler : public SbjCore::Mvc::NotifyMsgHandler
{
virtual bool OnHandleNotify(NMHDR* pNMHDR, LRESULT* pResult)
{
bool bRslt = true;
LPNMTREEVIEW pNMTV = (LPNMTREEVIEW)pNMHDR;
SbjCore::Mvc::TreeView::Controller* pTreeCtrlr = dynamic_cast<SbjCore::Mvc::TreeView::Controller*>(GetController());
if (pTreeCtrlr != NULL)
{
pTreeCtrlr->BlockNotify();
HTREEITEM hti = pNMTV->itemNew.hItem;
SbjCore::Mvc::TreeView::ItemController* pItemCtrlr = dynamic_cast<SbjCore::Mvc::TreeView::ItemController*>(pTreeCtrlr->GetItem(hti));
bRslt = pItemCtrlr->HandleNotifyMsg(pNMHDR, pResult);
SbjCore::Mvc::Model::ItemHandles selItems;
selItems.push_back(pItemCtrlr->GetModelItemHandle());
SbjCore::Mvc::Model::Controller* pModelCtrlr = SbjCore::Mvc::Model::GetCurController();
pModelCtrlr->SetSelectedItems(selItems);
pTreeCtrlr->BlockNotify(false);
}
return bRslt;
}
};
//////////////////////////////////////////////////////////////////////////
class SetFocusHandler : public SbjCore::Mvc::WndMsgHandler
{
CALL_DEFAULT_FIRST() // comment out to handle message before default
virtual LRESULT OnHandleWndMsg(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
wParam;
lParam;
*pResult = 0;
LRESULT lRslt = 1;
SbjCore::Mvc::TreeView::Controller* pCtrlr = dynamic_cast<SbjCore::Mvc::TreeView::Controller*>(GetController());
SbjCore::Mvc::TreeView::ItemController* pItemCtrlr = dynamic_cast<SbjCore::Mvc::TreeView::ItemController*>(pCtrlr->GetSelectedItem());
if (pItemCtrlr != NULL)
{
SbjCore::Mvc::Model::ItemHandles selItems;
selItems.push_back(pItemCtrlr->GetModelItemHandle());
SbjCore::Mvc::Model::Controller* pModelCtrlr = SbjCore::Mvc::Model::GetCurController();
pModelCtrlr->SetSelectedItems(selItems);
}
return lRslt;
}
};
}
namespace SbjCore
{
namespace Mvc
{
namespace TreeView
{
static int CALLBACK CompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
int nRslt = 0; // equal
CTreeCtrl* pTree = (CTreeCtrl*)lParamSort;
ItemController* pItemCtrlr1 = (ItemController*)lParam1;
ItemController* pItemCtrlr2 = (ItemController*)lParam2;
CString sItem1 = pTree->GetItemText(pItemCtrlr1->GetHandle());
CString sItem2 = pTree->GetItemText(pItemCtrlr2->GetHandle());
nRslt = strcmp(sItem1, sItem2); // default
if ((pItemCtrlr1 != NULL) && (pItemCtrlr2 != NULL))
{
bool b1 = pItemCtrlr1->CanAcceptChildren();
bool b2 = pItemCtrlr2->CanAcceptChildren();
if (b1) // is a folder
{
if (!b2) // is not folder
{
nRslt = -1; // folders < items
}
}
else // b1 is an item
{
if (b2) // is a folder
{
nRslt = 1; // items > folders
}
}
}
return nRslt;
}
//////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
// ControllerImpl
/// private implementation struct for Controller class
struct ControllerImpl
{
TVSORTCB tvs;
CImageList theImageList;
DWORD dwStyle;
typedef std::map<HTREEITEM, ItemController* > t_HTREEITEMToItemControllerMap;
t_HTREEITEMToItemControllerMap theItemCtrlrsByHTREEITEM;
typedef std::map<HANDLE, ItemController* > t_ModelSourceHandleToItemControllerMap;
t_ModelSourceHandleToItemControllerMap theItemCtrlrsByModelSourceHandle;
typedef std::map<CString, CRuntimeClass*> ItemRTCMap;
typedef ItemRTCMap::iterator ItemRTCMapIter;
ItemRTCMap theItemRTCMap;
int nBlockNotify;
UINT nImageListID;
bool bUsingResIDs;
int nExpandLevels;
localNS::FileOpenEventHandler theFileOpenHandler;
localNS::ItemInsertedEventHandler theItemInsertedHandler;
localNS::ItemRemovedEventHandler theItemRemovedHandler;
localNS::SelChangedHandler theSelChangedHandler;
localNS::SetFocusHandler theSetFocusHandler;
localNS::NMClickHandler theNMClickHandler;
localNS::NMRClickHandler theNMRClickHandler;
localNS::RenameItemHandler theRenameItemHandler;
localNS::LabelEdit theLabelEdit;
localNS::BeginLabelEditHandler theBeginLabelHandler;
localNS::EndLabelEditHandler theEndLabelHandler;
localNS::BeginDragHandler theBeginDragHandler;
localNS::TreeDropTarget theDropTarget;
ControllerImpl(const UINT n, const int n1, DWORD d) :
dwStyle(d),
nImageListID(n),
nBlockNotify(0),
bUsingResIDs(false),
nExpandLevels(n1)
{
if (nImageListID != -1)
{
CBitmap bmp;
VERIFY(bmp.LoadBitmap(nImageListID));
BITMAP bm;
bmp.GetBitmap (&bm);
UINT nFlags = ILC_MASK | ILC_COLOR24;
theImageList.Create(16, bm.bmHeight, nFlags, 0, 0);
theImageList.Add(&bmp, RGB (255, 0, 255));
}
}
bool Sort(HTREEITEM htiParent, CTreeCtrl* pTree, PFNTVCOMPARE lpfnCompare)
{
tvs.hParent = htiParent;
tvs.lpfnCompare = CompareProc;
tvs.lParam = (LPARAM)pTree;
if (lpfnCompare != NULL)
{
tvs.lpfnCompare = lpfnCompare;
}
return pTree->SortChildrenCB(&tvs);
}
virtual ~ControllerImpl()
{
try
{
for (t_HTREEITEMToItemControllerMap::iterator iter = theItemCtrlrsByHTREEITEM.begin(); iter != theItemCtrlrsByHTREEITEM.end(); iter++)
{
ItemController* pItemCtrlr = iter->second;
if (pItemCtrlr != NULL)
{
delete pItemCtrlr;
}
}
theItemCtrlrsByHTREEITEM.clear();
theItemCtrlrsByModelSourceHandle.clear();
}
catch (...)
{
ASSERT(FALSE);
}
}
};
///////////////////////////////////////////////////////////////////////////////////////////////
// Controller
IMPLEMENT_DYNCREATE(Controller, WndController)
Controller::Controller() :
m_pImpl(new ControllerImpl((UINT)-1, 0, 0))
{
}
Controller::Controller(const UINT nImageListID, const int nExpandLevels, const DWORD dwStyle /*= DEFAULT_TREE_STYLES*/) :
m_pImpl(new ControllerImpl(nImageListID, nExpandLevels, dwStyle))
{
AddHandler(TVN_SELCHANGED, &m_pImpl->theSelChangedHandler);
AddHandler(WM_SETFOCUS, &m_pImpl->theSetFocusHandler);
AddHandler(NM_CLICK, &m_pImpl->theNMClickHandler);
AddHandler(NM_RCLICK, &m_pImpl->theNMRClickHandler);
AddHandler(TVN_BEGINDRAG, &m_pImpl->theBeginDragHandler);
AddHandler(TVN_BEGINLABELEDIT, &m_pImpl->theBeginLabelHandler);
AddHandler(TVN_ENDLABELEDIT, &m_pImpl->theEndLabelHandler);
AddHandler(ID_SBJCORE_CTX_RENAME, &m_pImpl->theRenameItemHandler);
}
Controller::~Controller(void)
{
try
{
delete m_pImpl;
}
catch (...)
{
ASSERT(FALSE);
}
}
void Controller::MapItemRTCToModelTypeName( LPCTSTR lpszName, CRuntimeClass* pRTC )
{
m_pImpl->theItemRTCMap[lpszName] = pRTC;
}
ItemController* Controller::CreateItem(const SbjCore::Mvc::Model::Controller* pModelCtrlr, HANDLE hModelItem)
{
ItemController* pItem = NULL;
CString sItemType = pModelCtrlr->GetItemTypeName(hModelItem);
if (!sItemType.IsEmpty())
{
CRuntimeClass* pRTC = m_pImpl->theItemRTCMap[sItemType];
if (pRTC != NULL)
{
pItem = dynamic_cast<ItemController*>(pRTC->CreateObject());
pItem->Create(pModelCtrlr, this, hModelItem);
m_pImpl->theItemCtrlrsByModelSourceHandle[hModelItem] = pItem;
}
}
return pItem;
}
void Controller::LoadTree(const SbjCore::Mvc::Model::Controller* pModelCtrlr, HANDLE hItem, HTREEITEM htiAfter /*= TVI_LAST*/ )
{
CTreeCtrl* pTree = GetTreeCtrl();
DeleteAllItems();
InsertTree(pModelCtrlr, hItem, TVI_ROOT, htiAfter);
ItemController* pItemRoot = GetItemController(hItem);
if (pItemRoot != NULL)
{
pTree->Expand(pItemRoot->GetHandle(), TVE_EXPAND);
SelectItem(pItemRoot);
}
TreeLoaded();
}
void Controller::InsertTree(const SbjCore::Mvc::Model::Controller* pModelCtrlr, HANDLE hItem, HTREEITEM htiParent, HTREEITEM htiAfter /*= TVI_LAST*/ )
{
Inserter theInserter(pModelCtrlr, this, htiParent, htiAfter);
theInserter.Apply(hItem);
}
ItemController* Controller::GetItemController( HANDLE hItem )
{
return m_pImpl->theItemCtrlrsByModelSourceHandle[hItem];
}
CTreeCtrl* Controller::GetTreeCtrl() const
{
CTreeCtrl* pTree = dynamic_cast<CTreeCtrl*>(GetWnd());
if (NULL == pTree) // assume CTreeView
{
CTreeView* pView = dynamic_cast<CTreeView*>(GetCmdTarget());
if (pView != NULL)
{
pTree = &pView->GetTreeCtrl();
}
}
return pTree;
}
void Controller::SetExpandLevels( const int n )
{
m_pImpl->nExpandLevels = n;
}
int Controller::GetExpandLevels() const
{
return m_pImpl->nExpandLevels;
}
void Controller::SetStyle( const DWORD dwStyle )
{
m_pImpl->dwStyle = dwStyle;
}
DWORD Controller::GetStyle() const
{
return m_pImpl->dwStyle;
}
void Controller::SetImageListID( const UINT n )
{
m_pImpl->nImageListID = n;
}
UINT Controller::GetImageListID() const
{
return m_pImpl->nImageListID;
}
bool Controller::UsingResIDs() const
{
return m_pImpl->bUsingResIDs;
}
void Controller::SetUsingResIDs( const bool b )
{
m_pImpl->bUsingResIDs = b;
}
// Sets image list and initializes the CTreeCtrl as a OLE Drop Target
void Controller::OnInitialize()
{
SbjCore::Mvc::Controller::OnInitialize();
CTreeCtrl* pTree = GetTreeCtrl();
pTree->SetImageList(&m_pImpl->theImageList, TVSIL_NORMAL);
CImageList* pIL = pTree->GetImageList(TVSIL_NORMAL);
ASSERT(pIL == &m_pImpl->theImageList);
m_pImpl->theDropTarget.Register(static_cast<CWnd*>(GetCmdTarget()));
m_pImpl->theFileOpenHandler.SetCtrlr(this);
m_pImpl->theItemInsertedHandler.SetCtrlr(this);
m_pImpl->theItemRemovedHandler.SetCtrlr(this);
}
// called by the context menu handler so appropriate commands can be added to the menu
SbjCore::Utils::Menu::ItemRange Controller::OnPrepareCtxMenu(CMenu& ctxMenu)
{
Utils::Menu::ItemRange menuItems;
ItemController* pItemCtrlr = GetSelectedItem();
if (pItemCtrlr != NULL)
{
menuItems = pItemCtrlr->OnPrepareCtxMenu(ctxMenu);
}
return menuItems;
}
// inserts an item based on the ItemController class
HTREEITEM Controller::InsertItem(ItemController* pItemCtrlr,
HTREEITEM htiParent /*= TVI_ROOT*/,
HTREEITEM htiAfter /*= TVI_LAST*/,
bool bExpandParent /*= true*/)
{
CTreeCtrl* pTree = GetTreeCtrl();
TVINSERTSTRUCT tvis;
tvis.hParent = htiParent;
tvis.hInsertAfter = htiAfter;
tvis.itemex = (TVITEMEX&)pItemCtrlr->GetTVItem();
HTREEITEM hti = pTree->InsertItem(&tvis);
pItemCtrlr->SetHandle(hti);
pItemCtrlr->SetCmdTarget(GetCmdTarget());
m_pImpl->theItemCtrlrsByHTREEITEM[hti] = pItemCtrlr;
// call virtual methods
pItemCtrlr->OnInserted();
if (bExpandParent)
{
pTree->Expand(pTree->GetParentItem(hti), TVE_EXPAND);
}
return hti;
}
/** override in derived classes. Called in ItemController to provide derivative the
ability to map a resource ID to CImageList index
*/
int Controller::ImageIndexFromResID(UINT nResID) const
{
return nResID;
}
void Controller::AddDropTargetHandler(CLIPFORMAT cf, DragNDrop::DataHandler* p)
{
m_pImpl->theDropTarget.AddHandler(cf, p);
}
void Controller::BlockNotify(const bool bBlock /*= true*/)
{
if (bBlock)
{
m_pImpl->nBlockNotify++;
}
else
{
if (m_pImpl->nBlockNotify > 0)
{
m_pImpl->nBlockNotify--;
}
}
}
bool Controller::IsBlocked() const
{
return (m_pImpl->nBlockNotify > 0);
}
ItemController* Controller::GetItem(HTREEITEM hti) const
{
return m_pImpl->theItemCtrlrsByHTREEITEM[hti];
}
ItemController* Controller::GetItemFromPt(CPoint pt, UINT *pFlags)
{
ItemController* pItemCtrlr = NULL;
if (!IsBlocked())
{
BlockNotify(true);
CTreeCtrl* pTree = GetTreeCtrl();
HTREEITEM hti = pTree->HitTest(pt, pFlags);
if ((*pFlags & TVHT_ONITEM) && (hti != NULL))
{
pItemCtrlr = GetItem(hti);
}
BlockNotify(false);
}
return pItemCtrlr;
}
ItemController* Controller::GetParentItem(const ItemController* pItemCtrlr) const
{
CTreeCtrl* pTree = GetTreeCtrl();
ItemController* pParentItem = NULL;
if (pItemCtrlr != NULL)
{
HTREEITEM hti = pItemCtrlr->GetHandle();
if (hti != TVI_ROOT)
{
HTREEITEM htiParent = pTree->GetParentItem(hti);
pParentItem = GetItem(htiParent);
}
}
return pParentItem;
}
ItemController* Controller::GetSelectedItem()
{
ItemController* pItemCtrlr = NULL;
if (!IsBlocked())
{
BlockNotify(true);
CTreeCtrl* pTree = GetTreeCtrl();
HTREEITEM hti = pTree->GetSelectedItem();
if (hti != NULL)
{
pItemCtrlr = GetItem(hti);
}
BlockNotify(false);
}
return pItemCtrlr;
}
void Controller::SelectItem(ItemController* pItemCtrlr)
{
if (pItemCtrlr != NULL)
{
CTreeCtrl* pTree = GetTreeCtrl();
BlockNotify(true);
HTREEITEM hti = pItemCtrlr->GetHandle();
pTree->SelectItem(hti);
BlockNotify(false);
}
}
ItemController* Controller::FindItem(SearchCriteria* pCriteria)
{
ItemController* pItemCtrlrFound = NULL;
for (ControllerImpl::t_HTREEITEMToItemControllerMap::iterator iter = m_pImpl->theItemCtrlrsByHTREEITEM.begin(); iter != m_pImpl->theItemCtrlrsByHTREEITEM.end(); iter++)
{
ItemController* pItemCtrlr = iter->second;
if (pCriteria->ItemMatches(pItemCtrlr))
{
pItemCtrlrFound = pItemCtrlr;
break;
}
}
return pItemCtrlrFound;
}
void Controller::DeleteAllItems()
{
CTreeCtrl* pTree = GetTreeCtrl();
pTree->DeleteAllItems();
for (ControllerImpl::t_HTREEITEMToItemControllerMap::iterator iter = m_pImpl->theItemCtrlrsByHTREEITEM.begin(); iter != m_pImpl->theItemCtrlrsByHTREEITEM.end(); iter++)
{
ItemController* pItemCtrlr = iter->second;
delete pItemCtrlr;
}
m_pImpl->theItemCtrlrsByHTREEITEM.clear();
m_pImpl->theItemCtrlrsByModelSourceHandle.clear();
}
void Controller::DeleteItem(ItemController* pItemCtrlr)
{
CTreeCtrl* pTree = GetTreeCtrl();
if (pItemCtrlr != NULL)
{
HTREEITEM hti = pItemCtrlr->GetHandle();
if (pTree->ItemHasChildren(hti))
{
DeleteChildren(pItemCtrlr);
}
ControllerImpl::t_HTREEITEMToItemControllerMap::iterator iter = m_pImpl->theItemCtrlrsByHTREEITEM.find(hti);
if (iter != m_pImpl->theItemCtrlrsByHTREEITEM.end())
{
ItemController* pItemCtrlr = iter->second;
HANDLE hItem = pItemCtrlr->GetModelItemHandle();
ASSERT(hItem != NULL);
m_pImpl->theItemCtrlrsByHTREEITEM.erase(iter->first);
m_pImpl->theItemCtrlrsByModelSourceHandle.erase(hItem);
delete pItemCtrlr;
}
pTree->DeleteItem(hti);
}
}
void Controller::DeleteChildren(ItemController* pItemCtrlr)
{
CTreeCtrl* pTree = GetTreeCtrl();
HTREEITEM hti = pItemCtrlr->GetHandle();
if (pTree->ItemHasChildren(hti))
{
HTREEITEM htiChild = pTree->GetChildItem(hti);
while (htiChild != NULL)
{
HTREEITEM htiNext = pTree->GetNextItem(htiChild, TVGN_NEXT);
ItemController* pChildItem = (ItemController*)pTree->GetItemData(htiChild);
DeleteChildren(pChildItem);
htiChild = htiNext;
}
}
ControllerImpl::t_HTREEITEMToItemControllerMap::iterator iter = m_pImpl->theItemCtrlrsByHTREEITEM.find(hti);
if (iter != m_pImpl->theItemCtrlrsByHTREEITEM.end())
{
ItemController* pItemCtrlr = iter->second;
delete pItemCtrlr;
m_pImpl->theItemCtrlrsByHTREEITEM.erase(iter->first);
}
pTree->DeleteItem(hti);
}
bool Controller::SortChildren(HTREEITEM htiParent, PFNTVCOMPARE lpfnCompare /*= NULL*/)
{
return m_pImpl->Sort(htiParent, GetTreeCtrl(), lpfnCompare);
}
bool Controller::NameExists(LPCTSTR lpszName, HTREEITEM htiParent /*= TVI_ROOT*/) const
{
bool bRslt = false;
CTreeCtrl* pTree = GetTreeCtrl();
CString s(lpszName);
HTREEITEM htiChild = pTree->GetChildItem(htiParent);
while (htiChild != NULL)
{
HTREEITEM htiNext = pTree->GetNextItem(htiChild, TVGN_NEXT);
CString sChild(pTree->GetItemText(htiChild));
if (0 == sChild.Compare(s))
{
bRslt = true;
break;
}
htiChild = htiNext;
}
return bRslt;
}
CString Controller::AssureNewName(LPCTSTR lpszName, HTREEITEM htiParent /*= TVI_ROOT*/)
{
int nCount = 0;
CString sItemText(lpszName);
while (NameExists(sItemText, htiParent))
{
nCount++;
sItemText.Format(_T("%s(%d)"), lpszName, nCount);
}
return sItemText;
}
ItemController* Controller::AssureItemCanAcceptChildren(ItemController* pItemCtrlr)
{
if (!pItemCtrlr->CanAcceptChildren())
{
pItemCtrlr = pItemCtrlr->GetParentItem();
}
return pItemCtrlr;
}
void Controller::TreeLoaded()
{
OnTreeLoaded();
}
void Controller::OnTreeLoaded()
{
// nada
}
}
}
}
//*** Modification History ***
// $Log: /SbjDev/SbjCore/TreeController.cpp $
//
// 15 11/12/08 2:22p Steve
// Finished Generalization of Model v2.0.1
//
// 14 10/24/08 9:03a Steve
//
// 13 10/20/08 11:06a Steve
// Removed source parameter from Event and replaced with EventT data
//
// 12 10/14/08 1:12p Steve
// Implemented Deletes
//
// 11 9/23/08 3:05p Steve
// Fixed _com_error in XmlModelAccess::GetAttribute (guard against
// VT_NULL)
//
// 10 9/23/08 11:30a Steve