|
|
// TreeCtrl.cpp : implementation file
//
//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1999
//
// File: amctreectrl.cpp
//
// Contents: AMC Tree control implementation
//
// History: 16-Jul-96 WayneSc Created
//
//
//--------------------------------------------------------------------------
#include "stdafx.h"
#include "AMCDoc.h" // AMC Console Document
#include "amcview.h"
#include "childfrm.h"
#include "macros.h"
#include "AMCPriv.h"
#include "AMC.h"
#include "mainfrm.h"
#include "TreeCtrl.h"
#include "resource.h"
#include "guidhelp.h" // LoadRootDisplayName
#include "histlist.h"
#include "websnk.h"
#include "webctrl.h"
#include "..\inc\mmcutil.h"
#include "amcmsgid.h"
#include "resultview.h"
#include "eventlock.h"
extern "C" UINT dbg_count;
//############################################################################
//############################################################################
//
// Traces
//
//############################################################################
//############################################################################
#ifdef DBG
CTraceTag tagTree(TEXT("Tree View"), TEXT("Tree View")); #endif //DBG
//############################################################################
//############################################################################
//
// Implementation of class CTreeViewMap
//
//############################################################################
//############################################################################
/*+-------------------------------------------------------------------------*
* * CTreeViewMap::ScOnItemAdded * * PURPOSE: Called when an item is added. Indexes the item. * * PARAMETERS: * TVINSERTSTRUCT * pTVInsertStruct : * HTREEITEM hti : * HMTNODE hMTNode : * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC CTreeViewMap::ScOnItemAdded (TVINSERTSTRUCT *pTVInsertStruct, HTREEITEM hti, HMTNODE hMTNode) { DECLARE_SC(sc, TEXT("CTreeViewMap::ScOnItemAdded"));
// validate parameters
sc = ScCheckPointers(pTVInsertStruct); if(sc) return sc;
if(!hti || !hMTNode) return (sc = E_INVALIDARG);
// create a new map info structure.
TreeViewMapInfo *pMapInfo = new TreeViewMapInfo; if(!pMapInfo) return (sc = E_OUTOFMEMORY);
// fill in the values
pMapInfo->hNode = CAMCTreeView::NodeFromLParam (pTVInsertStruct->item.lParam); pMapInfo->hti = hti; pMapInfo->hMTNode = hMTNode;
// set up the indexes
ASSERT(m_hMTNodeMap.find(pMapInfo->hMTNode) == m_hMTNodeMap.end()); ASSERT(m_hNodeMap.find(pMapInfo->hNode) == m_hNodeMap.end());
m_hMTNodeMap [pMapInfo->hMTNode] = pMapInfo; m_hNodeMap [pMapInfo->hNode] = pMapInfo;
return sc; }
/*+-------------------------------------------------------------------------*
* * CTreeViewMap::ScOnItemDeleted * * PURPOSE: Called when a tree item is deleted. Removes the item from the * indexes. * * PARAMETERS: * HNODE hNode : * HTREEITEM hti : * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC CTreeViewMap::ScOnItemDeleted (HNODE hNode, HTREEITEM hti) { DECLARE_SC(sc, TEXT("CTreeViewMap::ScOnItemDeleted"));
// validate parameters
sc = ScCheckPointers((LPVOID) hNode, (LPVOID) hti); if(sc) return sc;
// remove the TreeViewMapInfo pointer from all the maps
HNodeLookupMap::iterator iter = m_hNodeMap.find(hNode); if(iter == m_hNodeMap.end()) return (sc = E_UNEXPECTED);
TreeViewMapInfo *pMapInfo = iter->second; // find the map info structure.
if(!pMapInfo) return (sc = E_UNEXPECTED);
HMTNODE hMTNode = pMapInfo->hMTNode;
#ifdef DBG
// verify that the same structure is pointed to by the other maps.
ASSERT(m_hMTNodeMap.find(hMTNode)->second == pMapInfo); #endif
m_hMTNodeMap.erase(hMTNode); m_hNodeMap.erase(hNode);
// finally delete the TreeViewMapInfo structure
delete pMapInfo;
return sc; }
// Fast lookup methods
/*+-------------------------------------------------------------------------*
* * CTreeViewMap::ScGetHNodeFromHMTNode * * PURPOSE: Quickly (log n time) retrieves the HNODE for an HMTNODE. * * PARAMETERS: * HMTNODE hMTNode : * ou t : * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC CTreeViewMap::ScGetHNodeFromHMTNode (HMTNODE hMTNode, /*out*/ HNODE* phNode) // fast conversion from hNode to hMTNode.
{ DECLARE_SC(sc, TEXT("CTreeViewMap::ScGetHNode"));
// validate parameters
sc = ScCheckPointers((LPVOID) hMTNode, phNode); if(sc) return sc;
// find the mapinfo structure.
HMTNodeLookupMap::iterator iter = m_hMTNodeMap.find(hMTNode); if(iter == m_hMTNodeMap.end()) return (sc = ScFromMMC(IDS_NODE_NOT_FOUND));
TreeViewMapInfo *pMapInfo = iter->second; // find the map info structure.
if(!pMapInfo) return (sc = E_UNEXPECTED);
*phNode = pMapInfo->hNode;
return sc; }
/*+-------------------------------------------------------------------------*
* * CTreeViewMap::ScGetHTreeItemFromHNode * * PURPOSE: Quickly (log n time) retrieves the HTREEITEM for an HNODE. * * PARAMETERS: * HNODE hNode : * ou t : * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC CTreeViewMap::ScGetHTreeItemFromHNode(HNODE hNode, /*out*/ HTREEITEM* phti) // fast conversion from HTREEITEM to HNODE
{ DECLARE_SC(sc, TEXT("CTreeViewMap::ScGetHTreeItem"));
// validate parameters
sc = ScCheckPointers((LPVOID) hNode, phti); if(sc) return sc;
// find the mapinfo structure.
HNodeLookupMap::iterator iter = m_hNodeMap.find(hNode); if(iter == m_hNodeMap.end()) return (sc = E_UNEXPECTED);
TreeViewMapInfo *pMapInfo = iter->second; // find the map info structure.
if(!pMapInfo) return (sc = E_UNEXPECTED);
*phti = pMapInfo->hti;
return sc; }
/*+-------------------------------------------------------------------------*
* * CTreeViewMap::ScGetHTreeItemFromHMTNode * * PURPOSE: Quickly (log n time) retrieves the HTREEITEM for an HMTNODE. * * PARAMETERS: * HMTNODE hMTNode : * ou t : * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC CTreeViewMap::ScGetHTreeItemFromHMTNode(HMTNODE hMTNode, /*out*/ HTREEITEM* phti) // fast conversion from HMTNode to HTREEITEM.
{ DECLARE_SC(sc, TEXT("CTreeViewMap::ScGetHTreeItem"));
// validate parameters
//sc = ScCheckPointers(hMTNode, phti);
if(sc) return sc;
// find the mapinfo structure.
HMTNodeLookupMap::iterator iter = m_hMTNodeMap.find(hMTNode); if(iter == m_hMTNodeMap.end()) return (sc = E_UNEXPECTED);
TreeViewMapInfo *pMapInfo = iter->second; // find the map info structure.
if(!pMapInfo) return (sc = E_UNEXPECTED);
*phti = pMapInfo->hti;
return sc; }
//############################################################################
//############################################################################
//
// Implementation of class CAMCTreeView
//
//############################################################################
//############################################################################
/////////////////////////////////////////////////////////////////////////////
// CAMCTreeView
DEBUG_DECLARE_INSTANCE_COUNTER(CAMCTreeView);
CAMCTreeView::CAMCTreeView() : m_FontLinker (this) { DEBUG_INCREMENT_INSTANCE_COUNTER(CAMCTreeView);
m_fInCleanUp = FALSE; m_fInExpanding = FALSE;
m_pAMCView = NULL;
SetHasList(TRUE); SetTempSelectedItem (NULL); ASSERT (!IsTempSelectionActive());
AddObserver(static_cast<CTreeViewObserver&>(m_treeMap)); // add an observer to this control.
}
CAMCTreeView::~CAMCTreeView() { DEBUG_DECREMENT_INSTANCE_COUNTER(CAMCTreeView); // Smart pointer are released during their destructor
}
IMPLEMENT_DYNCREATE(CAMCTreeView, CTreeView)
BEGIN_MESSAGE_MAP(CAMCTreeView, CTreeView) //{{AFX_MSG_MAP(CAMCTreeView)
ON_WM_CREATE() ON_NOTIFY_REFLECT(TVN_SELCHANGED, OnSelChanged) ON_NOTIFY_REFLECT(TVN_SELCHANGING, OnSelChanging) ON_NOTIFY_REFLECT(TVN_GETDISPINFO, OnGetDispInfo) ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING, OnItemExpanding) ON_NOTIFY_REFLECT(TVN_ITEMEXPANDED, OnItemExpanded) ON_WM_DESTROY() ON_WM_KEYDOWN() ON_WM_SYSKEYDOWN() ON_WM_SYSCHAR() ON_WM_MOUSEACTIVATE() ON_WM_SETFOCUS() ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBeginDrag) ON_NOTIFY_REFLECT(TVN_BEGINRDRAG, OnBeginRDrag) ON_WM_KILLFOCUS() //}}AFX_MSG_MAP
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw) END_MESSAGE_MAP()
/*+-------------------------------------------------------------------------*
* CAMCTreeView::ScSetTempSelection * * Applies temporary selection to the specified HTREEITEM. *--------------------------------------------------------------------------*/
SC CAMCTreeView::ScSetTempSelection (HTREEITEM htiTempSelect) { AFX_MANAGE_STATE (AfxGetAppModuleState()); DECLARE_SC (sc, _T("CAMCTreeView::ScSetTempSelection"));
/*
* Don't use ScSetTempSelection(NULL) to remove temporary selection; * use ScRemoveTempSelection instead. */ ASSERT (htiTempSelect != NULL); if (htiTempSelect == NULL) return (sc = E_FAIL);
/*
* If this fails, you must first call ScRemoveTempSelection to remove * the temporary selection state (TVIS_SELECTED) from the current * temporary selection. */ ASSERT (!IsTempSelectionActive());
SetTempSelectedItem (htiTempSelect); ASSERT (GetTempSelectedItem() == htiTempSelect);
HTREEITEM htiSelected = GetSelectedItem();
if (htiSelected != htiTempSelect) { SetItemState (htiSelected, 0, TVIS_SELECTED); SetItemState (htiTempSelect, TVIS_SELECTED, TVIS_SELECTED); }
ASSERT (IsTempSelectionActive()); return (sc); }
/*+-------------------------------------------------------------------------*
* CAMCTreeView::ScRemoveTempSelection * * Removes the temporary selection from the current temporarily selected * item, if there is one, and restores it to the item that was selected * when the temp selection was applied. *--------------------------------------------------------------------------*/
SC CAMCTreeView::ScRemoveTempSelection () { AFX_MANAGE_STATE (AfxGetAppModuleState()); DECLARE_SC (sc, _T("CAMCTreeView::ScRemoveTempSelection"));
if (!IsTempSelectionActive()) return (sc = S_FALSE);
HTREEITEM htiTempSelect = GetTempSelectedItem(); HTREEITEM htiSelected = GetSelectedItem();
if (htiTempSelect != htiSelected) { SetItemState (htiTempSelect, 0, TVIS_SELECTED); SetItemState (htiSelected, TVIS_SELECTED, TVIS_SELECTED); }
SetTempSelectedItem (NULL); ASSERT (!IsTempSelectionActive());
return (sc); }
/*+-------------------------------------------------------------------------*
* CAMCTreeView::ScReselect * * *--------------------------------------------------------------------------*/
SC CAMCTreeView::ScReselect () { AFX_MANAGE_STATE (AfxGetAppModuleState()); NM_TREEVIEW nmtv;
nmtv.itemOld.hItem = nmtv.itemNew.hItem = GetSelectedItem();
if (nmtv.itemOld.hItem) { nmtv.itemOld.lParam = nmtv.itemNew.lParam = GetItemData(nmtv.itemOld.hItem);
LRESULT lUnused; OnSelChangingWorker (&nmtv, &lUnused); OnSelChangedWorker (&nmtv, &lUnused); }
return (S_OK); }
/////////////////////////////////////////////////////////////////////////////
// CAMCTreeView message handlers
BOOL CAMCTreeView::PreCreateWindow(CREATESTRUCT& cs) { cs.style |= TVS_EDITLABELS | TVS_HASBUTTONS | TVS_HASLINES | TVS_SHOWSELALWAYS; cs.dwExStyle |= WS_EX_CLIENTEDGE;
// do not paint over the children
cs.style |= WS_CLIPCHILDREN;
return CTreeView::PreCreateWindow(cs); }
INodeCallback* CAMCTreeView::GetNodeCallback() { return m_pAMCView->GetNodeCallback(); }
inline IScopeTreeIter* CAMCTreeView::GetScopeIterator() { return m_pAMCView->GetScopeIterator(); }
inline IScopeTree* CAMCTreeView::GetScopeTree() { return m_pAMCView->GetScopeTree(); }
void CAMCTreeView::OnGetDispInfo(NMHDR* pNMHDR, LRESULT* pResult) { HRESULT hr; TV_DISPINFO* ptvdi = (TV_DISPINFO*)pNMHDR;
HNODE hNode = NodeFromLParam (ptvdi->item.lParam); ASSERT(m_pAMCView != NULL);
INodeCallback* spCallback = GetNodeCallback(); ASSERT(spCallback != NULL); if (hNode) { if (ptvdi->item.mask & TVIF_TEXT) { tstring strName; hr = spCallback->GetDisplayName(hNode, strName); if (hr != S_OK) { ptvdi->item.pszText[0] = _T('\0'); ASSERT(FALSE); } else { // copy the text, but not too much
ASSERT (!IsBadWritePtr (ptvdi->item.pszText, ptvdi->item.cchTextMax)); _tcsncpy (ptvdi->item.pszText, strName.data(), ptvdi->item.cchTextMax);
/*
* _tcsncpy won't terminate the destination if the * source is bigger than the buffer; make sure the * string is NULL-terminated */ ptvdi->item.pszText[ptvdi->item.cchTextMax-1] = _T('\0');
/*
* If this is the selected item and it's text has changed, * fire an event so observers can know. */ if ((m_strSelectedItemText != strName.data()) && (GetSelectedItem() == ptvdi->item.hItem)) { m_strSelectedItemText = strName.data(); SC sc = ScFireEvent (CTreeViewObserver::ScOnSelectedItemTextChanged, (LPCTSTR) m_strSelectedItemText); if (sc) sc.TraceAndClear(); } } }
int nImage, nSelectedImage; hr = spCallback->GetImages(hNode, &nImage, &nSelectedImage);
#ifdef DBG
if (hr != S_OK) { ASSERT(nImage == 0 && nSelectedImage == 0); } #endif
if (ptvdi->item.mask & TVIF_IMAGE) ptvdi->item.iImage = nImage;
if (ptvdi->item.mask & TVIF_SELECTEDIMAGE) ptvdi->item.iSelectedImage = nSelectedImage;
// We will get this request once, the first time the scope item comes into view
if (ptvdi->item.mask & TVIF_CHILDREN) { ptvdi->item.cChildren = (spCallback->IsExpandable(hNode) != S_FALSE);
// set children to fixed value, to avoid any more callbacks
SetCountOfChildren(ptvdi->item.hItem, ptvdi->item.cChildren); } } else { ASSERT(0 && "OnGetDispInfo(HNODE is NULL)"); }
*pResult = 0; }
//
// Description: This method will set the folders Button(+/-) on or
// of depending on the value of bState
//
// Parameters:
// hItem: the tree item affected
// bState: TRUE = Enable for folder to show it has children
// FALSE = Disable for folder to show it has NO children
//
void CAMCTreeView::SetButton(HTREEITEM hItem, BOOL bState) { ASSERT(hItem != NULL);
TV_ITEM item; ZeroMemory(&item, sizeof(item));
item.hItem = hItem; item.mask = TVIF_HANDLE | TVIF_CHILDREN; item.cChildren = bState;
SetItem(&item); }
//+-------------------------------------------------------------------
//
// Member: CAMCTreeView::ExpandNode
//
// Synopsis: This method populates hItem's(parent folder) children into
// the tree control.
// In the first phase, Expand notifications are sent to the snapin
// which causes all children to be inserted to the master scope
// tree.
// In the second phase, we step through these children and insert
// them to the tree controls.
// For performance reasons, we walk the child list in reverse order
// (last child to first child) inserting the current item at the
// first position.
// If we were to walk the child list in normal order (first to last
// child) inserting the current item at the last position -- the
// underlying tree control takes time that grows exponentially in
// the number of children (as opposed to linearly).
//
// Arguments: hItem: the parent
//
// Returns: TRUE on success and FALSE on failure
//
//--------------------------------------------------------------------
BOOL CAMCTreeView::ExpandNode(HTREEITEM hItem) { TRACE_METHOD(CAMCTreeView, ExpandNode);
// not frequently, but... snap-in will display the dialog, dismissing that will
// activate the frame again. Tree item will be automatically selected if there
// is none selected yet. Following will prevent the recursion.
if (m_fInExpanding) return FALSE;
HRESULT hr;
// Get the HNODE from the tree node
HNODE hNode = GetItemNode(hItem); ASSERT(hNode != NULL); ASSERT(m_pAMCView != NULL);
HMTNODE hMTNode; INodeCallback* spCallback = GetNodeCallback(); ASSERT(spCallback != NULL); hr = spCallback->GetMTNode(hNode, &hMTNode); ASSERT(hr == S_OK);
if (hr == S_OK) { // The notify will return S_FALSE to indicate already expanded
// or E_xxxx to indicate an error.
hr = spCallback->Notify(hNode, NCLBK_EXPAND, FALSE, 0);
if (hr == S_FALSE) {
__try { m_fInExpanding = TRUE; hr = spCallback->Notify(hNode, NCLBK_EXPAND, TRUE, 0); } __finally { m_fInExpanding = FALSE; }
if (SUCCEEDED(hr)) { IScopeTreeIter* spIterator = m_pAMCView->GetScopeIterator(); hr = spIterator->SetCurrent(hMTNode); HMTNODE hMTChildNode;
// Get the last child for the current iterator node:
// (Starting node for walking the child list in reverse order)
if ((spIterator->LastChild(&hMTChildNode) == S_OK) && (hMTChildNode)) // Do nothing if last child is NULL
{ IScopeTree* spScopeTree = m_pAMCView->GetScopeTree(); HNODE hNewNode;
// Set the last child as the "current" iterator node
if (spIterator->SetCurrent(hMTChildNode) == S_OK) { HMTNODE hCurrentChildNode = hMTChildNode; do { // Insert current node into the tree control
spScopeTree->CreateNode(hCurrentChildNode, reinterpret_cast<LONG_PTR>(m_pAMCView->GetViewData()), FALSE, &hNewNode);
#include "pushwarn.h"
#pragma warning(disable: 4552) // "!=" operator has no effect
// Insert at the first position
VERIFY(InsertNode(hItem, hNewNode, TVI_FIRST) != NULL); #include "popwarn.h"
// give 'em a chance to do the "preload" thing, if applicable
spCallback->PreLoad (hNewNode);
// Traverse the child list in reverse order:
// Set current iterator node to the previous sibling
hr = spIterator->Prev(&hCurrentChildNode); if(FAILED(hr)) return FALSE;
// child list completely traversed.
if (!hCurrentChildNode) break;
} while (1); } }
spCallback->Notify(hNode, NCLBK_EXPANDED, 0, 0); } } }
return SUCCEEDED(hr); }
HTREEITEM CAMCTreeView::InsertNode(HTREEITEM hParent, HNODE hNode, HTREEITEM hInsertAfter) { DECLARE_SC(sc, TEXT("CAMCTreeView::InsertNode")); ASSERT(hParent != NULL); ASSERT(hNode != NULL); HRESULT hr;
TV_INSERTSTRUCT tvInsertStruct; TV_ITEM& item = tvInsertStruct.item;
ZeroMemory(&tvInsertStruct, sizeof(tvInsertStruct));
// Insert item at the end of the hItem chain
tvInsertStruct.hParent = hParent; tvInsertStruct.hInsertAfter = hInsertAfter;
item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN | TVIF_PARAM | TVIF_TEXT;
item.pszText = LPSTR_TEXTCALLBACK; item.lParam = LParamFromNode (hNode);
INodeCallback* spCallback = GetNodeCallback(); ASSERT(spCallback != NULL);
// Set callback mode for children, so we don't have to determine this
// until the scope item becomes visible (it can be expensive).
item.cChildren = I_CHILDRENCALLBACK;
spCallback->GetImages(hNode, &item.iImage, &item.iSelectedImage);
HTREEITEM hti = InsertItem(&tvInsertStruct);
HMTNODE hMTNode = NULL;
sc = spCallback->GetMTNode(hNode, &hMTNode); if(sc) sc.TraceAndClear();
// send an event to all interested observers
sc = ScFireEvent(CTreeViewObserver::ScOnItemAdded, &tvInsertStruct, hti, hMTNode); if(sc) sc.TraceAndClear();
if (hParent != TVI_ROOT && hti != NULL) SetCountOfChildren(hParent, 1);
return hti; }
void CAMCTreeView::ResetNode(HTREEITEM hItem) { if (hItem == NULL) return;
TV_ITEM item; ZeroMemory(&item, sizeof(item));
item.hItem = hItem; item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATE | TVIF_TEXT | TVIF_CHILDREN; item.pszText = LPSTR_TEXTCALLBACK; item.iImage = I_IMAGECALLBACK; item.iSelectedImage = I_IMAGECALLBACK; item.cChildren = I_CHILDRENCALLBACK; item.lParam = GetItemData(hItem);
SetItem(&item); }
void CAMCTreeView::OnItemExpanding(NMHDR* pNMHDR, LRESULT* pResult) { TRACE_METHOD(CAMCTreeView, OnItemExpanding);
HRESULT hr;
NM_TREEVIEW* pNotify = (NM_TREEVIEW*)pNMHDR; ASSERT(pNotify != NULL);
HTREEITEM &hItem = pNotify->itemNew.hItem; ASSERT(hItem != NULL);
BOOL bExpand = FALSE;
// Iteratate the folders below this item
if (pNotify->action == TVE_EXPAND) { /*
* Bug 333971: Node expansion might take awhile. Supply a wait cursor * for all of the UI-challenged snap-ins out there. */ SetCursor (LoadCursor (NULL, IDC_WAIT));
ExpandNode(hItem); bExpand = TRUE;
/*
* return the arrow */ SetCursor (LoadCursor (NULL, IDC_ARROW)); }
INodeCallback* pCallback = GetNodeCallback(); ASSERT(pCallback != NULL); HNODE hNode = GetItemNode (hItem); pCallback->Notify(hNode, NCLBK_SETEXPANDEDVISUALLY, bExpand, 0);
// If item has no children remove the + sign
if (GetChildItem(hItem) == NULL) SetButton(hItem, FALSE);
*pResult = 0; }
/*+-------------------------------------------------------------------------*
* CAMCTreeView::OnItemExpanded * * TVN_ITEMEXPANDED handler for CAMCTreeView. *--------------------------------------------------------------------------*/
void CAMCTreeView::OnItemExpanded(NMHDR* pNMHDR, LRESULT* pResult) { DECLARE_SC (sc, _T("CAMCTreeView::OnItemExpanded"));
NM_TREEVIEW* pnmtv = (NM_TREEVIEW*)pNMHDR; sc = ScCheckPointers (pnmtv); if (sc) return;
/*
* Bug 23153: when collapsing, totally collapse the tree beneath the * collapsing item. We do this in OnItemExpanded rather than * OnItemExpanding so we won't see the collapse happen. */ if (pnmtv->action == TVE_COLLAPSE) { CWaitCursor wait; CollapseChildren (pnmtv->itemNew.hItem); } }
/*+-------------------------------------------------------------------------*
* CAMCTreeView::CollapseChildren * * Collapses each descendent node of htiParent. *--------------------------------------------------------------------------*/
void CAMCTreeView::CollapseChildren (HTREEITEM htiParent) { HTREEITEM htiChild;
for (htiChild = GetChildItem (htiParent); htiChild != NULL; htiChild = GetNextItem (htiChild, TVGN_NEXT)) { Expand (htiChild, TVE_COLLAPSE); CollapseChildren (htiChild); } }
void CAMCTreeView::OnDeSelectNode(HNODE hNode) { DECLARE_SC(sc, TEXT("CAMCTreeView::OnDeSelectNode"));
{ // tell all interested observers about the deselection.
// NOTE: the order is important - legacy snapins believe they can access the
// result pane at this point and have the items still there.
// But this is intermediate state, so Com events are locked out until the
// results are cleared.
// see windows bug (ntbug09) bug# 198660. (10/11/00)
LockComEventInterface(AppEvents); sc = ScFireEvent(CTreeViewObserver::ScOnItemDeselected, hNode); if(sc) return;
// Ensure the result view is clean.
if (HasList()) { // First findout if the result view is properly
// set in the nodemgr by asking IFramePrivate.
IFramePrivatePtr spFrame = m_spResultData; if (NULL != spFrame) { BOOL bIsResultViewSet = FALSE; sc = spFrame->IsResultViewSet(&bIsResultViewSet);
// The result view is set, clean it up.
if (bIsResultViewSet) { m_spResultData->DeleteAllRsltItems(); m_spResultData->ResetResultData(); } } } }
// don't have a valid result pane type anymore.
SetHasList(false); }
// Note that OnSelectNode will return S_FALSE if the snap-in changes
// the selection during the process of selecting the requested node.
// A caller that gets an S_FALSE should assume that a different node
// is selected and continue accordingly.
HRESULT CAMCTreeView::OnSelectNode(HTREEITEM hItem, HNODE hNode) { DECLARE_SC(sc, _T("CAMCTreeView::OnSelectNode"));
if (!hItem) { TraceError(_T("Null hItem ptr\n"), sc); sc = S_FALSE; return sc.ToHr(); }
if (!hNode) { TraceError(_T("Null hNode ptr\n"), sc); sc = S_FALSE; return sc.ToHr(); }
// First ensure that the node has been enumerated by calling expand node.
ExpandNode(hItem);
// set up the AMCView correctly.
BOOL bAddSubFolders = FALSE;
sc = m_pAMCView->ScOnSelectNode(hNode, bAddSubFolders); if(sc) return sc.ToHr();
SetHasList(m_pAMCView->HasList());
// add subfolders if necessary.
if(bAddSubFolders) { sc = AddSubFolders(hItem, m_spResultData); if (sc) return sc.ToHr(); }
if (HasList()) m_spResultData->SetLoadMode(FALSE); // SetLoadMode(FALSE) was called by CAMCView::OnSelectNode.
// Need to change so that both calls are from the same function.
// get the node callback
INodeCallback* spNodeCallBack = GetNodeCallback(); sc = ScCheckPointers(spNodeCallBack, E_UNEXPECTED); if (sc) return sc.ToHr();
// send preload notify to children
HTREEITEM hti = GetChildItem (hItem); while (hti != NULL) { HNODE hNode = GetItemNode (hti); if (hNode != 0) spNodeCallBack->PreLoad (hNode); hti = GetNextItem(hti, TVGN_NEXT); }
return S_OK; }
/*+-------------------------------------------------------------------------*
* CAMCTreeView::SetNavigatingWithKeyboard * * *--------------------------------------------------------------------------*/
void CAMCTreeView::SetNavigatingWithKeyboard (bool fKeyboardNav) { /*
* if the requested state doesn't match the current state, * change the current state to match the request */ if (fKeyboardNav != IsNavigatingWithKeyboard()) { m_spKbdNavDelay = std::auto_ptr<CKeyboardNavDelay>( (fKeyboardNav) ? new CKeyboardNavDelay (this) : NULL /*assigning NULL deletes*/); } }
/*+-------------------------------------------------------------------------*
* CAMCTreeView::OnSelChanging * * TVN_SELCHANGING handler for CAMCTreeView. *--------------------------------------------------------------------------*/
void CAMCTreeView::OnSelChanging(NMHDR* pNMHDR, LRESULT* pResult) { *pResult = 0;
if (!IsNavigatingWithKeyboard()) OnSelChangingWorker ((NM_TREEVIEW*) pNMHDR, pResult); }
/*+-------------------------------------------------------------------------*
* CAMCTreeView::OnSelChanged * * TVN_SELCHANGED handler for CAMCTreeView. *--------------------------------------------------------------------------*/
void CAMCTreeView::OnSelChanged(NMHDR* pNMHDR, LRESULT* pResult) { NM_TREEVIEW* pnmtv = (NM_TREEVIEW*) pNMHDR; *pResult = 0;
if (IsNavigatingWithKeyboard()) m_spKbdNavDelay->ScStopTimer();
SetNavigatingWithKeyboard (pnmtv->action == TVC_BYKEYBOARD);
bool fDelayedSelection = IsNavigatingWithKeyboard() && !m_spKbdNavDelay->ScStartTimer(pnmtv).IsError();
if (!fDelayedSelection) OnSelChangedWorker (pnmtv, pResult); }
/*+-------------------------------------------------------------------------*
* CAMCTreeView::CKeyboardNavDelay::CKeyboardNavDelay * * *--------------------------------------------------------------------------*/
CAMCTreeView::CKeyboardNavDelay::CKeyboardNavDelay (CAMCTreeView* pTreeView) : m_pTreeView (pTreeView) { ZeroMemory (&m_nmtvSelChanged, sizeof (m_nmtvSelChanged)); }
/*+-------------------------------------------------------------------------*
* CAMCTreeView::CKeyboardNavDelay::OnTimer * * Called when the keyboard navigation delay timer fires. When that happens, * we need to perform the selection *--------------------------------------------------------------------------*/
void CAMCTreeView::CKeyboardNavDelay::OnTimer() { /*
* we don't need any more ticks from this timer (ignoring errors) */ ScStopTimer(); Trace (tagKeyboardNavDelay, _T("Applying delayed scope selection change"));
LRESULT lUnused = 0; m_pTreeView->OnSelChangedWorker (&m_nmtvSelChanged, &lUnused); m_pTreeView->SetNavigatingWithKeyboard (false);
/*
* HANDS OFF! CAMCTreeView::SetNavigatingWithKeyboard deleted this object! */ }
/*+-------------------------------------------------------------------------*
* CAMCTreeView::CKeyboardNavDelay::ScStartTimer * * *--------------------------------------------------------------------------*/
SC CAMCTreeView::CKeyboardNavDelay::ScStartTimer(NMTREEVIEW* pnmtv) { DECLARE_SC (sc, _T("CAMCTreeView:CKeyboardNavDelay::ScStartTimer"));
/*
* let the base class start the timer */ sc = BaseClass::ScStartTimer(); if (sc) return (sc);
/*
* copy the notification struct so we can send it when our timer ticks */ m_nmtvSelChanged = *pnmtv;
return (sc); }
void CAMCTreeView::OnSelChangedWorker(NM_TREEVIEW* pnmtv, LRESULT* pResult) { TRACE_METHOD(CAMCTreeView, OnSelChangedWorker);
if (m_fInCleanUp == TRUE) return;
// See which pane has focus. Some snapins/ocx may steal the focus
// so we restore the focus after selecting the node.
ASSERT (m_pAMCView != NULL); const CConsoleView::ViewPane ePane = m_pAMCView->GetFocusedPane();
//
// Select the new node
//
// Disable drawing to avoid seeing intermediate tree states.
UpdateWindow(); HRESULT hr = OnSelectNode(pnmtv->itemNew.hItem, (HNODE)pnmtv->itemNew.lParam);
if (hr == S_OK) { CStandardToolbar* pStdToolbar = m_pAMCView->GetStdToolbar(); ASSERT(NULL != pStdToolbar); if (NULL != pStdToolbar) { pStdToolbar->ScEnableUpOneLevel(GetRootItem() != pnmtv->itemNew.hItem);
pStdToolbar->ScEnableExportList(m_pAMCView->HasListOrListPad()); } *pResult = 0; } else if (hr == S_FALSE) { // snap-in changed the selection on us, so don't continue with this node.
return; } else { // something wrong with the node we are trying to select, reselect the old one
// SelectItem(pnmtv->itemOld.hItem);
MMCMessageBox(IDS_SNAPIN_FAILED_INIT); *pResult = hr; }
/*
* Even if the active view hasn't changed always restore the active view. * Reason being, for OCX's even though they have the focus, they require * MMC to inform that OCX being selected. (see bug: 180964) */ switch (ePane) { case CConsoleView::ePane_ScopeTree: { // if another view was made active, switch it back.
// View could still be active, but have focus stolen by
// a snap-in or ocx, so ensure view has focus too.
CFrameWnd* pFrame = GetParentFrame();
if (pFrame->GetActiveView() != this) pFrame->SetActiveView(this);
else if (::GetFocus() != m_hWnd) SetFocus();
break; }
case CConsoleView::ePane_Results: // If the result pane has the focus before and after
// the node was selected, then the last event snapin
// receives is scope selected which is incorrect.
// So we first set scope pane as active view but do
// not send notifications. Then we set result pane
// as active view which sends scope de-select and
// result pane select.
// Set Scope pane as active view and we also want to
// be notified about this active view so that our
// view activation observers will know who is the
// active view.
GetParentFrame()->SetActiveView(this, true);
// Now set result pane as active view and ask for notifications.
m_pAMCView->ScDeferSettingFocusToResultPane(); break;
case CConsoleView::ePane_None: // no pane is active, do nothing
break;
default: m_pAMCView->ScSetFocusToPane (ePane); break; }
/*
* Bug 345402: Make sure the focus rect is on the list control (if it * actually has the focus) to wake up any accessibility tools that might * be watching for input and focus changes. */ m_pAMCView->ScJiggleListViewFocus (); }
void CAMCTreeView::OnSelChangingWorker (NM_TREEVIEW* pnmtv, LRESULT* pResult) { TRACE_METHOD(CAMCTreeView, OnSelChangingWorker);
if (m_fInCleanUp == TRUE) return;
//
// De-select the current node
//
OnDeSelectNode ((HNODE)pnmtv->itemOld.lParam);
*pResult = 0; }
HRESULT CAMCTreeView::AddSubFolders(MTNODEID* pIDs, int length) { ASSERT(pIDs != NULL && length != 0);
HRESULT hr = E_FAIL;
// first make sure the specified node is expanded in the tree ctrl
HTREEITEM hti = ExpandNode(pIDs, length, TRUE, false /*bExpandVisually*/); ASSERT(hti != NULL);
// if successful, add the node's subfolders to the list view
if (hti != NULL) { hr = AddSubFolders(hti, m_spResultData); ASSERT(SUCCEEDED(hr)); }
return hr; }
HRESULT CAMCTreeView::AddSubFolders(HTREEITEM hti, LPRESULTDATA pResultData) { HRESULT hr; RESULTDATAITEM tRDI; ::ZeroMemory(&tRDI, sizeof(tRDI));
tRDI.mask = RDI_STR | RDI_IMAGE | RDI_PARAM; tRDI.nCol = 0; tRDI.str = MMC_TEXTCALLBACK; tRDI.nImage = MMC_IMAGECALLBACK; tRDI.nIndex = -1;
hti = GetChildItem(hti);
ASSERT(m_pAMCView != NULL); INodeCallback* spCallback = GetNodeCallback(); ASSERT(spCallback != NULL);
while (hti != NULL) { HNODE hNode = GetItemNode (hti);
if (hNode != 0) { tRDI.lParam = LParamFromNode (hNode);
hr = pResultData->InsertItem(&tRDI); CHECK_HRESULT(hr);
if (SUCCEEDED(hr)) hr = spCallback->SetResultItem(hNode, tRDI.itemID);
// add custom image if any
spCallback->AddCustomFolderImage (hNode, m_spRsltImageList); }
hti = GetNextItem(hti, TVGN_NEXT); }
return S_OK; }
int CAMCTreeView::OnCreate(LPCREATESTRUCT lpCreateStruct) { DECLARE_SC (sc, _T("CAMCTreeView::OnCreate")); TRACE_METHOD(CAMCTreeView, OnCreate);
if (CTreeView::OnCreate(lpCreateStruct) == -1) return -1;
m_pAMCView = ::GetAMCView (this); if (NULL == m_pAMCView) { ASSERT(m_pAMCView != NULL); sc = E_UNEXPECTED; return (-1); }
IScopeTree* spScopeTree = m_pAMCView->GetScopeTree(); ASSERT(spScopeTree != NULL);
HIMAGELIST hImageList; spScopeTree->GetImageList(reinterpret_cast<PLONG_PTR>(&hImageList));
CBitmap bmp; bmp.LoadBitmap(MAKEINTRESOURCE(IDB_AMC_NODES16)); int i = ImageList_AddMasked(hImageList, (HBITMAP) bmp.GetSafeHandle(), RGB(255,0,255));
ASSERT(i != -1 && "ImageList_Add failed.");
TreeView_SetImageList( *this, hImageList, TVSIL_NORMAL );
sc = ScRegisterAsDropTarget(m_hWnd); if (sc) return (-1);
sc = CreateNodeManager(); if (sc) return (-1);
return 0; }
BOOL CAMCTreeView::DestroyWindow() { TRACE_METHOD(CAMCTreeView, DestroyWindow);
CleanUp();
return CTreeView::DestroyWindow(); }
void CAMCTreeView::DeleteNode( HTREEITEM htiToDelete, BOOL fDeleteThis) { // Ensure curr sel is not a child of the item to be deleted.
for (HTREEITEM hti = GetSelectedItem(); hti != NULL; hti = GetParentItem(hti)) { if (htiToDelete == hti) { if (fDeleteThis == TRUE) { hti = GetParentItem(hti); if (hti) SelectItem(hti); } break; } }
// There are two paths to this function. Path 1, the view is deleted and there is no
// longer a root node. Path 2. When a node is manually deleted, the selection is updated
// in CAMCView::OnUpdateSelectionForDelete, therefore, the above code traverses to the root node
ASSERT(hti == NULL || fDeleteThis == FALSE);
// Collapse the node for performance reasons:
// For each node deleted, the underlying tree control re-adjusts
// the scroll bar dimensions. This involves walking the succeeding
// visible nodes; collapsing reduces the number of such nodes.
Expand(htiToDelete, TVE_COLLAPSE);
SDeleteNodeInfo dniLocal = {htiToDelete, hti, fDeleteThis}; _DeleteNode(dniLocal); }
void CAMCTreeView::_DeleteNode(SDeleteNodeInfo& dni) { ASSERT(&dni != NULL); ASSERT(dni.htiToDelete != NULL);
if (dni.htiToDelete == NULL) return;
SDeleteNodeInfo dniLocal = {GetChildItem(dni.htiToDelete), dni.htiSelected, TRUE}; // delete all the child nodes of the node being deleted
while (dniLocal.htiToDelete != NULL) { _DeleteNode(dniLocal); dniLocal.htiToDelete = GetChildItem(dni.htiToDelete); }
if (dni.fDeleteThis == TRUE) { // Reset the temp selection cache.
// This deals with items that are right click selected (temporary) on the context
// menu
if (IsTempSelectionActive() && (GetTempSelectedItem() == dni.htiToDelete)) { SC sc = ScRemoveTempSelection (); if (sc) sc.TraceAndClear(); }
HNODE hNode = (HNODE)GetItemData(dni.htiToDelete);
HTREEITEM htiParentOfItemToDelete = GetParentItem(dni.htiToDelete);
// If the item is in list view remove it. We do not want to do this
// if it is virtual list or if selected item is "Console Root"
// in which case then parent is NULL.
if (HasList() && !m_pAMCView->IsVirtualList() && (NULL != htiParentOfItemToDelete) && (htiParentOfItemToDelete == dni.htiSelected) ) { HRESULTITEM itemID; HRESULT hr; hr = m_spResultData->FindItemByLParam(LParamFromNode(hNode), &itemID); if (SUCCEEDED(hr)) { hr = m_spResultData->DeleteItem(itemID, 0); ASSERT(SUCCEEDED(hr)); } }
// tell the tree control to nuke it
DeleteItem(dni.htiToDelete);
// send an event to all interested observers
SC sc = ScFireEvent(CTreeViewObserver::ScOnItemDeleted, hNode, dni.htiToDelete); if(sc) sc.TraceAndClear();
// tell the master tree to nuke it.
m_pAMCView->GetScopeTree()->DestroyNode(hNode);
// maintain history
m_pAMCView->GetHistoryList()->DeleteEntry (hNode); } }
void CAMCTreeView::DeleteScopeTree() { DECLARE_SC(sc, _T("CAMCTreeView::DeleteScopeTree"));
m_fInCleanUp = TRUE;
// Release the ResultView from the IFrame in the primary snapin in
// the selected node.
// This is necessary to release the result view if the selected node
// is a snap-in node.
// Free all the nodes
HTREEITEM htiRoot = GetRootItem(); if (htiRoot != NULL) DeleteNode(htiRoot, TRUE);
// First findout if the result view is properly
// set in the nodemgr by asking IFramePrivate.
IFramePrivatePtr spFrame = m_spResultData; if (NULL != spFrame) { BOOL bIsResultViewSet = FALSE; sc = spFrame->IsResultViewSet(&bIsResultViewSet);
// The result view is set, clean it up.
if (bIsResultViewSet) sc = m_spResultData->DeleteAllRsltItems(); }
m_fInCleanUp = FALSE; }
void CAMCTreeView::CleanUp() { TRACE_METHOD(CAMCTreeView, CleanUp);
m_fInCleanUp = TRUE;
m_spNodeManager = NULL; m_spHeaderCtrl = NULL; m_spResultData = NULL; m_spRsltImageList = NULL; m_spScopeData = NULL;
m_fInCleanUp = FALSE; }
void CAMCTreeView::OnDestroy() { TRACE_METHOD(CAMCTreeView, OnDestroy);
//CleanUp();
CTreeView::OnDestroy();
CleanUp(); }
HRESULT CAMCTreeView::CreateNodeManager(void) { TRACE_METHOD(CAMCTreeView, CreateNodeManager);
if (m_spScopeData) return S_OK;
#if _MSC_VER >= 1100
IFramePrivatePtr pIFrame(CLSID_NodeInit, NULL, MMC_CLSCTX_INPROC); #else
IFramePrivatePtr pIFrame(CLSID_NodeInit, MMC_CLSCTX_INPROC); #endif
ASSERT(pIFrame != NULL); if (pIFrame == NULL) return E_FAIL;
m_spScopeData = pIFrame; m_spHeaderCtrl = pIFrame;
if (m_spHeaderCtrl) pIFrame->SetHeader(m_spHeaderCtrl);
m_spResultData = pIFrame; m_spRsltImageList = pIFrame; m_spNodeManager = pIFrame;
pIFrame->SetComponentID(TVOWNED_MAGICWORD);
return S_OK; }
HTREEITEM CAMCTreeView::GetClickedNode() { TV_HITTESTINFO tvhi; tvhi.pt = (POINT)GetCaretPos(); tvhi.flags = TVHT_ONITEMLABEL; tvhi.hItem = 0;
HTREEITEM htiClicked = HitTest(&tvhi); return htiClicked; }
void CAMCTreeView::GetCountOfChildren(HTREEITEM hItem, LONG* pcChildren) { TV_ITEM tvi; tvi.hItem = hItem; tvi.mask = TVIF_CHILDREN; tvi.cChildren = 0;
GetItem(&tvi); *pcChildren = tvi.cChildren; }
void CAMCTreeView::SetCountOfChildren(HTREEITEM hItem, int cChildren) { TV_ITEM tvi; tvi.hItem = hItem; tvi.mask = TVIF_HANDLE | TVIF_CHILDREN; tvi.cChildren = cChildren;
SetItem(&tvi); }
HTREEITEM CAMCTreeView::FindNode(HTREEITEM hti, MTNODEID id) { INodeCallback* pCallback = GetNodeCallback(); static MTNODEID nID = -1; static HRESULT hr = S_OK;
hr = pCallback->GetMTNodeID(GetItemNode(hti), &nID); ASSERT(SUCCEEDED(hr)); if (FAILED(hr)) return NULL;
if (nID == id) return hti;
HTREEITEM htiTemp = GetChildItem(hti);
if (htiTemp != NULL) htiTemp = FindNode(htiTemp, id);
if (htiTemp == NULL) { htiTemp = GetNextSiblingItem(hti);
if (htiTemp != NULL) htiTemp = FindNode(htiTemp, id); }
return htiTemp;
}
HTREEITEM CAMCTreeView::FindSiblingItem(HTREEITEM hti, MTNODEID id) { INodeCallback* pCallback = GetNodeCallback(); if (!pCallback) return NULL;
static MTNODEID nID = -1; static HRESULT hr = S_OK;
while (hti != NULL) { hr = pCallback->GetMTNodeID(GetItemNode(hti), &nID); if (FAILED(hr)) return NULL;
if (nID == id) return hti;
hti = GetNextSiblingItem(hti); }
return NULL; }
//+-------------------------------------------------------------------
//
// Member: CAMCTreeView::SelectNode
//
// Synopsis: Given path to a node, select the node. If bSelectExactNode
// is false then walk the path as much as possible and select
// the last node in the best matched path. If bSelectExactNode
// is true then select the node if available else do nothing.
//
// Arguments: [pIDs] - [in] Array of node-id's (the path)
// [length] - [in] length of the above array
// [bSelectExactNode] - [in] select the exact node or not?
//
// Returns: SC, return ScFromMMC(IDS_NODE_NOT_FOUND) if select exact node is specified
// and it cannot be selected
//
//--------------------------------------------------------------------
SC CAMCTreeView::ScSelectNode(MTNODEID* pIDs, int length, bool bSelectExactNode /*= false*/) { DECLARE_SC(sc, TEXT("CAMCTreeView::ScSelectNode")); sc = ScCheckPointers(pIDs); if (sc) return sc;
if (m_fInExpanding) return (sc);
HTREEITEM hti = GetRootItem(); sc = ScCheckPointers( (void*)hti, E_UNEXPECTED); if (sc) return sc;
if (pIDs[0] != ROOTNODEID) return (sc = E_INVALIDARG);
INodeCallback* pCallback = GetNodeCallback(); sc = ScCheckPointers(pCallback, E_UNEXPECTED); if (sc) return sc;
MTNODEID nID = 0; sc = pCallback->GetMTNodeID(GetItemNode(hti), &nID); if (sc) return sc;
bool bExactNodeFound = false;
for (int i=0; i<length; ++i) { if (pIDs[i] == nID) break; }
for (++i; i < length; ++i) { if (GetChildItem(hti) == NULL) Expand(hti, TVE_EXPAND);
hti = FindSiblingItem(GetChildItem(hti), pIDs[i]);
if (hti == NULL) break; }
if (length == i) bExactNodeFound = true;
if (hti) { // If exact node is to be selected make sure we have walked through the entire path.
if ( (bSelectExactNode) && (! bExactNodeFound) ) return ScFromMMC(IDS_NODE_NOT_FOUND); // do not trace this error.
if (GetSelectedItem() == hti) ScReselect(); else SelectItem(hti); }
return sc; }
/*+-------------------------------------------------------------------------*
* * CAMCTreeView::Expand * * PURPOSE: Expands a particular tree item. This is just a wrapper around the * tree control's expand method, which allows items to be expanded * without changing the visual appearance of the tree. * * PARAMETERS: * HTREEITEM hItem : * UINT nCode : * bool bExpandVisually : * * RETURNS: * BOOL * *+-------------------------------------------------------------------------*/ BOOL CAMCTreeView::Expand(HTREEITEM hItem, UINT nCode, bool bExpandVisually) { if( (nCode==TVE_EXPAND) && (!bExpandVisually) ) { bool bExpand = true; // code repeated here from OnItemExpand - we just mimic the effect of TVN_ITEMEXPANDING.
ExpandNode(hItem);
INodeCallback* pCallback = GetNodeCallback(); ASSERT(pCallback != NULL); HNODE hNode = GetItemNode(hItem); pCallback->Notify(hNode, NCLBK_SETEXPANDEDVISUALLY, bExpand, 0);
// If item has no children remove the + sign
if (GetChildItem(hItem) == NULL) SetButton(hItem, FALSE); return true; } else return Expand(hItem, nCode); }
/*+-------------------------------------------------------------------------*
* * CAMCTreeView::ExpandNode * * PURPOSE: Expands a particular node in the tree. * * PARAMETERS: * MTNODEID* pIDs : * int length : * bool bExpand : * bool bExpandVisually : valid only if bExpand is true. If bExpandVisually * is true, the items appear in the tree. If false, * the tree appears unchanged, although items have been * added. * * RETURNS: * HTREEITEM * *+-------------------------------------------------------------------------*/ HTREEITEM CAMCTreeView::ExpandNode(MTNODEID* pIDs, int length, bool bExpand, bool bExpandVisually) { HTREEITEM hti = GetRootItem(); ASSERT(hti != NULL); ASSERT(pIDs[0] == ROOTNODEID);
INodeCallback* pCallback = GetNodeCallback(); if (!pCallback) return NULL;
MTNODEID nID = 0; HRESULT hr = pCallback->GetMTNodeID(GetItemNode(hti), &nID); if (FAILED(hr)) return NULL;
for (int i=0; i<length; ++i) { if (pIDs[i] == nID) break; }
for (++i; i < length; ++i) { if (GetChildItem(hti) == NULL) Expand(hti, TVE_EXPAND, bExpandVisually);
hti = FindSiblingItem(GetChildItem(hti), pIDs[i]);
if (hti == NULL) break; }
if (hti) Expand(hti, bExpand ? TVE_EXPAND : TVE_COLLAPSE, bExpandVisually);
return hti; }
/*+-------------------------------------------------------------------------*
* CAMCTreeView::OnKeyDown * * WM_KEYDOWN handler for CAMCTreeView. *--------------------------------------------------------------------------*/
void CAMCTreeView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { switch (nChar) { case VK_DELETE: if (m_pAMCView->IsVerbEnabled(MMC_VERB_DELETE)) { HTREEITEM hti = GetSelectedItem(); if (hti != NULL) { HNODE hNodeSel = GetItemNode(hti); ASSERT(hNodeSel != NULL);
INodeCallback* pNC = GetNodeCallback(); ASSERT(pNC != NULL); pNC->Notify(hNodeSel, NCLBK_DELETE, TRUE, 0); } return; } break; }
CTreeView::OnKeyDown(nChar, nRepCnt, nFlags); }
#ifdef DBG
void CAMCTreeView::DbgDisplayNodeName(HNODE hNode) { ASSERT(hNode != NULL);
INodeCallback* spCallback = GetNodeCallback(); ASSERT(spCallback != NULL);
tstring strName; HRESULT hr = spCallback->GetDisplayName(hNode, strName);
::MMCMessageBox( strName.data() ); }
void CAMCTreeView::DbgDisplayNodeName(HTREEITEM hti) { DbgDisplayNodeName((HNODE)GetItemData(hti)); }
#endif
/*+-------------------------------------------------------------------------*
* * CAMCTreeView::OnSysKeyDown and CAMCTreeView::OnSysChar * * PURPOSE: Handle the WM_SYSKEYDOWN and WM_SYSCHAR messages. Note: * VK_RETURN causes a beep if handled in WM_SYSKEYDOWN. And VK_LEFT and * VK_RIGHT don't cause a WM_SYSCHAR. Thats why we need to handle these * differently. * * PARAMETERS: * UINT nChar : * UINT nRepCnt : * UINT nFlags : * * RETURNS: * void * *+-------------------------------------------------------------------------*/ void CAMCTreeView::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { switch (nChar) { case VK_LEFT: case VK_RIGHT: { CWnd* pwndParent = GetParent(); ASSERT(pwndParent != NULL); if (pwndParent != NULL) pwndParent->SendMessage (WM_SYSKEYDOWN, nChar, MAKELPARAM (nRepCnt, nFlags)); return; }
default: break; }
CTreeView::OnSysKeyDown(nChar, nRepCnt, nFlags); } void CAMCTreeView::OnSysChar(UINT nChar, UINT nRepCnt, UINT nFlags) { DECLARE_SC(sc, TEXT("CAMCTreeView::OnSysChar")); switch (nChar) { case VK_RETURN: { INodeCallback* pCallback = GetNodeCallback(); CAMCView* pAMCView = GetAMCView(); sc = ScCheckPointers(pAMCView, pCallback, E_UNEXPECTED); if (sc) return;
if (! pAMCView->IsVerbEnabled(MMC_VERB_PROPERTIES)) return;
HTREEITEM hti = GetSelectedItem(); if (!hti) break;
HNODE hNode = (HNODE)GetItemData(hti); if (hNode != 0) pCallback->Notify(hNode, NCLBK_PROPERTIES, TRUE, 0);
return; }
default: break; }
CTreeView::OnSysChar(nChar, nRepCnt, nFlags); }
BOOL CAMCTreeView::OnCmdMsg( UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo ) { // Do normal command routing
if (CTreeView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE;
// if view didn't handle it, give parent view a chance
if (m_pAMCView != NULL) return static_cast<CWnd*>(m_pAMCView)->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); else return FALSE; }
int CAMCTreeView::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message) { /*------------------------------------------------------------------------*/ /* Short out the WM_MOUSEACTIVATE here to prevent default processing, */ /* which is to send the message on to succeeding parent windows until */ /* one answers the message. In our case, it goes all the way up to */ /* the main frame, which invariably decides to activate. This is a */ /* problem for two reasons: */ /* */ /* 1. On the way back down from the main frame, the message passes */ /* through CAMCView, which lets CView::OnMouseActivate do the */ /* work. CView::OnMouseActivate will set itself (CAMCView) as */ /* the active view, which in turn causes focus to be set to */ /* the view. CAMCView never wants the focus, since it is just */ /* a frame for the scope and result panes, so it will deflect */ /* the activation to the scope pane (CAMCTreeView) in */ /* CAMCView::OnSetFocus, which is where we want it to be. If */ /* we short out the processing here, we avoid excessive focus */ /* churn. It is essential that CAMCTreeView::OnSetFocus set */ /* itself as the active view to keep the bookkeeping straight. */ /* */ /* 2. If we don't short out here and avoid excessive focus churn, */ /* we have a problem with sometimes erroneously entering rename */ /* mode when the tree isn't active and the user clicks (once) on */ /* the selected item. An ordinary activation sequence goes like */ /* this: WM_MOUSEACTIVATE, WM_xBUTTONDOWN, WM_SETFOUS -- all to */ /* the tree view. The tree's button down processing doesn't enter */ /* the label edit (i.e. rename) sequence because it recognizes */ /* that it doesn't have the focus when the click happens. When */ /* the tree view is a CView, as in this case, CView::OnMouseActivate */ /* sets the focus to the tree view, causing the activation sequence */ /* to look like this: WM_MOUSEACTIVATE, WM_SETFOCUS, WM_xBUTTONDOWN. */ /* Now the tree's button down processing sees that the tree has */ /* the focus, so it enters label edit mode. BUG! Shorting out */ /* here (and relying on CAMCTreeView::OnSetFocus to properly activate */ /* the view) fixes all that. */ /*------------------------------------------------------------------------*/
return (MA_ACTIVATE); }
/*+-------------------------------------------------------------------------*
* CAMCTreeView::OnSetFocus * * WM_SETFOCUS handler for CAMCTreeView. *--------------------------------------------------------------------------*/
void CAMCTreeView::OnSetFocus(CWnd* pOldWnd) { Trace(tagTree, TEXT("OnSetFocus"));
/*
* if this view has the focus, it should be the active view */ GetParentFrame()->SetActiveView (this);
CTreeView::OnSetFocus(pOldWnd); }
/*+-------------------------------------------------------------------------*
* CAMCTreeView::OnKillFocus * * WM_KILLFOCUS handler for CAMCTreeView. *--------------------------------------------------------------------------*/
void CAMCTreeView::OnKillFocus(CWnd* pNewWnd) { Trace(tagTree, TEXT("OnKillFocus"));
CTreeView::OnKillFocus(pNewWnd);
/*
* Bug 114948 (from the "Windows NT Bugs" database, aka "the overlapping * rectangle problem"): The tree control has code to invalidate the * selected item when focus is lost. If we have a temp selection, we've * made a temporary item appear selected by fiddling with TVIS_SELECTED * states (see ScSet/RemoveTempSelection). We need to do it that way * instead of sending TVM_SELECTITEM so we don't get unwanted * TVN_SELCHANGED notifications, but it has the side effect of fooling * the tree control's WM_KILLFOCUS handler into invalidating the non-temp * selected item instead of the item that is really showing selection, the * temp item. * * This bug was originally fixed with a sledgehammer, specifically by * forcing the entire main frame and all of its children to be totally * redrawn after displaying any context menu. This caused bug 139541 * (in the "Windows Bugs" database). * * A much more surgical fix to 114948, which also avoids 139541, is to * manually invalidate the temporarily selected item. It's important * that we do this after calling the base class so it will be redrawn * in the "we don't have the focus" color (usually gray), rather than * the standard selection color. */ if (IsTempSelectionActive()) { CRect rectItem; GetItemRect (GetTempSelectedItem(), rectItem, false); RedrawWindow (rectItem); } }
void CAMCTreeView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView) { DECLARE_SC(sc, TEXT("CAMCTreeView::OnActivateView"));
#ifdef DBG
Trace(tagTree, _T("TreeView::OnActivateView (%s, pAct=0x%08x, pDeact=0x%08x))\n"), (bActivate) ? _T("true") : _T("false"), pActivateView, pDeactiveView); #endif
if ( (pActivateView != pDeactiveView) && (bActivate) ) { sc = ScFireEvent(CTreeViewObserver::ScOnTreeViewActivated); if (sc) sc.TraceAndClear(); }
CTreeView::OnActivateView(bActivate, pActivateView, pDeactiveView); }
/*+-------------------------------------------------------------------------*
* CAMCTreeView::OnCustomDraw * * NM_CUSTOMDRAW handler for CAMCTreeView. *--------------------------------------------------------------------------*/
void CAMCTreeView::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) { NMCUSTOMDRAW* pnmcd = reinterpret_cast<NMCUSTOMDRAW *>(pNMHDR); ASSERT (CWnd::FromHandle (pnmcd->hdr.hwndFrom) == this);
*pResult = m_FontLinker.OnCustomDraw (pnmcd); }
/*+-------------------------------------------------------------------------*
* CTreeFontLinker::GetItemText * * *--------------------------------------------------------------------------*/
std::wstring CTreeFontLinker::GetItemText (NMCUSTOMDRAW* pnmcd) const { USES_CONVERSION; HTREEITEM hItem = reinterpret_cast<HTREEITEM>(pnmcd->dwItemSpec); CTreeCtrl& tc = m_pTreeView->GetTreeCtrl();
return (std::wstring (T2CW (tc.GetItemText (hItem)))); }
//+-------------------------------------------------------------------
//
// Member: CAMCTreeView::ScGetTreeItemIconInfo
//
// Synopsis: Get the given node's small icon.
//
// Arguments: [hNode] - for which info is needed.
// [phIcon] - [out], ptr to HICON.
//
// Note: Caller calls DestroyIcon on the HICON returned.
//
// Returns: SC
//
//--------------------------------------------------------------------
SC CAMCTreeView::ScGetTreeItemIconInfo(HNODE hNode, HICON *phIcon) { DECLARE_SC(sc, TEXT("CAMCTreeView::ScGetTreeItemIconInfo")); sc = ScCheckPointers(hNode, phIcon); if (sc) return sc;
INodeCallback* spNodeCallBack = GetNodeCallback(); sc = ScCheckPointers(spNodeCallBack, m_pAMCView, E_UNEXPECTED); if (sc) return sc;
// Get the index.
int nImage = -1; int nSelectedImage = -1; sc = spNodeCallBack->GetImages(hNode, &nImage, &nSelectedImage); if (sc) return sc;
// Get the imagelist.
HIMAGELIST hImageList = NULL; hImageList = TreeView_GetImageList(GetSafeHwnd(), TVSIL_NORMAL); if (! hImageList) return (sc = E_FAIL);
*phIcon = ImageList_GetIcon(hImageList, nImage, ILD_TRANSPARENT); if (!*phIcon) return (sc = E_FAIL);
return sc; }
/*+-------------------------------------------------------------------------*
* * CAMCTreeView::ScRenameScopeNode * * PURPOSE: put the specified scope node into rename mode. * * PARAMETERS: * HMTNODE hMTNode : * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC CAMCTreeView::ScRenameScopeNode(HMTNODE hMTNode) { DECLARE_SC(sc, TEXT("CAMCTreeView::ScRenameScopeNode"));
if(!IsWindowVisible()) return (sc = E_FAIL);
HTREEITEM hti = NULL; sc = m_treeMap.ScGetHTreeItemFromHMTNode(hMTNode, &hti); if(sc) return sc;
// must have the focus to rename
if (::GetFocus() != m_hWnd) SetFocus();
if(NULL==EditLabel(hti)) return (sc = E_FAIL); // if for any reason the operation failed, return an error
return sc; }
|