Click here to Skip to main content
Click here to Skip to main content
 
Add your own
alternative version
Go to top

Be Sweet - a set of visual source code browsers

, 1 Jul 2003
A set of source code and project browsers to compliment Visual Studio.
besweet-latest-greatest.zip
BeSweet-0.6.2
besweet-screen.jpg
ctags55
ctags.exe
dot-net-addin-experimental
AddIn.tlb
BeSweetDotNetAddin.dll
dte.tlh
install-screen-shot.jpg
MSADDNDR.tlh
MSO.tlh
ESBAddInSimulator.exe
ESBDSAddin.dll
ESBDSAddin.tlb
ESBServer.exe
ESBServer.ini
ESBServer.pdb
ESBServer.tlb
res
abstract256.bmp
class256.bmp
close_a256.bmp
close_f256.bmp
const256.bmp
Copy of enum256.bmp
enum256.bmp
ESBServer.ico
field256.bmp
function256.bmp
headerfile256.bmp
hierarchy.ico
icon2.ico
inherited_members_a256.bmp
inherited_members_f256.bmp
lock_hierarchy_a256.bmp
lock_hierarchy_f256.bmp
members_a256.bmp
method256.bmp
namespace256.bmp
no_fields_a256.bmp
no_field_f256.bmp
no_field_s256.bmp
no_public_a256.bmp
no_public_f256.bmp
no_public_s256.bmp
no_static_a256.bmp
no_static_f256.bmp
no_static_s256.bmp
outline-icon-large.bmp
outline.ico
outline256.bmp
overwriten256.bmp
packages_a256.bmp
package_browser.ico
previous_hierarchies_a256.bmp
previous_hierarchies_f256.bmp
private256.bmp
private_field256.bmp
private_method256.bmp
problem256.bmp
problem_256.bmp
protected256.bmp
protected_field256.bmp
protected_method256.bmp
public256.bmp
public_field256.bmp
public_method256.bmp
public_only_a256.bmp
public_only__f256.bmp
public__only_s256.bmp
questionmark256.bmp
questionmark_256.bmp
selection_arrow256.bmp
sort_a256.bmp
sort_f256.bmp
sort_s256.bmp
sourcefile256.bmp
static256.bmp
struct256.bmp
subtype_hierarchy_a256.bmp
subtype_hierarchy_f256.bmp
supertype_hierarchy_a256.bmp
supertype_hierarchy_f256.bmp
typebrowser.ico
typedef256.bmp
types_a256.bmp
type_hierarchy_a256.bmp
type_hierarchy_f256.bmp
union256.bmp
variable256.bmp
virtual256.bmp
workspace.ico
workspace256.bmp
stlport_vc645.dll
besweet_061_src.zip
BeSweet_061_src
beswee-screen.jpg
EclipseStyleBrowsers.dsw
ESBDSAddin
ESBDSAddin.clw
ESBDSAddin.dsp
res
Commands.rgs
ESBDSAddin.rgs
TBarLrge.bmp
TBarMedm.bmp
src
ESBDSAddin.def
ESBDSAddinSimulator
src
ESBAddInSimulator.clw
ESBAddInSimulator.dsp
res
ESBAddInSimulator.ico
ESBDSAddin.rgs
ESBServer
bin
ctags55
ctags.exe
res
.cvsignore
abstract256.bmp
class256.bmp
close_a256.bmp
close_f256.bmp
const256.bmp
Copy of enum256.bmp
enum256.bmp
ESBServer.ico
field256.bmp
function256.bmp
headerfile256.bmp
hierarchy.ico
icon2.ico
inherited_members_a256.bmp
inherited_members_f256.bmp
lock_hierarchy_a256.bmp
lock_hierarchy_f256.bmp
members_a256.bmp
method256.bmp
namespace256.bmp
no_fields_a256.bmp
no_field_f256.bmp
no_field_s256.bmp
no_public_a256.bmp
no_public_f256.bmp
no_public_s256.bmp
no_static_a256.bmp
no_static_f256.bmp
no_static_s256.bmp
outline-icon-large.bmp
outline.ico
outline256.bmp
overwriten256.bmp
packages_a256.bmp
package_browser.ico
previous_hierarchies_a256.bmp
previous_hierarchies_f256.bmp
private256.bmp
private_field256.bmp
private_method256.bmp
problem256.bmp
problem_256.bmp
protected256.bmp
protected_field256.bmp
protected_method256.bmp
public256.bmp
public_field256.bmp
public_method256.bmp
public_only_a256.bmp
public_only__f256.bmp
public__only_s256.bmp
questionmark256.bmp
questionmark_256.bmp
selection_arrow256.bmp
sort_a256.bmp
sort_f256.bmp
sort_s256.bmp
sourcefile256.bmp
static256.bmp
struct256.bmp
subtype_hierarchy_a256.bmp
subtype_hierarchy_f256.bmp
supertype_hierarchy_a256.bmp
supertype_hierarchy_f256.bmp
typebrowser.ico
typedef256.bmp
types_a256.bmp
type_hierarchy_a256.bmp
type_hierarchy_f256.bmp
union256.bmp
variable256.bmp
virtual256.bmp
workspace.ico
workspace256.bmp
doc
esbserver.proj
images
no_fields_a256.bmp
no_static_a256.bmp
outline.jpg
outlineProperties.jpg
public_only_a256.bmp
sort_a256.bmp
src
Controller
Browsers
ESBServer.clw
ESBServer.dsp
ESBServer.ini
ESBServer.rgs
ESBServerManager.rgs
ESBServerMgr.rgs
Model
CTags
MetaModel
Simulators
RemoteInterface
COM
Renderer
MFC
.#ESBServerCtrlPanel.cpp.1.20
3rdParty
OptionPage
Base
Browsers
.#ScopedNameBrowserRenderer.cpp.1.12
.#ScopedNameBrowserRenderer.h.1.12
Properties
res
abstract256.bmp
class256.bmp
close_a256.bmp
close_f256.bmp
const256.bmp
enum256.bmp
ESBServer.ico
field256.bmp
function256.bmp
headerfile256.bmp
hierarchy.ico
inherited_members_a256.bmp
inherited_members_f256.bmp
lock_hierarchy_a256.bmp
lock_hierarchy_f256.bmp
members_a256.bmp
method256.bmp
namespace256.bmp
no_fields_a256.bmp
no_field_f256.bmp
no_field_s256.bmp
no_public_a256.bmp
no_public_f256.bmp
no_public_s256.bmp
no_static_a256.bmp
no_static_f256.bmp
no_static_s256.bmp
outline-icon-large.bmp
outline.ico
outline256.bmp
overwriten256.bmp
packages_a256.bmp
package_browser.ico
previous_hierarchies_a256.bmp
previous_hierarchies_f256.bmp
private256.bmp
private_field256.bmp
private_method256.bmp
problem256.bmp
problem_256.bmp
protected256.bmp
protected_field256.bmp
protected_method256.bmp
prototype256.bmp
prototype_256.bmp
public256.bmp
public_field256.bmp
public_method256.bmp
public_only_a256.bmp
public_only__f256.bmp
public__only_s256.bmp
questionmark256.bmp
questionmark_256.bmp
selection_arrow256.bmp
sort_a256.bmp
sort_f256.bmp
sort_s256.bmp
sourcefile256.bmp
static256.bmp
struct256.bmp
subtype_hierarchy_a256.bmp
subtype_hierarchy_f256.bmp
supertype_hierarchy_a256.bmp
supertype_hierarchy_f256.bmp
typebrowser.ico
typedef256.bmp
types_a256.bmp
type_hierarchy_a256.bmp
type_hierarchy_f256.bmp
union256.bmp
variable256.bmp
virtual256.bmp
workspace.ico
workspace256.bmp
Test
Utilities
bsuite_demo.zip
BeSweet_051
ctags54
ctags.exe
ESBDSAddin.dll
ESBDSAddin.exp
ESBDSAddin.tlb
ESBDSAddinSimulator.exe
ESBServer.exe
ESBServer.ini
ESBServer.ini.bak
ESBServer.pdb
ESBServer.tlb
regsvr32.trg
res
abstract256.bmp
class256.bmp
close_a256.bmp
close_f256.bmp
const256.bmp
Copy of enum256.bmp
enum256.bmp
ESBServer.ico
field256.bmp
function256.bmp
headerfile256.bmp
hierarchy.ico
icon2.ico
inherited_members_a256.bmp
inherited_members_f256.bmp
lock_hierarchy_a256.bmp
lock_hierarchy_f256.bmp
members_a256.bmp
method256.bmp
namespace256.bmp
no_fields_a256.bmp
no_field_f256.bmp
no_field_s256.bmp
no_public_a256.bmp
no_public_f256.bmp
no_public_s256.bmp
no_static_a256.bmp
no_static_f256.bmp
no_static_s256.bmp
outline.ico
outline256.bmp
overwriten256.bmp
packages_a256.bmp
package_browser.ico
previous_hierarchies_a256.bmp
previous_hierarchies_f256.bmp
private256.bmp
private_field256.bmp
private_method256.bmp
problem256.bmp
problem_256.bmp
protected256.bmp
protected_field256.bmp
protected_method256.bmp
public256.bmp
public_field256.bmp
public_method256.bmp
public_only_a256.bmp
public_only__f256.bmp
public__only_s256.bmp
questionmark256.bmp
questionmark_256.bmp
selection_arrow256.bmp
sort_a256.bmp
sort_f256.bmp
sort_s256.bmp
sourcefile256.bmp
static256.bmp
struct256.bmp
subtype_hierarchy_a256.bmp
subtype_hierarchy_f256.bmp
supertype_hierarchy_a256.bmp
supertype_hierarchy_f256.bmp
typebrowser.ico
typedef256.bmp
types_a256.bmp
type_hierarchy_a256.bmp
type_hierarchy_f256.bmp
union256.bmp
variable256.bmp
virtual256.bmp
workspace.ico
workspace256.bmp
stlport_vc645.dll
bsuite_src.zip
EclipseStyleBrowsers
doc
images
architecture.jpg
hierarchybrowser.jpg
methodbrowser.jpg
outlinebrowser.jpg
overview.jpg
typebrowser.jpg
vsplugin.jpg
workspacebrowser.jpg
EclipseStyleBrowsers.dsw
ESBDSAddin
ESBDSAddin.dsp
res
Commands.rgs
ESBDSAddin.rgs
TBarLrge.bmp
TBarMedm.bmp
src
CVS
ESBDSAddin.def
ESBDSAddinSimulator
ESBDSAddinSimulator.clw
ESBDSAddinSimulator.dsp
res
ESBAddInSimulator.ico
src
ESBServer
bin
ctags54
ctags.exe
res
abstract256.bmp
class256.bmp
close_a256.bmp
close_f256.bmp
const256.bmp
Copy of enum256.bmp
CVS
enum256.bmp
ESBServer.ico
field256.bmp
function256.bmp
headerfile256.bmp
hierarchy.ico
icon2.ico
inherited_members_a256.bmp
inherited_members_f256.bmp
lock_hierarchy_a256.bmp
lock_hierarchy_f256.bmp
members_a256.bmp
method256.bmp
namespace256.bmp
no_fields_a256.bmp
no_field_f256.bmp
no_field_s256.bmp
no_public_a256.bmp
no_public_f256.bmp
no_public_s256.bmp
no_static_a256.bmp
no_static_f256.bmp
no_static_s256.bmp
outline.ico
outline256.bmp
overwriten256.bmp
packages_a256.bmp
package_browser.ico
previous_hierarchies_a256.bmp
previous_hierarchies_f256.bmp
private256.bmp
private_field256.bmp
private_method256.bmp
problem256.bmp
problem_256.bmp
protected256.bmp
protected_field256.bmp
protected_method256.bmp
public256.bmp
public_field256.bmp
public_method256.bmp
public_only_a256.bmp
public_only__f256.bmp
public__only_s256.bmp
questionmark256.bmp
questionmark_256.bmp
selection_arrow256.bmp
sort_a256.bmp
sort_f256.bmp
sort_s256.bmp
sourcefile256.bmp
static256.bmp
struct256.bmp
subtype_hierarchy_a256.bmp
subtype_hierarchy_f256.bmp
supertype_hierarchy_a256.bmp
supertype_hierarchy_f256.bmp
typebrowser.ico
typedef256.bmp
types_a256.bmp
type_hierarchy_a256.bmp
type_hierarchy_f256.bmp
union256.bmp
variable256.bmp
virtual256.bmp
workspace.ico
workspace256.bmp
src
Controller
Browsers
CVS
CVS
CVS
ESBServer.clw
ESBServer.dsp
ESBServer.ini
ESBServer.rgs
ESBServerMgr.rgs
Model
CTags
CVS
CVS
MetaModel
CVS
Simulators
CVS
RemoteInterface
COM
CVS
CVS
Renderer
CVS
MFC
3rdParty
CVS
OptionPage
CVS
Base
CVS
Browsers
CVS
CVS
Properties
CVS
res
abstract256.bmp
class256.bmp
close_a256.bmp
close_f256.bmp
const256.bmp
Copy of enum256.bmp
CVS
enum256.bmp
ESBServer.ico
field256.bmp
function256.bmp
headerfile256.bmp
hierarchy.ico
icon2.ico
inherited_members_a256.bmp
inherited_members_f256.bmp
lock_hierarchy_a256.bmp
lock_hierarchy_f256.bmp
members_a256.bmp
method256.bmp
namespace256.bmp
no_fields_a256.bmp
no_field_f256.bmp
no_field_s256.bmp
no_public_a256.bmp
no_public_f256.bmp
no_public_s256.bmp
no_static_a256.bmp
no_static_f256.bmp
no_static_s256.bmp
outline-icon-large.bmp
outline.ico
outline256.bmp
overwriten256.bmp
packages_a256.bmp
package_browser.ico
previous_hierarchies_a256.bmp
previous_hierarchies_f256.bmp
private256.bmp
private_field256.bmp
private_method256.bmp
problem256.bmp
problem_256.bmp
protected256.bmp
protected_field256.bmp
protected_method256.bmp
public256.bmp
public_field256.bmp
public_method256.bmp
public_only_a256.bmp
public_only__f256.bmp
public__only_s256.bmp
questionmark256.bmp
questionmark_256.bmp
selection_arrow256.bmp
sort_a256.bmp
sort_f256.bmp
sort_s256.bmp
sourcefile256.bmp
static256.bmp
struct256.bmp
subtype_hierarchy_a256.bmp
subtype_hierarchy_f256.bmp
supertype_hierarchy_a256.bmp
supertype_hierarchy_f256.bmp
typebrowser.ico
typedef256.bmp
types_a256.bmp
type_hierarchy_a256.bmp
type_hierarchy_f256.bmp
union256.bmp
variable256.bmp
virtual256.bmp
workspace.ico
workspace256.bmp
Test
CVS
Utilities
CVS
// MltiTree.cpp : implementation file
// Copyright (c) 1999 Richard Hazlewood
// This code is provided as-is.  Use at your own peril.
//
// Multi-selection tree. Based, for the most part, on the
// selection behaviour of the listview control.
// TVN_SELCHANGING/TVN_SELCHANGED notifications are used
//  throughout: itemOld For de-selection, itemNew for selection.
// Note: TVN_SELCHANGING/TVN_SELCHANGED are still sent by default
//       tree processing for focus changes, i.e. a SetItemState passed
//       TVIS_FOCUSED without TVIS_SELECTED will still cause notification
//       (if not already focused)

//Decoding in TVN_SELCHANGED:
//B = IsEmulatedNotify
//O = itemOld.hItem != 0
//N = itemNew.hItem != 0
//
//B  O  N
//~~~~~~~
//0  1  0	A focus loss on itemOld
//0  0  1	A focus/selection gain on itemNew
//0  1  1	A focus loss on itemOld, a focus/selection gain on itemNew
//1  1  0	A selection loss on itemOld
//1  0  1	A selection gain on itemNew
//else undefined

#include "stdafx.h"
#include <windowsx.h>
#include "MltiTree.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////

#ifndef MST_TIMER_PERIOD
#define MST_TIMER_PERIOD	75		//ms
#endif

/////////////////////////////////////////////////////////////////////////////
// CMultiTree

IMPLEMENT_DYNAMIC(CMultiTree, CMultiTree_BASE)

CMultiTree::CMultiTree()
{
	m_bMulti = TRUE;
	m_hSelect = NULL;
	m_bBandLabel = FALSE;
	m_bEmulated = FALSE;
}

CMultiTree::~CMultiTree()
{
}

#define CTreeCtrl	CMultiTree_BASE
BEGIN_MESSAGE_MAP(CMultiTree, CTreeCtrl)
#undef CTreeCtrl
	//{{AFX_MSG_MAP(CMultiTree)
	ON_WM_LBUTTONDOWN()
	ON_WM_SETFOCUS()
	ON_WM_KILLFOCUS()
	ON_WM_RBUTTONDOWN()
	ON_NOTIFY_REFLECT_EX(TVN_ITEMEXPANDING, OnItemExpanding)
	ON_WM_KEYDOWN()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMultiTree message handlers

/////////////////////////////////////////////////////////////////////////////
// GetSelectedCount
// - returns number of selection tree items

UINT CMultiTree::GetSelectedCount() const
{
	UINT nCount = 0;
	HTREEITEM hItem = GetFirstSelectedItem();
	while (hItem) {
		nCount++;
		hItem = GetNextSelectedItem(hItem);
	}
	return nCount;
}

/////////////////////////////////////////////////////////////////////////////
// SetMultiSelect
// - allow mode to be turned off

BOOL CMultiTree::SetMultiSelect(BOOL bMulti)
{
	BOOL b = m_bMulti;
	m_bMulti = bMulti;
	if (!m_bMulti) {
		HTREEITEM hItem = GetSelectedItem();
		if (hItem && !IsSelected(hItem))
			hItem = NULL;
		SelectAllIgnore(FALSE, hItem);
		if (hItem)
			SelectItem(hItem);
	}
	return b;
}

/////////////////////////////////////////////////////////////////////////////
// SetItemState
// - replacement to handle TVIS_FOCUSED

BOOL CMultiTree::SetItemState(HTREEITEM hItem, UINT nState, UINT nStateMask)
{
	ASSERT(hItem);

	if (!m_bMulti)
		return CMultiTree_BASE::SetItemState(hItem, nState, nStateMask);

	HTREEITEM hFocus = GetSelectedItem();		//current focus
	BOOL bWasFocus = (hFocus == hItem);
	BOOL bFocusWasSel = hFocus && IsSelected(hFocus);	//selection state of current focus
	BOOL bWasSel = IsSelected(hItem);		//select state of acting item

	UINT nS = nState & ~TVIS_FOCUSED;
	UINT nSM = nStateMask & ~TVIS_FOCUSED;

	if (nStateMask & TVIS_FOCUSED) {
		//wanted to affect focus
		if (nState & TVIS_FOCUSED) {
			//wanted to set focus
			if (!bWasFocus && bFocusWasSel) {
				//because SelectItem would de-select the current 'real' selection
				// (the one with focus), need to make the tree ctrl think there is
				// no 'real' selection but still keep the the old item selected
				//it has to be done before the SelectItem call because
				// otherwise the TVN_SELCHANGING/ED notification handlers
				// wouldn't be able to get the proper list of selected items
				CMultiTree_BASE::SelectItem(NULL);	//will cause notify, but can be taken as focus change
				CMultiTree_BASE::SetItemState(hFocus, TVIS_SELECTED, TVIS_SELECTED);
				UpdateWindow();
			}
			if (!CMultiTree_BASE::SelectItem(hItem))	//set focus (will consequently select, if not already focused)
				return FALSE;
			if (nStateMask & TVIS_SELECTED) {
				//wanted to affect select state
				if (nState & TVIS_SELECTED) {
					//wanted to select, already done if wasn't focused
					if (!bWasFocus || bFocusWasSel) {
						nS &= ~TVIS_SELECTED;
						nSM &= ~TVIS_SELECTED;
					}
				}
				//else wanted to clear, base call will do that
			}
			else {
				//didn't want to affect select state
				if (!bWasSel) {
					//it wasn't previously selected, let base clear (correct)
					nS &= ~TVIS_SELECTED;
					nSM |= TVIS_SELECTED;
				}
				//else was already selected, no harm done
			}
		}
		else {
			//wanted to clear focus
			if (bWasFocus) {
				//it had the focus
				CMultiTree_BASE::SelectItem(NULL);	//clear focus
				if (!(nStateMask & TVIS_SELECTED)) {
					//didn't want to affect select state
					if (bWasSel) {
						//it was selected, so restore
						ASSERT( !(nS & TVIS_SELECTED) );
						ASSERT( !(nSM & TVIS_SELECTED) );
						//set state here, to avoid double-notify
						CMultiTree_BASE::SetItemState(hItem, TVIS_SELECTED, TVIS_SELECTED);
						//let base do other states
					}
				}
				else if (nState & TVIS_SELECTED) {
					//wanted to select (but clear focus)
					if (bWasSel) {
						//if was selected, restore
						CMultiTree_BASE::SetItemState(hItem, TVIS_SELECTED, TVIS_SELECTED);
					}
					//don't want to notify, default did it
					nS &= ~TVIS_SELECTED;
					nSM &= ~TVIS_SELECTED;
				}
			}
		}
	}

	if (!nSM)
		return TRUE;	//no other states to alter

	if (nSM & TVIS_SELECTED) {
		//still need to alter selection state
		NMTREEVIEW nmtv;
		nmtv.hdr.hwndFrom = m_hWnd;
		nmtv.hdr.idFrom = ::GetDlgCtrlID(m_hWnd);
		nmtv.hdr.code = TVN_SELCHANGING;
		nmtv.itemOld.mask = nmtv.itemNew.mask = 0;
		nmtv.itemOld.hItem = nmtv.itemNew.hItem = NULL;
		TVITEM& item = (nS & TVIS_SELECTED) ? nmtv.itemNew : nmtv.itemOld;
		item.mask = TVIF_HANDLE|TVIF_PARAM;
		item.hItem = hItem;
		item.lParam = GetItemData(hItem);
		if (_SendNotify(&nmtv.hdr))
			return FALSE;	//sel-changing stopped
		VERIFY( CMultiTree_BASE::SetItemState(hItem, nS, nSM) );
		nmtv.hdr.code = TVN_SELCHANGED;
		_SendNotify(&nmtv.hdr);
		nS &= ~TVIS_SELECTED;
		nSM &= ~TVIS_SELECTED;
	}
	if (!nSM)
		return TRUE;
	return CMultiTree_BASE::SetItemState(hItem, nS, nSM);
}

UINT CMultiTree::GetItemState(HTREEITEM hItem, UINT nStateMask) const
{
	UINT n = CMultiTree_BASE::GetItemState(hItem, nStateMask & ~TVIS_FOCUSED);
	if (nStateMask & TVIS_FOCUSED)
		if (GetSelectedItem() == hItem)
			n |= TVIS_FOCUSED;
	return n;
}

BOOL CMultiTree::SelectItem(HTREEITEM hItem)
{
	if (m_bMulti) {
		TRACE(_T("Use SetItemState or FocusItem when in multi-select mode\n"));
		ASSERT(FALSE);
	}
	return CMultiTree_BASE::SelectItem(hItem);
}

BOOL CMultiTree::FocusItem(HTREEITEM hItem)
{
	ASSERT(m_bMulti);

	BOOL bRet = FALSE;
	if (hItem)
		bRet = SetItemState(hItem, TVIS_FOCUSED, TVIS_FOCUSED);
	else {
		hItem = CMultiTree_BASE::GetSelectedItem();
		if (hItem)
			bRet = SetItemState(hItem, 0, TVIS_FOCUSED);
	}
	return bRet;
}

/////////////////////////////////////////////////////////////////////////////
// _SendNotify
// - helper to distinguish between default control generated notifications
//   and this classes emulated ones (so can tell if focus or select notify)

BOOL CMultiTree::_SendNotify(LPNMHDR pNMHDR)
{
	ASSERT(::GetParent(m_hWnd));	//never expected this

	BOOL b = m_bEmulated;
	m_bEmulated = TRUE;
	BOOL bRes = ::SendMessage(::GetParent(m_hWnd), WM_NOTIFY, (WPARAM)pNMHDR->idFrom, (LPARAM)pNMHDR);
	m_bEmulated = b;
	return bRes;
}

/////////////////////////////////////////////////////////////////////////////
// Selection Parsing

HTREEITEM CMultiTree::GetFirstSelectedItem() const
{
	HTREEITEM hItem = GetRootItem();
	while (hItem) {
		if (IsSelected(hItem))
			break;
		hItem = GetNextVisibleItem(hItem);
	}
	return hItem;
}

HTREEITEM CMultiTree::GetNextSelectedItem(HTREEITEM hItem) const
{
	hItem = GetNextVisibleItem(hItem);
	while (hItem) {
		if (IsSelected(hItem))
			break;
		hItem = GetNextVisibleItem(hItem);
	}
	return hItem;
}

void CMultiTree::SelectAll(BOOL bSelect /*= TRUE*/)
{
	bSelect = !!bSelect;	//ensure 0 or 1
	UINT nState = bSelect ? TVIS_SELECTED : 0;
	HTREEITEM hItem = GetRootItem();
	while (hItem) {
		if (IsSelected(hItem) != bSelect)
			SetItemState(hItem, nState, TVIS_SELECTED);
		hItem = GetNextVisibleItem(hItem);
	}
}

void CMultiTree::SelectAllIgnore(BOOL bSelect, HTREEITEM hIgnore)
{
	//special case to avoid multiple notifications for
	// the same item
	bSelect = !!bSelect;	//ensure 0 or 1
	UINT nState = bSelect ? TVIS_SELECTED : 0;
	HTREEITEM hItem = GetRootItem();
	while (hItem) {
		if (hItem != hIgnore)
			if (IsSelected(hItem) != bSelect)
				SetItemState(hItem, nState, TVIS_SELECTED);
		hItem = GetNextVisibleItem(hItem);
	}
}

void CMultiTree::SelectRange(HTREEITEM hFirst, HTREEITEM hLast, BOOL bOnly /*= TRUE*/)
{
	//locate (and select) either first or last
	// (so order is arbitrary)
	HTREEITEM hItem = GetRootItem();
	while (hItem) {
		if ((hItem == hFirst) || (hItem == hLast)) {
			if (hFirst != hLast) {
				if (!IsSelected(hItem))
					SetItemState(hItem, TVIS_SELECTED, TVIS_SELECTED);
				hItem = GetNextVisibleItem(hItem);
			}
			break;
		}

		if (bOnly && IsSelected(hItem))
			SetItemState(hItem, 0, TVIS_SELECTED);
		hItem = GetNextVisibleItem(hItem);
	}
	//select rest of range
	while (hItem) {
		if (!IsSelected(hItem))
			SetItemState(hItem, TVIS_SELECTED, TVIS_SELECTED);
		if ((hItem == hFirst) || (hItem == hLast)) {
			hItem = GetNextVisibleItem(hItem);
			break;
		}
		hItem = GetNextVisibleItem(hItem);
	}
	if (!bOnly)
		return;
	while (hItem) {
		if (IsSelected(hItem))
			SetItemState(hItem, 0, TVIS_SELECTED);
		hItem = GetNextVisibleItem(hItem);
	}
}

/////////////////////////////////////////////////////////////////////////////
// OnButtonDown

#define _bShift	(nFlags & MK_SHIFT)
#define _bCtrl	(nFlags & MK_CONTROL)


void CMultiTree::OnLButtonDown(UINT nFlags, CPoint point) 
{
	OnButtonDown(TRUE, nFlags, point);
}

void CMultiTree::OnRButtonDown(UINT nFlags, CPoint point) 
{
	OnButtonDown(FALSE, nFlags, point);
}

void CMultiTree::OnButtonDown(BOOL bLeft, UINT nFlags, CPoint point)
{
	UINT nHF;
	HTREEITEM hItem;

	BOOL bBase = !m_bMulti;
	if (!bBase) {
		hItem = HitTest(point, &nHF);
		if (hItem) {
			//base always handles expanding items
			//(doesn't really mean much to right button, but pass anyway)
			bBase = (nHF & (TVHT_ONITEMBUTTON/*|TVHT_ONITEMINDENT*/));
			if (!bBase && bLeft && (GetStyle() & TVS_CHECKBOXES)) {
				//when the tree has check-boxes, the default handler makes
				// a quick selection of the clicked item, then re-selects
				// the previously selected item - to cause a sel-changed
				// notification.  Fortunately it doesn't affect the multi-
				// selection, so can pass on.
				bBase = (nHF & TVHT_ONITEMSTATEICON);

#ifdef _MST_MULTI_CHECK
				//Use the above define if you want all selected items to
				// be checked the same when any one of them is checked
				// - Interestingly this doesn't happen in the listview control
				//  (LVS_EX_CHECKBOXES)
				if (bBase) {
					//the default selection notification would mess
					// the multi-selection up, so generate the notification
					// manually
					// (anyway, this is smoother than the selection flicker
					//  the default gives)
					NMTREEVIEW nmtv;
#ifdef TVN_CHKCHANGE
					nmtv.hdr.code = TVN_CHKCHANGE;
#else
					nmtv.hdr.code = TVN_SELCHANGED;
#endif
					nmtv.hdr.hwndFrom = m_hWnd;
					nmtv.hdr.idFrom = ::GetDlgCtrlID(m_hWnd);
					nmtv.itemOld.hItem = NULL;
					nmtv.itemNew.mask = TVIF_HANDLE|TVIF_PARAM;

					BOOL bChk = !GetCheck(hItem);
					if (IsSelected(hItem)) {
						HTREEITEM h = GetFirstSelectedItem();
						while (h) {
							if (!GetCheck(h) == bChk) {		//! to ensure 0 or 1
								SetCheck(h, bChk);
#ifdef TVN_CHKCHANGE
								//only send multiple check-change
								// notifications (not sel-changed)
								if (h != hItem) {		//clicked item will be done last
									nmtv.itemNew.hItem = h;
									nmtv.itemNew.lParam = GetItemData(h);
									_SendNotify(&nmtv.hdr);
								}
#endif
							}
							h = GetNextSelectedItem(h);
						}
					}
					else
						SetCheck(hItem, bChk);
					//notify clicked item
					nmtv.itemNew.hItem = hItem;
					nmtv.itemNew.lParam = GetItemData(hItem);
					_SendNotify(&nmtv.hdr);
					return;
				}
#endif

			}
		}
	}
	if (bBase) {
		if (bLeft)
			CMultiTree_BASE::OnLButtonDown(nFlags, point);
		else
			CMultiTree_BASE::OnRButtonDown(nFlags, point);
		return;
	}

	if (!hItem || (nHF & (TVHT_ONITEMRIGHT|TVHT_NOWHERE|TVHT_ONITEMINDENT))) {
		//clicked in space, do rubber-banding
		DoBanding(bLeft, nFlags, point);
		return;
	}

	ASSERT(nHF & (TVHT_ONITEM|TVHT_ONITEMSTATEICON) );	//nothing else left

	DoPreSelection(hItem, bLeft, nFlags);
	DoAction(hItem, bLeft, nFlags, point);
}

void CMultiTree::DoPreSelection(HTREEITEM hItem, BOOL bLeft, UINT nFlags)
{
	if (bLeft) {
		//if shift key down, select immediately
		if (_bShift) {
			if (!m_hSelect)
				m_hSelect = GetSelectedItem();	//focus
			SelectRange(m_hSelect, hItem, !_bCtrl);
			SetItemState(hItem, TVIS_FOCUSED, TVIS_FOCUSED);	//focus changes to last clicked
		}
		else {
			if (!_bCtrl) {
				//if ctrl was down, then the selection is delayed until
				// mouse up, otherwise select the one item
				if (!IsSelected(hItem))
					SelectAllIgnore(FALSE, hItem);
				SetItemState(hItem, TVIS_SELECTED|TVIS_FOCUSED, TVIS_SELECTED|TVIS_FOCUSED);
			}
			m_hSelect = NULL;	//reset when a non-shift operation occurs
		}
		return;
	}

	//right mouse
	if (nFlags & (MK_CONTROL|MK_SHIFT)) {
		if (!_bShift)
			m_hSelect = hItem;
		return;		//do nothing if shift or ctrl
	}
	if (!IsSelected(hItem))
		SelectAllIgnore(FALSE, hItem);
	SetItemState(hItem, TVIS_SELECTED|TVIS_FOCUSED, TVIS_SELECTED|TVIS_FOCUSED);
}

void CMultiTree::DoAction(HTREEITEM hItem, BOOL bLeft, UINT nFlags, CPoint point)
{
	::SetCapture(m_hWnd);
	ASSERT(::GetCapture() == m_hWnd);

	MSG msg;
	UINT nDone = 0;
	CPoint pt;
	CSize sizeDrag(::GetSystemMetrics(SM_CXDRAG), ::GetSystemMetrics(SM_CYDRAG));

	while (!nDone && ::GetMessage(&msg, NULL, 0, 0)) {

		if (::GetCapture() != m_hWnd)
			break;

		switch (msg.message) {
			case WM_MOUSEMOVE:
				pt.x = GET_X_LPARAM(msg.lParam);
				pt.y = GET_Y_LPARAM(msg.lParam);
				if ((abs(pt.x - point.x) > sizeDrag.cx)
								|| ((abs(pt.y - point.y) > sizeDrag.cy)) )
					nDone = 2;
					//because we exit loop, button up will still be dispatched
					// which means WM_CONTEXTMENU will be sent after TVN_BEGINRDRAG
					// - this is the same behaviour as original tree
				break;

			case WM_LBUTTONUP:
			case WM_RBUTTONUP:
				nDone = 1;
				break;

			default:
				::DispatchMessage(&msg);
				break;
		}
	}

	::ReleaseCapture();
	ASSERT(::GetCapture() != m_hWnd);

	//construct tree notification info
	NMTREEVIEW nmtv;
	nmtv.hdr.hwndFrom = m_hWnd;
	nmtv.hdr.idFrom = ::GetDlgCtrlID(m_hWnd);
	nmtv.itemNew.mask = TVIF_HANDLE|TVIF_PARAM;
	nmtv.itemNew.hItem = hItem;
	nmtv.itemNew.lParam = GetItemData(hItem);
	DWORD dwStyle = GetStyle();

	if (nDone == 1) {
		//click
		if (!_bShift && bLeft) {
			UINT nState = TVIS_SELECTED;
			if (_bCtrl)
				nState ^= (GetItemState(hItem, TVIS_SELECTED) & TVIS_SELECTED);
			else
				SelectAllIgnore(FALSE, hItem);
			SetItemState(hItem, TVIS_FOCUSED|nState, TVIS_FOCUSED|TVIS_SELECTED);
		}
		if (::GetFocus() != m_hWnd)
			::SetFocus(m_hWnd);
		nmtv.hdr.code = bLeft ? NM_CLICK : NM_RCLICK;
		_SendNotify(&nmtv.hdr);
	}
	else if (nDone == 2) {
		//drag
		SetItemState(hItem, TVIS_FOCUSED|TVIS_SELECTED, TVIS_FOCUSED|TVIS_SELECTED);
		if (!(dwStyle & TVS_DISABLEDRAGDROP)) {
			nmtv.hdr.code = bLeft ? TVN_BEGINDRAG : TVN_BEGINRDRAG;
			nmtv.ptDrag = point;
			_SendNotify(&nmtv.hdr);
		}
	}
}

void CMultiTree::DoBanding(BOOL bLeft, UINT nFlags, CPoint point)
{
	if (::GetFocus() != m_hWnd)
		::SetFocus(m_hWnd);

	::SetCapture(m_hWnd);

	CTreeItemList list;
	if (nFlags & (MK_SHIFT|MK_CONTROL))
		GetSelectedList(list);

	CClientDC dc(this);
	CRect rectCli;
	GetClientRect(&rectCli);

	MSG msg;
	BOOL bDone = FALSE;
	CPoint pt;
	CSize sizeDrag(::GetSystemMetrics(SM_CXDRAG), ::GetSystemMetrics(SM_CYDRAG));
	BOOL bDrag = FALSE;
	CSize sizeEdge(1, 1);

	UINT nTimer = SetTimer(1, MST_TIMER_PERIOD, NULL);	//for scroll
	CPoint ptScr(GetScrollPos(SB_HORZ), GetScrollPos(SB_VERT));
	CRect rect(0, 0, 0, 0);
	UINT h = 0;
	HTREEITEM hItem = GetRootItem();
	if (hItem) {
		GetItemRect(hItem, &rect, FALSE);
		ptScr.y *= (h = rect.Height());		//this assumes equal height items
	}
	point += ptScr;

	while (!bDone && ::GetMessage(&msg, NULL, 0, 0)) {

		if (::GetCapture() != m_hWnd)
			break;

		switch (msg.message) {
			case WM_TIMER:
				pt = msg.pt;
				ScreenToClient(&pt);
				if (rectCli.PtInRect(pt)) {
					::DispatchMessage(&msg);
					break;
				}
				msg.lParam = MAKELPARAM(pt.x, pt.y);
				//fall through to mousemove

			case WM_MOUSEMOVE:
				pt.x = GET_X_LPARAM(msg.lParam);
				pt.y = GET_Y_LPARAM(msg.lParam);
				if (!bDrag) {
					if ((abs(pt.x - point.x) <= sizeDrag.cx)
							&& ((abs(pt.y - point.y) <= sizeDrag.cy)) )
						break;
					bDrag = TRUE;
					if (!(nFlags & (MK_CONTROL|MK_SHIFT)))
						SelectAll(FALSE);
					UpdateWindow();
					rect.SetRect(point, point);
					dc.DrawDragRect(rect, sizeEdge, NULL, sizeEdge);
				}

				dc.DrawDragRect(rect, sizeEdge, NULL, sizeEdge);	//delete

				if (pt.y < rectCli.top)
					::SendMessage(m_hWnd, WM_VSCROLL, SB_LINEUP, 0);
				else if (pt.y >= rectCli.bottom)
					::SendMessage(m_hWnd, WM_VSCROLL, SB_LINEDOWN, 0);
				if (pt.x < rectCli.left)
					::SendMessage(m_hWnd, WM_HSCROLL, SB_LINELEFT, 0);
				else if (pt.x >= rectCli.right)
					::SendMessage(m_hWnd, WM_HSCROLL, SB_LINERIGHT, 0);

				ptScr = point;
				ptScr.x -= GetScrollPos(SB_HORZ);
				ptScr.y -= GetScrollPos(SB_VERT) * h;
				rect.SetRect(ptScr, pt);
				rect.NormalizeRect();
				UpdateSelectionForRect(rect, nFlags, list);
				dc.DrawDragRect(rect, sizeEdge, NULL, sizeEdge);	//draw
				break;

			case WM_LBUTTONUP:
			case WM_RBUTTONUP:
				bDone = TRUE;
				break;

			case WM_KEYDOWN:
				if (LOWORD(msg.wParam) == VK_ESCAPE) {
					SelectAll(FALSE);
					bDone = TRUE;
					break;
				}
				//dispatch

			default:
				::DispatchMessage(&msg);
				break;
		}
	}
	KillTimer(nTimer);
	::ReleaseCapture();

	if (bDrag)
		dc.DrawDragRect(rect, sizeEdge, NULL, sizeEdge);
	else
		if (!(nFlags & (MK_CONTROL|MK_SHIFT)))
			SelectAll(FALSE);

	//construct notification info
	NMHDR hdr;
	hdr.hwndFrom = m_hWnd;
	hdr.idFrom = ::GetDlgCtrlID(m_hWnd);
	hdr.code = bLeft ? NM_CLICK : NM_RCLICK;
	_SendNotify(&hdr);

	//when banding, make sure we receive WM_CONTEXTMENU
	// every time - which is what the listview ctrl does
	::DispatchMessage(&msg);
}

void CMultiTree::UpdateSelectionForRect(LPCRECT pRect, UINT nFlags, CTreeItemList& list)
{
	CRect rect;
	BOOL bSel;
	POSITION pos;

	HTREEITEM hItem = GetRootItem();
	while (hItem) {
		bSel = IsSelected(hItem);
		GetItemRect(hItem, &rect, m_bBandLabel);
		if (rect.IntersectRect(rect, pRect)) {
			//item in rect
			pos = list.Find(hItem);
			if (!bSel && !pos)
				SetItemState(hItem, TVIS_SELECTED, TVIS_SELECTED);
			else if (_bCtrl && pos)
				SetItemState(hItem, 0, TVIS_SELECTED);
			else if (_bShift && pos)
				list.RemoveAt(pos);		//if shift and in rect, don't lock anymore
		}
		else {
			//item not in rect
			pos = list.Find(hItem);
			if (bSel && !pos)
				SetItemState(hItem, 0, TVIS_SELECTED);
			else if (pos)
				SetItemState(hItem, TVIS_SELECTED, TVIS_SELECTED);
		}
		hItem = GetNextVisibleItem(hItem);
	}
	UpdateWindow();
}

void CMultiTree::OnSetFocus(CWnd* pOldWnd) 
{
	CMultiTree_BASE::OnSetFocus(pOldWnd);
	if (m_bMulti) {
		//'emulated' selected items will remain greyed
		// if application gets covered
		HTREEITEM hItem = GetFirstSelectedItem();
		RECT rect;
		while (hItem) {
			GetItemRect(hItem, &rect, TRUE);
			InvalidateRect(&rect);
			hItem = GetNextSelectedItem(hItem);
		}
	}
}

void CMultiTree::OnKillFocus(CWnd* pNewWnd) 
{
	CMultiTree_BASE::OnKillFocus(pNewWnd);
	if (m_bMulti) {
		//'emulated' selected items may not get
		// refreshed to grey
		HTREEITEM hItem = GetFirstSelectedItem();
		RECT rect;
		while (hItem) {
			GetItemRect(hItem, &rect, TRUE);
			InvalidateRect(&rect);
			hItem = GetNextSelectedItem(hItem);
		}
	}
}

BOOL CMultiTree::OnItemExpanding(NMHDR* pNMHDR, LRESULT* pResult) 
{
	if (!m_bMulti)
		return FALSE;

	LPNMTREEVIEW pNMTreeView = (LPNMTREEVIEW)pNMHDR;
	*pResult = 0;
	if ((pNMTreeView->action == TVE_COLLAPSE) || (pNMTreeView->action == TVE_COLLAPSERESET)) {
		//clear selection of children, it would be confusing otherwise
		// - the notifications can be over-ridden to stop the de-selection
		// if required
		//unfortunately, the parent window can't over-ride this functionality
		// because MFC gives this class first crack.  So if changes are required
		// a derived class will have to be used..
		ASSERT(pNMTreeView->itemNew.hItem);

		//if a descendent item has focus the parent will get selected as a
		// consequence of collapsing the tree, so preserve
		// (if the parent was already selected it will gain focus, but
		//  that's acceptable)
		BOOL bWasSel = IsSelected(pNMTreeView->itemNew.hItem);
		BOOL bWasFocus = SelectChildren(pNMTreeView->itemNew.hItem, FALSE, TRUE);
		if (bWasFocus && !bWasSel)
			CMultiTree_BASE::SelectItem(NULL);	//stop parent from gaining selection
	}

	return FALSE;	//pass to parent
}

BOOL CMultiTree::SelectChildren(HTREEITEM hParent, BOOL bSelect /*= TRUE*/, BOOL bRecurse /*= TRUE*/)
{
	UINT nS = bSelect ? TVIS_SELECTED : 0;

	BOOL bFocusWasInHere = FALSE;

	HTREEITEM hItem = GetNextItem(hParent, TVGN_CHILD);
	while (hItem) {
		UINT nState = GetItemState(hItem, TVIS_SELECTED|TVIS_EXPANDED|TVIS_FOCUSED);
		if ((nState & TVIS_SELECTED) != nS)
			SetItemState(hItem, nS, TVIS_SELECTED);
		bFocusWasInHere |= (nState & TVIS_FOCUSED);
		if (bRecurse && (nState & TVIS_EXPANDED))
			bFocusWasInHere |= SelectChildren(hItem, bSelect, bRecurse);
		hItem = GetNextSiblingItem(hItem);
	}
	return bFocusWasInHere;
}

void CMultiTree::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	if (!m_bMulti) {
		CMultiTree_BASE::OnKeyDown(nChar, nRepCnt, nFlags);
		return;
	}
		
	BOOL bCtrl = (GetKeyState(VK_CONTROL) & 0x8000);
	BOOL bShift = (GetKeyState(VK_SHIFT) & 0x8000);

	BOOL bDir = FALSE;
	HTREEITEM hSel = NULL;
	switch (nChar) {
		case VK_UP:
			bDir = TRUE;
		case VK_DOWN:
			//common
			hSel = GetSelectedItem();
			if (!m_hSelect)
				m_hSelect = hSel;

			if (!bCtrl && !bShift) {
				m_hSelect = NULL;	//reset
				SelectAll(FALSE);
			}
			break;
//		case VK_SPACE:
//			hSel = GetSelectedItem();
//			if (hSel) {
//				BOOL b = IsSelected(hSel);
//				if (bCtrl)
//					SetItemState(hSel, b ? 0 : TVIS_SELECTED, TVIS_SELECTED);
//				else if (!b)
//					SetItemState(hSel, TVIS_SELECTED, TVIS_SELECTED);
//			}
//			return;		//don't call base class (it would start a search on the char)
//TODO: the text search isn't stopped by this ^.  It may be done in the TranslateMessage,
// so would have to trap PreTranslateMessage to avoid it.  If 'space' selection is
// required then need to implement - I'm not bothered.
	}

	CMultiTree_BASE::OnKeyDown(nChar, nRepCnt, nFlags);
	if (!hSel || (!bCtrl && !bShift) )
		return;

	HTREEITEM hNext = bDir ? GetPrevVisibleItem(hSel) : GetNextVisibleItem(hSel);
	if (!hNext)
		hNext = hSel;
	if (bShift)
		SelectRange(m_hSelect, hNext, TRUE);
	else if (bCtrl)
		SetItemState(hNext, TVIS_FOCUSED, TVIS_FOCUSED);
}

void CMultiTree::GetSelectedList(CTreeItemList& list) const
{
	list.RemoveAll();

	HTREEITEM hItem = GetFirstSelectedItem();
	while (hItem) {
		list.AddTail(hItem);
		hItem = GetNextSelectedItem(hItem);
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

cider1

Switzerland Switzerland
No Biography provided

| Advertise | Privacy | Mobile
Web04 | 2.8.140921.1 | Last Updated 2 Jul 2003
Article Copyright 2003 by cider1
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid