Click here to Skip to main content
15,887,746 members
Articles / Desktop Programming / MFC

CTreePropSheetEx – an extended version of CTreePropSheet

Rate me:
Please Sign up or sign in to vote.
4.98/5 (104 votes)
7 Apr 2005CPOL13 min read 423.8K   17.7K   288  
CTreePropSheetEx is an extension of CTreePropSheet offering new features such as resizing, skipping empty pages, and new property frames such as Office 2003 option sheet.
// TreePropSheetBase.cpp
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2004 by Yves Tkaczyk
// (http://www.tkaczyk.net - yves@tkaczyk.net)
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License. 
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// This code is an update of CTreePropSheet written by Sven Wiegand and published 
// on code project at http://www.codetools.com/property/treepropsheet.asp.
//
//  /********************************************************************
//  *
//  * Copyright (c) 2002 Sven Wiegand <forum@sven-wiegand.de>
//  *
//  * You can use this and modify this in any way you want,
//  * BUT LEAVE THIS HEADER INTACT.
//  *
//  * Redistribution is appreciated.
//  *
//  * $Workfile:$
//  * $Revision: 1.4 $
//  * $Modtime:$
//  * $Author: ytkaczyk $
//  *
//  * Revision History:
//  *	$History:$
//  *
//  *********************************************************************/
//
///////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "TreePropSheetBase.h"
#include "PropPageFrameEx.h"
#include "TreePropSheetTreeCtrl.h"
#include "HighColorTab.hpp"
#include "TreePropSheetUtil.hpp"

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



namespace TreePropSheet
{
const DWORD kNoPageAssociated = 0xFFFFFFFF;         //! Value for tree item data when no page is associated in the tree. 

//-------------------------------------------------------------------
// class CTreePropSheetBase
//-------------------------------------------------------------------

BEGIN_MESSAGE_MAP(CTreePropSheetBase, CPropertySheet)
	//{{AFX_MSG_MAP(CTreePropSheetBase)
	ON_WM_DESTROY()
	//}}AFX_MSG_MAP
	ON_MESSAGE(PSM_ADDPAGE, OnAddPage)
	ON_MESSAGE(PSM_REMOVEPAGE, OnRemovePage)
	ON_MESSAGE(PSM_SETCURSEL, OnSetCurSel)
	ON_MESSAGE(PSM_SETCURSELID, OnSetCurSelId)
	ON_MESSAGE(PSM_ISDIALOGMESSAGE, OnIsDialogMessage)
	
	ON_NOTIFY(TVN_SELCHANGINGA,  s_unPageTreeId, OnPageTreeSelChanging)
	ON_NOTIFY(TVN_SELCHANGINGW,  s_unPageTreeId, OnPageTreeSelChanging)
	ON_NOTIFY(TVN_SELCHANGEDA,   s_unPageTreeId, OnPageTreeSelChanged)
	ON_NOTIFY(TVN_SELCHANGEDW,   s_unPageTreeId, OnPageTreeSelChanged)

  ON_REGISTERED_MESSAGE(WMU_ENABLEPAGE, OnEnablePage)
END_MESSAGE_MAP()

IMPLEMENT_DYNAMIC(CTreePropSheetBase, CPropertySheet)

const UINT CTreePropSheetBase::s_unPageTreeId = 0x7EEE;

CTreePropSheetBase::CTreePropSheetBase()
:	CPropertySheet()
{
  Init();
}


CTreePropSheetBase::CTreePropSheetBase(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
:	CPropertySheet(nIDCaption, pParentWnd, iSelectPage)
{
  Init();
}


CTreePropSheetBase::CTreePropSheetBase(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage)
:	CPropertySheet(pszCaption, pParentWnd, iSelectPage)
{
  Init();
}

CTreePropSheetBase::~CTreePropSheetBase()
{
	if( m_Images.GetSafeHandle() )
    m_Images.DeleteImageList();

  //Fix for TN017
  if( m_pwndPageTree )
    delete m_pwndPageTree;
  m_pwndPageTree = NULL;
  
  if( m_pFrame )
    delete m_pFrame;
  m_pFrame = NULL;
}


/////////////////////////////////////////////////////////////////////
// Operations
/////////////////////////////////////////////////////////////////////

BOOL CTreePropSheetBase::SetTreeViewMode(BOOL bTreeViewMode /* = TRUE */, BOOL bPageCaption /* = FALSE */, BOOL bTreeImages /* = FALSE */)
{
	if (IsWindow(m_hWnd))
	{
		// needs to be called, before the window has been created
		ASSERT(FALSE);
		return FALSE;
	}

	m_bTreeViewMode = bTreeViewMode;
	if (m_bTreeViewMode)
	{
		m_bPageCaption = bPageCaption;
		m_bTreeImages = bTreeImages;
	}

	return TRUE;
}


/////////////////////////////////////////////////////////////////////
//
BOOL CTreePropSheetBase::SetTreeWidth(int nWidth)
{
	if (IsWindow(m_hWnd))
	{
		// needs to be called, before the window is created.
		ASSERT(FALSE);
		return FALSE;
	}

	m_nPageTreeWidth = nWidth;

	return TRUE;
}


/////////////////////////////////////////////////////////////////////
//
void CTreePropSheetBase::SetEmptyPageText(LPCTSTR lpszEmptyPageText)
{
	m_strEmptyPageMessage = lpszEmptyPageText;
}


/////////////////////////////////////////////////////////////////////
//
DWORD	CTreePropSheetBase::SetEmptyPageTextFormat(DWORD dwFormat)
{
	DWORD	dwPrevFormat = m_pFrame->GetMsgFormat();
	m_pFrame->SetMsgFormat(dwFormat);
	return dwPrevFormat;
}


/////////////////////////////////////////////////////////////////////
//
BOOL CTreePropSheetBase::SetTreeDefaultImages(CImageList *pImages)
{
	if (pImages->GetImageCount() != 2)
	{
		ASSERT(FALSE);
		return FALSE;
	}

	if (m_DefaultImages.GetSafeHandle())
		m_DefaultImages.DeleteImageList();
	m_DefaultImages.Create(pImages);

	// update, if necessary
	if (IsWindow(m_hWnd))
		RefillPageTree();
	
	return TRUE;
}


/////////////////////////////////////////////////////////////////////
//
BOOL CTreePropSheetBase::SetTreeDefaultImages(UINT unBitmapID, int cx, COLORREF crMask)
{
	if (m_DefaultImages.GetSafeHandle())
		m_DefaultImages.DeleteImageList();
	if (!m_DefaultImages.Create(unBitmapID, cx, 0, crMask))
		return FALSE;

	if (m_DefaultImages.GetImageCount() != 2)
	{
		m_DefaultImages.DeleteImageList();
		return FALSE;
	}

	return TRUE;
}


/////////////////////////////////////////////////////////////////////
//
CTreeCtrl* CTreePropSheetBase::GetPageTreeControl()
{
	return m_pwndPageTree;
}

/////////////////////////////////////////////////////////////////////
//
CPropPageFrame* CTreePropSheetBase::GetFrameControl()
{
  return m_bTreeViewMode?m_pFrame:NULL;
}

/////////////////////////////////////////////////////////////////////
//
bool CTreePropSheetBase::IsTreeViewMode() const
{
  return m_bTreeViewMode?true:false;
}

/////////////////////////////////////////////////////////////////////
//
int CTreePropSheetBase::GetTreeWidth() const
{
  if( m_pwndPageTree->GetSafeHwnd() )
  {
    CRect rectTree;
    m_pwndPageTree->GetWindowRect( &rectTree );
    return rectTree.Width();
  }
  else
  {
    return m_nPageTreeWidth;
  }
}

/////////////////////////////////////////////////////////////////////
//
void CTreePropSheetBase::SetAutoExpandTree(const bool bAutoExpandTree)
{
	m_bAutoExpandTree = bAutoExpandTree;
}

/////////////////////////////////////////////////////////////////////
//
bool CTreePropSheetBase::IsAutoExpandTree() const
{
	return m_bAutoExpandTree;
}

/////////////////////////////////////////////////////////////////////
//
bool CTreePropSheetBase::EnablePage(const CPropertyPage* const pPage,const bool bEnable)
{
	/* - Based on flag value:
       - If disabling, if the current page is the page that is being disabled, 
         activate the next page. If the new page is the initial
         page, the call fails as there is no available new page. If the current
         is not the page being disabled, just disable it. Act accordingly
         and set flag, return previous status.
       - If enabling, just switch the flag.
    - Refresh tree. */
  CPageInformation& pageInformation = m_mapPageInformation[pPage];
  bool bPreviousStatus = pageInformation.IsEnabled();

  if( bEnable )
  {
    pageInformation.SetIsEnabled( true );
  }
  else
  {
    // Active next page if disabling the current page.
    if( GetActivePage() == pPage )
    {
      ActivateNextPage();
      // This is the only active page left, it cannot be disabled.
      if( GetActivePage() == pPage )
      {
        return bPreviousStatus;
      }
    }
    // We are now sure that the active page is not the page being disabled, 
    // we can proceed.
    pageInformation.SetIsEnabled( false );
  }

  // Enable/disable the page if it exists.
  if( pPage->GetSafeHwnd() )
  {
    ::EnableWindow( pPage->GetSafeHwnd(), bEnable?TRUE:FALSE );
  }

  // Refresh the tree control.
  RefreshPageTree();

  return bPreviousStatus;
}

/////////////////////////////////////////////////////////////////////
//
bool CTreePropSheetBase::IsPageEnabled(const CPropertyPage* const pPage) const
{
  /* If the page is in the information map, return the status from the map 
     otherwise returns true (the page has never been disabled. Implementation 
     is such that querying the page status will not any unnecessary add entry 
     into the map.*/
  tMapPageInformation::const_iterator citPage = m_mapPageInformation.find( pPage );
  if( m_mapPageInformation.end() == citPage )
    return true; 

  return citPage->second.IsEnabled();
}

/////////////////////////////////////////////////////////////////////
// public helpers
/////////////////////////////////////////////////////////////////////

BOOL CTreePropSheetBase::SetPageIcon(CPropertyPage *pPage, HICON hIcon)
{
	pPage->m_psp.dwFlags|= PSP_USEHICON;
	pPage->m_psp.hIcon = hIcon;
	return TRUE;
}


/////////////////////////////////////////////////////////////////////
//
BOOL CTreePropSheetBase::SetPageIcon(CPropertyPage *pPage, UINT unIconId)
{
	HICON	hIcon = AfxGetApp()->LoadIcon(unIconId);
	if (!hIcon)
		return FALSE;

	return SetPageIcon(pPage, hIcon);
}


/////////////////////////////////////////////////////////////////////
//
BOOL CTreePropSheetBase::SetPageIcon(CPropertyPage *pPage, CImageList &Images, int nImage)
{
	HICON	hIcon = Images.ExtractIcon(nImage);
	if (!hIcon)
		return FALSE;

	return SetPageIcon(pPage, hIcon);
}


/////////////////////////////////////////////////////////////////////
//
BOOL CTreePropSheetBase::DestroyPageIcon(CPropertyPage *pPage)
{
	if (!pPage || !(pPage->m_psp.dwFlags&PSP_USEHICON) || !pPage->m_psp.hIcon)
		return FALSE;

	DestroyIcon(pPage->m_psp.hIcon);
	pPage->m_psp.dwFlags&= ~PSP_USEHICON;
	pPage->m_psp.hIcon = NULL;

	return TRUE;
}


/////////////////////////////////////////////////////////////////////
// Overridable implementation helpers
/////////////////////////////////////////////////////////////////////

CString CTreePropSheetBase::GenerateEmptyPageMessage(LPCTSTR lpszEmptyPageMessage, LPCTSTR lpszCaption)
{
	CString	strMsg;
	strMsg.Format(lpszEmptyPageMessage, lpszCaption);
	return strMsg;
}


/////////////////////////////////////////////////////////////////////
//
CTreeCtrl* CTreePropSheetBase::CreatePageTreeObject()
{
  // Create the tree control.
  return new CTreePropSheetTreeCtrl;
}

/////////////////////////////////////////////////////////////////////
//
CPropPageFrame* CTreePropSheetBase::CreatePageFrame()
{
  return new CPropPageFrameEx;
}

/////////////////////////////////////////////////////////////////////
//
void CTreePropSheetBase::RefreshTreeItem(HTREEITEM hItem)
{
	ASSERT(hItem);
	if (hItem)
	{
		DWORD dwItemData = m_pwndPageTree->GetItemData( hItem );
    if( kNoPageAssociated != dwItemData )
    {
      // Set font if disabled.
      CPropertyPage* pPage = GetPage( dwItemData );
      ASSERT( pPage );
      if( NULL != pPage )
      {
        // Set text color
        ::SendMessage( m_pwndPageTree->GetSafeHwnd(), WMU_ENABLETREEITEM, (WPARAM)hItem, (LPARAM)( IsPageEnabled( pPage ) ? m_pwndPageTree->GetTextColor():GetSysColor( COLOR_GRAYTEXT ) ) );
      }
    }
	}
}

/////////////////////////////////////////////////////////////////////
// Implementation helpers
/////////////////////////////////////////////////////////////////////

void CTreePropSheetBase::MoveChildWindows(int nDx, int nDy)
{
	CWnd	*pWnd = GetWindow(GW_CHILD);
	while (pWnd)
	{
		CRect	rect;
		pWnd->GetWindowRect(rect);
		rect.OffsetRect(nDx, nDy);
		ScreenToClient(rect);
		pWnd->MoveWindow(rect);

		pWnd = pWnd->GetNextWindow();
	}
}

/////////////////////////////////////////////////////////////////////
//
void CTreePropSheetBase::RefillPageTree()
{
	if (!IsWindow(m_hWnd))
		return;

  // TreePropSheetEx: OnPageTreeSelChanging does not process message.
  TreePropSheet::CIncrementScope RefillingPageTreeContentGuard( m_nRefillingPageTreeContent );
  // TreePropSheetEx: End OnPageTreeSelChanging does not process message.

	m_pwndPageTree->DeleteAllItems();

	CTabCtrl	*pTabCtrl = GetTabControl();
	if (!IsWindow(pTabCtrl->GetSafeHwnd()))
	{
		ASSERT(FALSE);
		return;
	}

	const int	nPageCount = pTabCtrl->GetItemCount();

	// rebuild image list
	if (m_bTreeImages)
	{
		for (int i = m_Images.GetImageCount()-1; i >= 0; --i)
			m_Images.Remove(i);

		// add page images
		CImageList	*pPageImages = pTabCtrl->GetImageList();
		if (pPageImages)
		{
			for (int nImage = 0; nImage < pPageImages->GetImageCount(); ++nImage)
			{
				HICON	hIcon = pPageImages->ExtractIcon(nImage);
				m_Images.Add(hIcon);
				DestroyIcon(hIcon);
			}
		}

		// add default images
		if (m_DefaultImages.GetSafeHandle())
		{	
			HICON	hIcon;

			// add default images
			hIcon = m_DefaultImages.ExtractIcon(0);
			if (hIcon)
			{
				m_Images.Add(hIcon);
				DestroyIcon(hIcon);
			}
			hIcon = m_DefaultImages.ExtractIcon(1);
			{
				m_Images.Add(hIcon);
				DestroyIcon(hIcon);
			}
		}
	}

	// insert tree items
	for (int nPage = 0; nPage < nPageCount; ++nPage)
	{
		// Get title and image of the page
		CString	strPagePath;

		TCITEM	ti;
		ZeroMemory(&ti, sizeof(ti));
		ti.mask = TCIF_TEXT|TCIF_IMAGE;
		ti.cchTextMax = MAX_PATH;
		ti.pszText = strPagePath.GetBuffer(ti.cchTextMax);
		ASSERT(ti.pszText);
		if (!ti.pszText)
			return;

		pTabCtrl->GetItem(nPage, &ti);
		strPagePath.ReleaseBuffer();

		// Create an item in the tree for the page
		HTREEITEM	hItem = CreatePageTreeItem(ti.pszText);
		ASSERT(hItem);
		if (hItem)
		{
			m_pwndPageTree->SetItemData(hItem, nPage);

			// set image
			if (m_bTreeImages)
			{
				int	nImage = ti.iImage;
				if (nImage < 0 || nImage >= m_Images.GetImageCount())
					nImage = m_DefaultImages.GetSafeHandle()? m_Images.GetImageCount()-1 : -1;

				m_pwndPageTree->SetItemImage(hItem, nImage, nImage);
			}

      // Set font if disabled.
      CPropertyPage* pPage = GetPage( nPage );
      ASSERT( pPage );
      if( NULL != pPage && !IsPageEnabled( pPage ) )
      {
        ::SendMessage( m_pwndPageTree->GetSafeHwnd(), WMU_ENABLETREEITEM, (WPARAM)hItem, (LPARAM)GetSysColor(COLOR_GRAYTEXT) );
      }
		}
	}
}

/////////////////////////////////////////////////////////////////////
//
void CTreePropSheetBase::RefreshPageTree(HTREEITEM hItem /* = TVI_ROOT */, const bool bRedraw /* = true */)
{
  // If TVI_ROOT, get the first item.
  if (hItem == TVI_ROOT)
  {
    hItem = m_pwndPageTree->GetNextItem(NULL, TVGN_ROOT);
  }

  while( hItem )
	{
		RefreshTreeItem( hItem );
		if( m_pwndPageTree->ItemHasChildren( hItem ) )
		{
      RefreshPageTree( m_pwndPageTree->GetNextItem(hItem, TVGN_CHILD), false );
		}

		hItem = m_pwndPageTree->GetNextItem(hItem, TVGN_NEXT);
	}

  if( bRedraw )
  {
    m_pwndPageTree->Invalidate( TRUE );
  }
}

/////////////////////////////////////////////////////////////////////
//
HTREEITEM CTreePropSheetBase::CreatePageTreeItem(LPCTSTR lpszPath, HTREEITEM hParent /* = TVI_ROOT */)
{
	CString		strPath(lpszPath);
	CString		strTopMostItem(SplitPageTreePath(strPath));
	
	// Check if an item with the given text does already exist
	HTREEITEM	hItem = NULL;
	HTREEITEM	hChild = m_pwndPageTree->GetChildItem(hParent);
	while (hChild)
	{
		if (m_pwndPageTree->GetItemText(hChild) == strTopMostItem)
		{
			hItem = hChild;
			break;
		}
		hChild = m_pwndPageTree->GetNextItem(hChild, TVGN_NEXT);
	}

	// If item with that text does not already exist, create a new one
	if (!hItem)
	{
		hItem = m_pwndPageTree->InsertItem(strTopMostItem, hParent);
		m_pwndPageTree->SetItemData(hItem, kNoPageAssociated);
		if (!strPath.IsEmpty() && m_bTreeImages && m_DefaultImages.GetSafeHandle())
			// set folder image
			m_pwndPageTree->SetItemImage(hItem, m_Images.GetImageCount()-2, m_Images.GetImageCount()-2);
	}
	if (!hItem)
	{
		ASSERT(FALSE);
		return NULL;
	}

	if (strPath.IsEmpty())
		return hItem;
	else
		return CreatePageTreeItem(strPath, hItem);
}

/////////////////////////////////////////////////////////////////////
//
CString CTreePropSheetBase::SplitPageTreePath(CString &strRest)
{
	int	nSeperatorPos = 0;
	while (TRUE)
	{
		nSeperatorPos = strRest.Find(_T("::"), nSeperatorPos);
		if (nSeperatorPos == -1)
		{
			CString	strItem(strRest);
			strRest.Empty();
			return strItem;
		}
		else if (nSeperatorPos>0)
		{
			// if there is an odd number of backslashes in front of the
			// separator, than do not interpret it as separator
			int	nBackslashCount = 0;
			for (int nPos = nSeperatorPos-1; nPos >= 0 && strRest[nPos]==_T('\\'); --nPos, ++nBackslashCount);
			if (nBackslashCount%2 == 0)
				break;
			else
				++nSeperatorPos;
		}
	}

	CString	strItem(strRest.Left(nSeperatorPos));
	strItem.Replace(_T("\\::"), _T("::"));
	strItem.Replace(_T("\\\\"), _T("\\"));
	strRest = strRest.Mid(nSeperatorPos+2);
	return strItem;
}

/////////////////////////////////////////////////////////////////////
//
BOOL CTreePropSheetBase::KillActiveCurrentPage()
{
	HWND	hCurrentPage = PropSheet_GetCurrentPageHwnd(m_hWnd);
	if (!IsWindow(hCurrentPage))
	{
		ASSERT(FALSE);
		return TRUE;
	}

	// Check if the current page is really active (if page is invisible
	// an virtual empty page is the active one.
	if (!::IsWindowVisible(hCurrentPage))
		return TRUE;

	// Try to deactivate current page
	PSHNOTIFY	pshn;
	pshn.hdr.code = PSN_KILLACTIVE;
	pshn.hdr.hwndFrom = m_hWnd;
	pshn.hdr.idFrom = GetDlgCtrlID();
	pshn.lParam = 0;
	if (::SendMessage(hCurrentPage, WM_NOTIFY, pshn.hdr.idFrom, (LPARAM)&pshn))
		// current page does not allow page change
		return FALSE;

	// Hide the page
	::ShowWindow(hCurrentPage, SW_HIDE);

	return TRUE;
}

/////////////////////////////////////////////////////////////////////
//
HTREEITEM CTreePropSheetBase::GetPageTreeItem(int nPage, HTREEITEM hRoot /* = TVI_ROOT */)
{
	// Special handling for root case
	if (hRoot == TVI_ROOT)
		hRoot = m_pwndPageTree->GetNextItem(NULL, TVGN_ROOT);

	// Check parameters
	if (nPage < 0 || nPage >= GetPageCount())
	{
		ASSERT(FALSE);
		return NULL;
	}

	if (hRoot == NULL)
	{
		ASSERT(FALSE);
		return NULL;
	}

	// we are performing a simple linear search here, because we are
	// expecting only little data
	HTREEITEM	hItem = hRoot;
	while (hItem)
	{
		if ((signed)m_pwndPageTree->GetItemData(hItem) == nPage)
			return hItem;
		if (m_pwndPageTree->ItemHasChildren(hItem))
		{
			HTREEITEM	hResult = GetPageTreeItem(nPage, m_pwndPageTree->GetNextItem(hItem, TVGN_CHILD));
			if (hResult)
				return hResult;
		}

		hItem = m_pwndPageTree->GetNextItem(hItem, TVGN_NEXT);
	}

	// we've found nothing, if we arrive here
	return hItem;
}

/////////////////////////////////////////////////////////////////////
//
BOOL CTreePropSheetBase::SelectPageTreeItem(int nPage)
{
  ASSERT( -1 != nPage );

  HTREEITEM	hItem = GetPageTreeItem(nPage);
	if (!hItem)
		return FALSE;

	return m_pwndPageTree->SelectItem(hItem);
}

/////////////////////////////////////////////////////////////////////
//
BOOL CTreePropSheetBase::SelectCurrentPageTreeItem()
{
	CTabCtrl	*pTab = GetTabControl();
	if (!IsWindow(pTab->GetSafeHwnd()))
		return FALSE;

  // TreePropSheetEx: Fix problem when removing last page from control.
  int nPage = pTab->GetCurSel();
  if( nPage >= 0 && nPage < pTab->GetItemCount() )
    return SelectPageTreeItem( nPage );
  // TreePropSheetEx: End fix problem when removing last page from control.

  return FALSE;
}

/////////////////////////////////////////////////////////////////////
//
void CTreePropSheetBase::UpdateCaption()
{
	HWND			hPage = PropSheet_GetCurrentPageHwnd(GetSafeHwnd());
	BOOL			bRealPage = IsWindow(hPage) && ::IsWindowVisible(hPage);
	HTREEITEM	hItem = m_pwndPageTree->GetSelectedItem();
	if (!hItem)
		return;
	CString		strCaption = m_pwndPageTree->GetItemText(hItem);

	// if empty page, then update empty page message
	if (!bRealPage)
		m_pFrame->SetMsgText(GenerateEmptyPageMessage(m_strEmptyPageMessage, strCaption));

	// if no captions are displayed, cancel here
	if (!m_pFrame->GetShowCaption())
		return;

	// get tab control, to the the images from
	CTabCtrl	*pTabCtrl = GetTabControl();
	if (!IsWindow(pTabCtrl->GetSafeHwnd()))
	{
		ASSERT(FALSE);
		return;
	}

	if (m_bTreeImages)
	{
		// get image from tree
		int	nImage;
		m_pwndPageTree->GetItemImage(hItem, nImage, nImage);
		HICON	hIcon = m_Images.ExtractIcon(nImage);
		m_pFrame->SetCaption(strCaption, hIcon);
		if (hIcon)
			DestroyIcon(hIcon);
	}
	else if (bRealPage)
	{
		// get image from hidden (original) tab provided by the original
		// implementation
		CImageList	*pImages = pTabCtrl->GetImageList();
		if (pImages)
		{
			TCITEM	ti;
			ZeroMemory(&ti, sizeof(ti));
			ti.mask = TCIF_IMAGE;

			HICON	hIcon = NULL;
			if (pTabCtrl->GetItem((int)m_pwndPageTree->GetItemData(hItem), &ti))
				hIcon = pImages->ExtractIcon(ti.iImage);

			m_pFrame->SetCaption(strCaption, hIcon);
			if (hIcon)
				DestroyIcon(hIcon);
		}
		else
			m_pFrame->SetCaption(strCaption);
	}
	else
		m_pFrame->SetCaption(strCaption);
}

/////////////////////////////////////////////////////////////////////
//
void CTreePropSheetBase::ActivatePreviousPage()
{
	if( !IsWindow(m_hWnd) || 0 == GetPageCount() )
		return;

	if (!IsWindow(m_pwndPageTree->GetSafeHwnd()))
	{
		// normal tab property sheet. Simply use page index
		int	nPageIndex = GetActiveIndex();
		if (nPageIndex<0 || nPageIndex>=GetPageCount())
			return;

		int	nPrevIndex = (nPageIndex==0)? GetPageCount()-1 : nPageIndex-1;
		SetActivePage(nPrevIndex);
	}
	else
	{
		HTREEITEM	hItem = m_pwndPageTree->GetSelectedItem();
    ActivatePreviousPage( hItem );
	}
}

/////////////////////////////////////////////////////////////////////
//
void CTreePropSheetBase::ActivatePreviousPage(HTREEITEM hItem)
{
		// property sheet with page tree.
		// we need a more sophisticated handling here, than simply using
		// the page index, because we won't skip empty pages.
		// so we have to walk the page tree
		ASSERT(hItem);
		if (!hItem)
			return;

		HTREEITEM	hPrevItem = m_pwndPageTree->GetPrevSiblingItem(hItem);
		if( hPrevItem )
		{
			while (m_pwndPageTree->ItemHasChildren(hPrevItem))
			{
				hPrevItem = m_pwndPageTree->GetChildItem(hPrevItem);
				while (m_pwndPageTree->GetNextSiblingItem(hPrevItem))
					hPrevItem = m_pwndPageTree->GetNextSiblingItem(hPrevItem);
			}
		}
		else 
			hPrevItem=m_pwndPageTree->GetParentItem(hItem);

		if (!hPrevItem)
		{
			// no prev item, so cycle to the last item
			hPrevItem = m_pwndPageTree->GetRootItem();

			while (TRUE)
			{
				while (m_pwndPageTree->GetNextSiblingItem(hPrevItem))
					hPrevItem = m_pwndPageTree->GetNextSiblingItem(hPrevItem);

				if (m_pwndPageTree->ItemHasChildren(hPrevItem))
					hPrevItem = m_pwndPageTree->GetChildItem(hPrevItem);
				else
					break;
			}
		}

    if (!hPrevItem)
      return;

    // If we skip the empty pages and the tree item does not point to 
    // a property page, call ActivateNextPage again. This is fine since
    // we know there is at least one valid property page in the sheet.
    if( IsTreeItemDisplayable( hPrevItem ) )
			m_pwndPageTree->SelectItem( hPrevItem );
    else
      ActivatePreviousPage( hPrevItem );
}

/////////////////////////////////////////////////////////////////////
//
void CTreePropSheetBase::ActivateNextPage()
{
  // Window must be defined and a least one property page.
	if (!IsWindow(m_hWnd) || 0 == GetPageCount() )
		return;

	if (!IsWindow(m_pwndPageTree->GetSafeHwnd()))
	{
		// normal tab property sheet. Simply use page index
		int	nPageIndex = GetActiveIndex();
		if (nPageIndex<0 || nPageIndex>=GetPageCount())
			return;

		int	nNextIndex = (nPageIndex==GetPageCount()-1)? 0 : nPageIndex+1;
		SetActivePage(nNextIndex);
	}
	else
	{
		HTREEITEM	hItem = m_pwndPageTree->GetSelectedItem();
    ActivateNextPage( hItem );
	}
}

/////////////////////////////////////////////////////////////////////
//
#pragma warning(push)
#pragma warning(disable:4706)

void CTreePropSheetBase::ActivateNextPage(HTREEITEM hItem)
{
		// property sheet with page tree.
		// we need a more sophisticated handling here, than simply using
		// the page index, because we won't skip empty pages.
		// so we have to walk the page tree
		ASSERT(hItem);
		if (!hItem)
			return;

		HTREEITEM	hNextItem = m_pwndPageTree->GetChildItem(hItem);
		if ( hNextItem )
    {
      // Have a child, skip rest of search.
    } 
		else if (hNextItem=m_pwndPageTree->GetNextSiblingItem(hItem))
    {
      // Had not child but has a sibling, skip rest of search.
    }
		else if (m_pwndPageTree->GetParentItem(hItem))
		{
      // No child, no sibling, get the next sibling from the parent.
			while (!hNextItem) 
			{
				hItem = m_pwndPageTree->GetParentItem(hItem);
				if (!hItem)
					break;

				hNextItem	= m_pwndPageTree->GetNextSiblingItem(hItem);
			}
		}

		if (!hNextItem)
			// no next item -- so cycle to the first item
			hNextItem = m_pwndPageTree->GetRootItem();

		if (!hNextItem)
      return;

    // If we skip the empty pages and the tree item does not point to 
    // a property page, call ActivateNextPage again. This is fine since
    // we know there is at least one valid property page in the sheet.
    if( IsTreeItemDisplayable( hNextItem ) )
			m_pwndPageTree->SelectItem( hNextItem );
    else
      ActivateNextPage( hNextItem );
}

#pragma warning(pop)

/////////////////////////////////////////////////////////////////////
//
bool CTreePropSheetBase::IsWizardMode() const
{
  return PSH_WIZARD & m_psh.dwFlags?true:false;
}

//////////////////////////////////////////////////////////////////////
//
int CTreePropSheetBase::GetNextValidPageIndex(HTREEITEM hTreeItem) const
{
	HTREEITEM hNextItem = GetNextValidPageTreeItem( hTreeItem );
  if( NULL == hNextItem )
    return 0;

  return m_pwndPageTree->GetItemData( hNextItem );
}

//////////////////////////////////////////////////////////////////////
//
HTREEITEM CTreePropSheetBase::GetNextItem(HTREEITEM hItem) const
{
  if( m_pwndPageTree->ItemHasChildren(hItem) )
	{
		return m_pwndPageTree->GetNextItem(hItem, TVGN_CHILD);
	}

	return m_pwndPageTree->GetNextItem(hItem, TVGN_NEXT);
}

//////////////////////////////////////////////////////////////////////
//
HTREEITEM CTreePropSheetBase::GetNextValidPageTreeItem(HTREEITEM hTreeItem) const
{
	if (hTreeItem == TVI_ROOT)
		hTreeItem = m_pwndPageTree->GetNextItem(NULL, TVGN_ROOT);

	if (hTreeItem == NULL)
	{
		return NULL;
	}

	HTREEITEM	hItem = GetNextItem( hTreeItem );
	while (hItem)
	{
		if( IsTreeItemDisplayable( hItem ) )
			return hItem;

    if (m_pwndPageTree->ItemHasChildren(hItem))
		{
			HTREEITEM	hResult = GetNextValidPageTreeItem( m_pwndPageTree->GetNextItem(hItem, TVGN_CHILD) );
			if( hResult )
				return hResult;
		}

		hItem = m_pwndPageTree->GetNextItem(hItem, TVGN_NEXT);
	}

	return NULL;
}

//////////////////////////////////////////////////////////////////////
//
bool CTreePropSheetBase::HasValidPropertyPageAssociated(HTREEITEM hItem) const
{
	if( NULL == m_pwndPageTree || NULL == hItem )
    return false;

  return kNoPageAssociated != (int)m_pwndPageTree->GetItemData( hItem );
}

//////////////////////////////////////////////////////////////////////
//
bool CTreePropSheetBase::IsAssociatedPropertyPageEnabled( HTREEITEM hItem ) const
{
	DWORD dwItemData = m_pwndPageTree->GetItemData( hItem );
  if( kNoPageAssociated == dwItemData )
  {
    return true;
  }

  CPropertyPage* pPage = GetPage( dwItemData );
  // ASSERT: No page associated with this item data.
  ASSERT( pPage );
  if( NULL == pPage )
    return true;

  return IsPageEnabled( pPage );
}

//////////////////////////////////////////////////////////////////////
//
bool CTreePropSheetBase::IsItemExpanded(HTREEITEM hItem) const
{
	if( NULL == m_pwndPageTree || NULL == hItem )
    return false;

  return ( TVIS_EXPANDED | TVIS_EXPANDPARTIAL) &
         m_pwndPageTree->GetItemState( hItem, TVIS_EXPANDED | TVIS_EXPANDPARTIAL )?true:false;
}

//////////////////////////////////////////////////////////////////////
//
bool CTreePropSheetBase::IsTreeItemDisplayable(const HTREEITEM hItem) const
{
  // Determine if the provided hItem has a property page associated with it.
  bool bHasValidPropertyPageAssociated = HasValidPropertyPageAssociated( hItem );
	
  bool bDisplayEmpty = bHasValidPropertyPageAssociated || m_bSkipEmptyPages ;
  bool bDisplayDisabled = !bHasValidPropertyPageAssociated || IsAssociatedPropertyPageEnabled( hItem ) || !m_bSkipDisabledPages;
  return bDisplayEmpty && bDisplayDisabled;
}

//////////////////////////////////////////////////////////////////////
//
void CTreePropSheetBase::Init()
{
	m_bTreeViewMode = FALSE;
	m_bPageCaption = FALSE;
	m_bTreeImages = FALSE;
	m_nPageTreeWidth = 150;
  m_nSeparatorWidth = 5;
	m_pwndPageTree = NULL;
	m_pFrame = NULL;
  m_nRefillingPageTreeContent = 0;
  m_bAutoExpandTree = false;
  m_bSkipDisabledPages = true;
  m_bSkipEmptyPages = false;
}

//////////////////////////////////////////////////////////////////////
//
void CTreePropSheetBase::SetSkipEmptyPages(const bool bSkipEmptyPages)
{
	m_bSkipEmptyPages = bSkipEmptyPages;
}

//////////////////////////////////////////////////////////////////////
//
bool CTreePropSheetBase::IsSkippingEmptyPages() const
{
	return m_bSkipEmptyPages;
}

/////////////////////////////////////////////////////////////////////
// Overridings
/////////////////////////////////////////////////////////////////////

BOOL CTreePropSheetBase::OnInitDialog() 
{
	if (m_bTreeViewMode && !IsWizardMode() )
	{
	  // Fix suggested by Przemek Miszczuk 
	  // http://www.codeproject.com/property/TreePropSheetEx.asp?msg=1024928#xx1024928xx
    TreePropSheet::CIncrementScope RefillingPageTreeContentGuard(m_nRefillingPageTreeContent );

		// be sure, there are no stacked tabs, because otherwise the
		// page caption will be to large in tree view mode
		EnableStackedTabs(FALSE);

		// Initialize image list.
		if (m_DefaultImages.GetSafeHandle())
		{
			IMAGEINFO	ii;
			m_DefaultImages.GetImageInfo(0, &ii);
			if (ii.hbmImage) DeleteObject(ii.hbmImage);
			if (ii.hbmMask) DeleteObject(ii.hbmMask);
			m_Images.Create(ii.rcImage.right-ii.rcImage.left, ii.rcImage.bottom-ii.rcImage.top, ILC_COLOR32|ILC_MASK, 0, 1);
		}
		else
			m_Images.Create(16, 16, ILC_COLOR32|ILC_MASK, 0, 1);
	}

	// perform default implementation
	BOOL bResult = CPropertySheet::OnInitDialog();

  // If in wizard mode, stop here.
  if( IsWizardMode() )
    return bResult;

	// Get tab control...
	CTabCtrl	*pTab = GetTabControl();
	if (!IsWindow(pTab->GetSafeHwnd()))
	{
		ASSERT(FALSE);
		return bResult;
	}

  // HighColorTab::UpdateImageList to change the internal image list to 24 bits colors)
  HighColorTab::UpdateImageList( *this );

	// If not in tree mode, stop here.
  if (!m_bTreeViewMode)
		// stop here, if we would like to use tabs
		return bResult;

	// ... and hide it
	pTab->ShowWindow(SW_HIDE);
	pTab->EnableWindow(FALSE);

	// Place another (empty) tab ctrl, to get a frame instead
	CRect	rectFrame;
	pTab->GetWindowRect(rectFrame);
	ScreenToClient(rectFrame);

	m_pFrame = CreatePageFrame();
	if (!m_pFrame)
	{
		ASSERT(FALSE);
		AfxThrowMemoryException();
	}
	m_pFrame->Create(WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS, rectFrame, this, 0xFFFF);
	m_pFrame->ShowCaption(m_bPageCaption);

	// Lets make place for the tree ctrl
	const int	nTreeWidth = m_nPageTreeWidth;

	CRect	rectSheet;
	GetWindowRect(rectSheet);
	rectSheet.right+= nTreeWidth;
	SetWindowPos(NULL, -1, -1, rectSheet.Width(), rectSheet.Height(), SWP_NOZORDER|SWP_NOMOVE);
	CenterWindow();

	MoveChildWindows(nTreeWidth, 0);

	// Lets calculate the rectangle for the tree ctrl
	CRect	rectTree(rectFrame);
	rectTree.right = rectTree.left + nTreeWidth - m_nSeparatorWidth;

	// calculate caption height
	CTabCtrl	wndTabCtrl;
	wndTabCtrl.Create(WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS, rectFrame, this, 0x1234);
	wndTabCtrl.InsertItem(0, _T(""));
	CRect	rectFrameCaption;
	wndTabCtrl.GetItemRect(0, rectFrameCaption);
	wndTabCtrl.DestroyWindow();
	m_pFrame->SetCaptionHeight(rectFrameCaption.Height());

	// if no caption should be displayed, make the window smaller in
	// height
	if (!m_bPageCaption)
	{
		// make frame smaller
		m_pFrame->GetWnd()->GetWindowRect(rectFrame);
		ScreenToClient(rectFrame);
		rectFrame.top+= rectFrameCaption.Height();
		m_pFrame->GetWnd()->MoveWindow(rectFrame);

		// move all child windows up
		MoveChildWindows(0, -rectFrameCaption.Height());

		// modify rectangle for the tree ctrl
		rectTree.bottom-= rectFrameCaption.Height();

		// make us smaller
		CRect	rect;
		GetWindowRect(rect);
		rect.top+= rectFrameCaption.Height()/2;
		rect.bottom-= rectFrameCaption.Height()-rectFrameCaption.Height()/2;
		if (GetParent())
			GetParent()->ScreenToClient(rect);
		MoveWindow(rect);
    CenterWindow();
	}

	// finally create the tree control
	const DWORD	dwTreeStyle = TVS_SHOWSELALWAYS|TVS_TRACKSELECT|TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS;
	m_pwndPageTree = CreatePageTreeObject();
	if (!m_pwndPageTree)
	{
		ASSERT(FALSE);
		AfxThrowMemoryException();
	}
	
	// MFC7-support here (Thanks to Rainer Wollgarten)
  // YT: Cast tree control to CWnd and calls CWnd::CreateEx in all cases (VC 6 and7).
  ((CWnd*)m_pwndPageTree)->CreateEx(
    WS_EX_CLIENTEDGE|WS_EX_NOPARENTNOTIFY, 
    _T("SysTreeView32"), _T("PageTree"), 
    WS_TABSTOP|WS_CHILD|WS_VISIBLE|dwTreeStyle, 
    rectTree, this, s_unPageTreeId);
	
	if (m_bTreeImages)
	{
		m_pwndPageTree->SetImageList(&m_Images, TVSIL_NORMAL);
		m_pwndPageTree->SetImageList(&m_Images, TVSIL_STATE);
	}

  // TreePropSheetEx: Fix refresh problem.
	// Fill the tree ctrl
  {
    TreePropSheet::CWindowRedrawScope WindowRedrawScope( m_pwndPageTree, true );
    // Populate the tree control.
    RefillPageTree();
    // Expand the tree if necessary.
    if( IsAutoExpandTree() )
    {
      ExpandTreeItem( m_pwndPageTree, m_pwndPageTree->GetRootItem(), TVE_EXPAND );
    }
    // Select item for the current page
	  if (pTab->GetCurSel() > -1)
		  SelectPageTreeItem(pTab->GetCurSel());
  }
	return bResult;
}

/////////////////////////////////////////////////////////////////////
//
void CTreePropSheetBase::OnDestroy() 
{
	CPropertySheet::OnDestroy();
	
	// Fix for TN017
  if( m_pwndPageTree && m_pwndPageTree->m_hWnd )
    m_pwndPageTree->DestroyWindow();
  if( m_pFrame && m_pFrame->GetWnd()->m_hWnd )
    m_pFrame->GetWnd()->DestroyWindow();	

  if (m_Images.GetSafeHandle())
		m_Images.DeleteImageList();

	delete m_pwndPageTree;
	m_pwndPageTree = NULL;

	delete m_pFrame;
	m_pFrame = NULL;
}

/////////////////////////////////////////////////////////////////////
//
LRESULT CTreePropSheetBase::OnAddPage(WPARAM wParam, LPARAM lParam)
{
	LRESULT	lResult = DefWindowProc(PSM_ADDPAGE, wParam, lParam);
	if (!m_bTreeViewMode)
		return lResult;

  // TreePropSheetEx: Fix refresh problem.
  {
    TreePropSheet::CWindowRedrawScope WindowRedrawScope( m_pwndPageTree, true );
    RefillPageTree();
	  SelectCurrentPageTreeItem();
  }

	return lResult;
}

/////////////////////////////////////////////////////////////////////
//
LRESULT CTreePropSheetBase::OnRemovePage(WPARAM wParam, LPARAM lParam)
{
	LRESULT	lResult = DefWindowProc(PSM_REMOVEPAGE, wParam, lParam);
	if (!m_bTreeViewMode)
		return lResult;

  // TreePropSheetEx: Fix refresh problem.
  {
    TreePropSheet::CWindowRedrawScope WindowRedrawScope( m_pwndPageTree, true );
    RefillPageTree();
	  SelectCurrentPageTreeItem();
  }

	return lResult;
}

/////////////////////////////////////////////////////////////////////
//
LRESULT CTreePropSheetBase::OnSetCurSel(WPARAM wParam, LPARAM lParam)
{
	LRESULT	lResult = DefWindowProc(PSM_SETCURSEL, wParam, lParam);
	if (!m_bTreeViewMode)
		return lResult;

	SelectCurrentPageTreeItem();
	UpdateCaption();
	return lResult;
}

/////////////////////////////////////////////////////////////////////
//
LRESULT CTreePropSheetBase::OnSetCurSelId(WPARAM wParam, LPARAM lParam)
{
	LRESULT	lResult = DefWindowProc(PSM_SETCURSEL, wParam, lParam);
	if (!m_bTreeViewMode)
		return lResult;

	SelectCurrentPageTreeItem();
	UpdateCaption();
	return lResult;
}

/////////////////////////////////////////////////////////////////////
//
void CTreePropSheetBase::OnPageTreeSelChanging(NMHDR *pNotifyStruct, LRESULT *plResult)
{
  if( m_nRefillingPageTreeContent ) 
    return;

	*plResult = 0;

	NMTREEVIEW	*pTvn = reinterpret_cast<NMTREEVIEW*>(pNotifyStruct);
	int					nPage = m_pwndPageTree->GetItemData(pTvn->itemNew.hItem);
	BOOL				bResult;
  // Is the data item pointing to a valid page?
	if (nPage<0 || (unsigned)nPage>=m_pwndPageTree->GetCount())
	{
    // No, see if we are skipping empty page.
    if( IsTreeItemDisplayable( pTvn->itemNew.hItem ) ) 
    {
      // Skipping empty page, get the next valid page.
      HTREEITEM hNewItem = GetNextValidPageTreeItem( pTvn->itemNew.hItem );
      if( hNewItem )
        m_pwndPageTree->SelectItem( hNewItem );
      bResult = false;
    }
    else
    {
      // Not skipping empty pages, kill the current page so that we can display 
      // the generic message.
      bResult = KillActiveCurrentPage();
    }
  }
	else
  {
    if( IsTreeItemDisplayable( pTvn->itemNew.hItem ) )
    {
      // OK, set the new active page.
      TreePropSheet::CIncrementScope RefillingPageTreeContentGuard( m_nRefillingPageTreeContent );
      bResult = SetActivePage(nPage);
    }
    else
    {
      HTREEITEM hNewItem = GetNextValidPageTreeItem( pTvn->itemNew.hItem );
      if( hNewItem )
        m_pwndPageTree->SelectItem( hNewItem );
      bResult = false;
    }
  }

	if (!bResult)
		// prevent selection to change
		*plResult = TRUE;

	// Set focus to tree control (I guess that's what the user expects)
	m_pwndPageTree->SetFocus();

	return;
}

/////////////////////////////////////////////////////////////////////
//
void CTreePropSheetBase::OnPageTreeSelChanged(NMHDR *pNotifyStruct, LRESULT *plResult)
{
  UNREFERENCED_PARAMETER(pNotifyStruct);

  // Refilling the tree control, ignore selection change.
  if( m_nRefillingPageTreeContent ) 
    return;

  *plResult = 0;

	UpdateCaption();

	return;
}

/////////////////////////////////////////////////////////////////////
//
LRESULT CTreePropSheetBase::OnEnablePage(WPARAM wParam, LPARAM lParam)
{
  // Convert parameters
  CPropertyPage* pPage = reinterpret_cast<CPropertyPage*>( wParam );
  bool bEnable = lParam?true:false;

  // Make sure that this page is inside this sheet.

  // Enable/Disable the page.
  EnablePage( pPage, bEnable );
  return 1L;
}

/////////////////////////////////////////////////////////////////////
//
LRESULT CTreePropSheetBase::OnIsDialogMessage(WPARAM wParam, LPARAM lParam)
{
  MSG *pMsg = reinterpret_cast<MSG*>(lParam);
  ASSERT( pMsg );

  if (pMsg->message==WM_KEYDOWN && GetKeyState(VK_CONTROL)&0x8000)
  {
    if( pMsg->wParam==VK_TAB )
    {
      if (GetKeyState(VK_SHIFT)&0x8000)
      {
        ActivatePreviousPage(); 
      }
      else    
      {
        ActivateNextPage(); 
      }
    }
    else
    {
      if( pMsg->wParam==VK_PRIOR )      /*PageUp*/
      {
        ActivatePreviousPage();
      }
      else
      {
        if( pMsg->wParam==VK_NEXT )     /*PageDown*/
        {
          ActivateNextPage();
        }
      }
    }
    return TRUE; 
  }

	return CPropertySheet::DefWindowProc(PSM_ISDIALOGMESSAGE, wParam, lParam);
}

/////////////////////////////////////////////////////////////////////
//
BOOL CTreePropSheetBase::PreTranslateMessage(MSG* pMsg) 
{
  if( pMsg->hwnd == GetPageTreeControl()->GetSafeHwnd() )
  {
    // If not in skipping empty page mode, keep the standard behavior.
    if( !m_bSkipEmptyPages )
      return CPropertySheet::PreTranslateMessage(pMsg);

    if( pMsg->message == WM_KEYDOWN )
    {
      // Get the current tree item.
      HTREEITEM hCurrentItem = m_pwndPageTree->GetSelectedItem();
      if( NULL == hCurrentItem )
        return TRUE;

      if( pMsg->wParam == VK_UP )
      {
        // Active the previous page according to tree ordering - 
        // skipping empty pages on the way.
        ActivatePreviousPage( hCurrentItem );
   	    return TRUE;
      }
      else if ( pMsg->wParam == VK_DOWN )
      {
        // Active the next page according to tree ordering -
        // skipping empty pages on the way.
        ActivateNextPage( hCurrentItem );
  	    return TRUE;
      }
      else if( pMsg->wParam == VK_LEFT )
      {
        /* Here, we try to mimic the tree keyboard handling by doing 
           one of the two things:the following
           - If the tree item is expanded, collapse it
           - If the tree item is collapse, find a parent item that has a page
             associated with it and select it, collapsing all items along the 
             way. */
        if( IsItemExpanded( hCurrentItem ) )
        {
          // Collapse the item since it is expanded.
          m_pwndPageTree->Expand( hCurrentItem, TVE_COLLAPSE );
        }
        else
        {
          // Already collapsed, search for a candidate for selection.
          HTREEITEM hItem = m_pwndPageTree->GetParentItem( hCurrentItem );
          while( NULL != hItem && !HasValidPropertyPageAssociated( hItem ) )
          {
            // Add item to the stack.
            hItem = m_pwndPageTree->GetParentItem( hItem );
          }
          // If the item points to a valid page, select it and collapse 
          if( NULL != hItem && HasValidPropertyPageAssociated( hItem ) )
          {
            m_pwndPageTree->SelectItem( hItem );
          }
        }
        return TRUE;
      }
      else if( pMsg->wParam == VK_RIGHT )
      {
        /* Here, we try to mimic the tree keyboard handling by doing 
           one of the two things:the following
           - If the tree item is collapsed, expand it
           - If the tree item is expanded, find a child item that has a page
             associated with it and select it, expanding all items along the 
             way. The child has to be a first child in the hierarchy. */
        if( IsItemExpanded( hCurrentItem ) )
        {
          // Already expanded, search for a candidate for selection.
          HTREEITEM hItem = m_pwndPageTree->GetChildItem( hCurrentItem );
          while( NULL != hItem && !HasValidPropertyPageAssociated( hItem ) )
          {
            // Add item to the stack.
            hItem = m_pwndPageTree->GetChildItem( hItem );
          }
          // If the item points to a valid page, select it and collapse 
          if( NULL != hItem && HasValidPropertyPageAssociated( hItem ) )
          {
            m_pwndPageTree->SelectItem( hItem );
          }
        }
        else
        {
          // Expand the item since it is collapsed.
          m_pwndPageTree->Expand( hCurrentItem, TVE_EXPAND );
        }
        return TRUE;
      }
    }
  }
  else if( WM_ENABLE == pMsg->message)
  {
    // Handle WM_ENABLE messages for property pages: Update the property page
    // information map.
    TRACE("");

  }

	return CPropertySheet::PreTranslateMessage(pMsg);
}

} //namespace TreePropSheet

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
President ClearSquare Associates
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions