Click here to Skip to main content
15,885,914 members
Articles / Desktop Programming / MFC

CNetworkTreeCtrl

Rate me:
Please Sign up or sign in to vote.
4.31/5 (6 votes)
16 Oct 2000 130.1K   3.4K   40  
A CWaitingTreeCtrl-derived class to display network resources
// WaitingTreeCtrl.cpp : implementation file
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2000 by Paolo Messina
// (ppescher@yahoo.com)
//
// Free for non-commercial use.
// You may change the code to your needs,
// provided that credits to the original 
// author is given in the modified files.
//  
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "WaitingTreeCtrl.h"

#pragma warning(push)
#pragma warning(disable: 4201)
#include <mmsystem.h>
#pragma warning(pop)

#pragma comment(lib, "winmm.lib")


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

/////////////////////////////////////////////////////////////////////////////
// CWaitingTreeCtrl

CWaitingTreeCtrl::CWaitingTreeCtrl()
{
	m_sWaitMsg = _T("Loading...");
	m_bShowWaitMsg = FALSE;
	m_hIconMsg = NULL;	// default: blank icon
	m_nTimerDelay = 0;	// default: no timer
}

CWaitingTreeCtrl::~CWaitingTreeCtrl()
{
}


BEGIN_MESSAGE_MAP(CWaitingTreeCtrl, CTreeCtrl)
	//{{AFX_MSG_MAP(CWaitingTreeCtrl)
	ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING, OnItemExpanding)
	ON_NOTIFY_REFLECT(TVN_ITEMEXPANDED, OnItemExpanded)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CWaitingTreeCtrl message handlers

void CWaitingTreeCtrl::OnItemExpanding(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

	if (pNMTreeView->action & TVE_EXPAND)
		PreExpandItem(pNMTreeView->itemNew.hItem);

	*pResult = 0;
}

void CWaitingTreeCtrl::OnItemExpanded(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

	if (pNMTreeView->action & TVE_EXPAND)
		ExpandItem(pNMTreeView->itemNew.hItem);

	*pResult = 0;
}

BOOL CWaitingTreeCtrl::PopulateItem(HTREEITEM hParent)
{
	UNREFERENCED_PARAMETER(hParent);

	// must provide an implementation in the derived class
	ASSERT(FALSE);

	return FALSE;
}

void CWaitingTreeCtrl::PreAnimation(HTREEITEM hItemMsg)
{
	UNREFERENCED_PARAMETER(hItemMsg);
}

void CWaitingTreeCtrl::PostAnimation()
{
}

void CWaitingTreeCtrl::DoAnimation(BOOL bTimerEvent, int iMaxSteps, int iStep)
{
	UNREFERENCED_PARAMETER(bTimerEvent);
	UNREFERENCED_PARAMETER(iMaxSteps);
	UNREFERENCED_PARAMETER(iStep);
}

void CWaitingTreeCtrl::SetPopulationCount(int iMaxSubItems, int iFirstSubItem)
{
	m_iItemCount = iMaxSubItems;
	m_iItemIndex = iFirstSubItem;

	SetEvent(m_hRedrawEvent);
}

void CWaitingTreeCtrl::UpdatePopulation(int iSubItems)
{
	m_iItemIndex = iSubItems;

	SetEvent(m_hRedrawEvent);
}

void CWaitingTreeCtrl::IncreasePopulation(int iSubItemsToAdd)
{
	m_iItemIndex += iSubItemsToAdd;

	SetEvent(m_hRedrawEvent);
}

void CWaitingTreeCtrl::SetAnimationDelay(UINT nMilliseconds)
{
	// if greater than zero, periodic DoAnimation() will be called
	m_nTimerDelay = nMilliseconds;
}

DWORD WINAPI CWaitingTreeCtrl::AnimationThreadProc(LPVOID pThis)
{
	CWaitingTreeCtrl* me = (CWaitingTreeCtrl*)pThis;

	HANDLE events[2] = { me->m_hTimerEvent, me->m_hRedrawEvent };
	
	while (!me->m_bAbortAnimation)
	{
		DWORD wait = WaitForMultipleObjects(2, events, FALSE, INFINITE);
		
		if (me->m_bAbortAnimation || wait == WAIT_FAILED)
			break;

		if (wait == WAIT_OBJECT_0)	// timer event
			me->DoAnimation(TRUE, me->m_iItemCount, me->m_iItemIndex);
		else	// redraw event
			me->DoAnimation(FALSE, me->m_iItemCount, me->m_iItemIndex);
	}

	return 0;
}

void CWaitingTreeCtrl::StartAnimation()
{
	// user-defined setup
	PreAnimation(m_hItemMsg);

	// animation can go
	m_bAbortAnimation = FALSE;
	// automatic reset events, signaled
	m_hTimerEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
	m_hRedrawEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
	// start animation thread
	DWORD dwThreadID = 0;
	m_hThread = CreateThread(NULL, 0, AnimationThreadProc, this,
		THREAD_PRIORITY_HIGHEST, &dwThreadID);
	// setup timer, if specified
	if (m_nTimerDelay > 0)
		m_nTimerID = (UINT)timeSetEvent(m_nTimerDelay, 5, (LPTIMECALLBACK)m_hTimerEvent,
			0, TIME_PERIODIC | TIME_CALLBACK_EVENT_SET);
}

void CWaitingTreeCtrl::StopAnimation()
{
	// stop and destroy timer
	timeKillEvent(m_nTimerID);
	// signal thread to terminate
	m_bAbortAnimation = TRUE;
	SetEvent(m_hRedrawEvent);	// make sure it can see the signal
	// wait thread termination
	WaitForSingleObject(m_hThread, INFINITE);
	// clean up
	CloseHandle(m_hTimerEvent);
	CloseHandle(m_hRedrawEvent);
	CloseHandle(m_hThread);

	// user-defined cleanup
	PostAnimation();
}

void CWaitingTreeCtrl::PopulateRoot()
{
	PreExpandItem(TVI_ROOT);
	ExpandItem(TVI_ROOT);
	// force update, don't scroll
	SetRedraw(FALSE);
	SCROLLINFO si;
	GetScrollInfo(SB_HORZ, &si);
	EnsureVisible(GetChildItem(TVI_ROOT));
	SetScrollInfo(SB_HORZ, &si, FALSE);
	SetRedraw();
}

void CWaitingTreeCtrl::PreExpandItem(HTREEITEM hItem)
{
	if (!NeedsChildren(hItem))
	{
		if (WantsRefresh(hItem))
		{
			// delete child items before populating
			DeleteChildren(hItem);
		}
		else
		{
			// doesn't want new items
			m_hItemToPopulate = NULL;
			return;
		}
	}
	// if it wants new child items, go on
	m_hItemToPopulate = hItem;

	// fix redraw when expanded programatically
	UpdateWindow();
	// hide changes until it's expanded
	SetRedraw(FALSE);
	// add wait msg, to allow item expansion
	m_hItemMsg = InsertItem(m_sWaitMsg, m_hItemToPopulate);
	// zero progress
	m_iItemCount = 1;
	m_iItemIndex = 0;
}

void CWaitingTreeCtrl::ExpandItem(HTREEITEM hItem)
{
	if (m_hItemToPopulate == NULL)
		return;	// just expand, doesn't want new items

	ASSERT(hItem == m_hItemToPopulate);	// should never fail!!!

	if (m_bShowWaitMsg)
	{
		// display wait msg now, make sure it's visible
		SetRedraw();
		EnsureVisible(m_hItemMsg);
		UpdateWindow();
	}
	// setup animation thread, call PreAnimation
	StartAnimation();
	// draw icon
	if (m_bShowWaitMsg)
		DrawUserIcon();
	// delay redraw after populating
	SetRedraw(FALSE);
	// del temporary item (wait msg still shown)
	DeleteItem(m_hItemMsg);
	// fill in with sub items
	BOOL bCheckChildren = PopulateItem(hItem);
	// clean up animation thread, call PostAnimation
	StopAnimation();
	// change parent to reflect current children number
	if (hItem != TVI_ROOT)
	{
		TVITEM item;
		item.hItem = hItem;
		item.mask = TVIF_HANDLE | TVIF_CHILDREN;
		item.cChildren = NeedsChildren(hItem) ? 0 : 1;
		if (bCheckChildren)
			SetItem(&item);
		else if (item.cChildren == 0)
			// restore item's plus button if no children inserted
			SetItemState(hItem, 0, TVIS_EXPANDED);
	}
	// redraw now
	SetRedraw();
}

BOOL CWaitingTreeCtrl::WantsRefresh(HTREEITEM hItem)
{
	UNREFERENCED_PARAMETER(hItem);

	// default implementation, no refresh
	return FALSE;
}

BOOL CWaitingTreeCtrl::GetItemImageRect(HTREEITEM hItem, LPRECT pRect)
{
	if (GetImageList(TVSIL_NORMAL) == NULL)
		return FALSE;	// no images

	CRect rc;
	// get item rect
	if (!GetItemRect(hItem, &rc, TRUE))
		return FALSE;

	int cx = GetSystemMetrics(SM_CXSMICON);
	int cy = GetSystemMetrics(SM_CYSMICON);

	// move onto the icon space
	int margin = (rc.Height()-cy)/2;
	rc.OffsetRect(-cx-3 , margin);
	rc.right = rc.left + cx;	// make it square
	rc.bottom = rc.top + cy;	// make it square

	*pRect = rc;
	return TRUE;
}

void CWaitingTreeCtrl::DrawUserIcon()
{
	// draw user defined icon

	CRect rcIcon;
	if (!GetItemImageRect(m_hItemMsg, &rcIcon))
		return;	// no image

	// create background brush with current bg color (take rgb part only)
	HBRUSH hBrush = CreateSolidBrush(GetBkColor() & 0x00FFFFFF);

	CClientDC dc(this);

	if (m_hIconMsg != NULL)
		DrawIconEx(dc.GetSafeHdc(), rcIcon.left, rcIcon.top, m_hIconMsg,
			rcIcon.Width(), rcIcon.Height(), 0, hBrush, DI_NORMAL);
	else
		FillRect(dc.GetSafeHdc(), &rcIcon, hBrush);

	DeleteObject(hBrush);
}

void CWaitingTreeCtrl::SetWaitMessage(LPCTSTR pszText, HICON hIcon)
{
	m_sWaitMsg = pszText;
	m_hIconMsg = hIcon;
}

void CWaitingTreeCtrl::RefreshSubItems(HTREEITEM hParent)
{
	if (hParent != TVI_ROOT && !ItemHasChildren(hParent))
		return;

	SetRedraw(FALSE);
	DeleteChildren(hParent);
	if (hParent == TVI_ROOT)
		PopulateRoot();
	else
	{
		PreExpandItem(hParent);
		ExpandItem(hParent);
	}
	SetRedraw();
}

inline BOOL CWaitingTreeCtrl::NeedsChildren(HTREEITEM hParent)
{
	return (GetChildItem(hParent) == NULL);
}

void CWaitingTreeCtrl::DeleteChildren(HTREEITEM hParent)
{
	HTREEITEM hChild = GetChildItem(hParent);
	HTREEITEM hNext;
	do	// must have at least one child
	{
		hNext = GetNextSiblingItem(hChild);
		DeleteItem(hChild);
		hChild = hNext;
	}
	while (hChild != NULL);
}

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.


Written By
Technical Lead RoboTech srl
Italy Italy
Paolo began programming at the age of 9 with a glorious Olivetti M24 (i8086) and GW-BASIC, then he played a bit with Turbo C, Turbo Pascal and Assembly (using the MS-DOS Debug). Quick BASIC and Visual Basic shortly followed, until he learned C++ in College. He tought himself MFC and Windows programming, along with some DHTML and Javascript.

Always attracted by low-level programming and Assembly, he started to appreciate the joys of templates and STL while working for his Master Thesis. For seven months he was playing with airplanes and automatic control at the Unversity of Illinois at Urbana-Champaign, where he first met QNX and embedded systems.

In his job experience he learned Java to develop user interfaces and graphical editors, and re-discovered the Eclipse IDE that he had used in its early versions with the QNX SDK. He also deepened his knowledge of Linux and embedded systems, microcontrollers firmware and embedded voice recognition, while also practicing electronics design.

He graduated in Computer Engineering (Ingegneria informatica) at the University of Pisa, Italy, in December 2003. Currently working for an electronics and robotics company (www.robotechsrl.com).

He lives in Pisa and in Follonica (GR), Italy.

Comments and Discussions