Click here to Skip to main content
15,894,343 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 425.9K   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.
// TreePropSheetEx.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
//
// Documentation: http://www.codeproject.com/property/treepropsheetex.asp
// CVS tree:      http://sourceforge.net/projects/treepropsheetex
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "TreePropSheetEx.h"
#include "TreePropSheetSplitter.h"
#include "MemDC.h"
#include <stdlib.h>

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

namespace
{
  /*! Initialized size. */
  const CSize kInitializeSize( -1, -1 );
};
namespace TreePropSheet
{

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

IMPLEMENT_DYNCREATE(CTreePropSheetEx, CTreePropSheetBase )

#pragma warning(push)
#pragma warning(4: 4355)

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

CTreePropSheetEx::CTreePropSheetEx(UINT nIDCaption,CWnd* pParentWnd /*=NULL*/,UINT iSelectPage /*=0*/)
 : CTreePropSheetBase( nIDCaption, pParentWnd, iSelectPage )
{
  Init();
}

CTreePropSheetEx::CTreePropSheetEx(LPCTSTR pszCaption,CWnd* pParentWnd /*=NULL*/,UINT iSelectPage /*=0*/)
 : CTreePropSheetBase( pszCaption, pParentWnd, iSelectPage )
{
  Init();
}

#pragma warning(pop)

CTreePropSheetEx::~CTreePropSheetEx()
{
}

//////////////////////////////////////////////////////////////////////
// Properties
//////////////////////////////////////////////////////////////////////

bool CTreePropSheetEx::SetIsResizable(const bool bIsResizable)
{
  if( ::IsWindow( m_hWnd ) )
    return false;

	m_bIsResizable = bIsResizable;
  return true;
}

//////////////////////////////////////////////////////////////////////
//
bool CTreePropSheetEx::IsResizable() const
{
	return m_bIsResizable;
}

//////////////////////////////////////////////////////////////////////
//
void CTreePropSheetEx::SetMinSize(const CSize& sizeMin)
{
  ASSERT( m_bIsResizable );
	m_sizeSheetMinimumSize = sizeMin;

  // If the window exists, update it.
  UpdateSheetMinimumSize();
}

//////////////////////////////////////////////////////////////////////
//
CSize CTreePropSheetEx::GetMinSize() const
{
	return m_sizeSheetMinimumSize;
}

//////////////////////////////////////////////////////////////////////
//
void CTreePropSheetEx::SetMaxSize(const CSize& sizeMax)
{
  ASSERT( m_bIsResizable );
	m_sizeSheetMaximumSize = sizeMax;

  // If the window exists, update it.
  UpdateSheetMaximumSize();
}

//////////////////////////////////////////////////////////////////////
//
CSize CTreePropSheetEx::GetMaxSize() const
{
	return m_sizeSheetMaximumSize;
}

//////////////////////////////////////////////////////////////////////
//
bool CTreePropSheetEx::SetPaneMinimumSize(const int nPaneMinimumSize)
{
  ASSERT( nPaneMinimumSize >= 0 );

  if( ::IsWindow( m_hWnd ) )
    return false;

  m_nTreeMinimumSize = nPaneMinimumSize;
  m_nFrameMinimumSize = nPaneMinimumSize;

  return true;
}

//////////////////////////////////////////////////////////////////////
//
int CTreePropSheetEx::GetPaneMinimumSize() const
{
  if( m_nTreeMinimumSize != m_nFrameMinimumSize )
    return -1;

	return m_nFrameMinimumSize;
}

//////////////////////////////////////////////////////////////////////
//
bool CTreePropSheetEx::SetPaneMinimumSizes(const int nTreeMinimumSize,const int nFrameMinimumSize)
{
  ASSERT( nTreeMinimumSize >= 0 );
  ASSERT( nFrameMinimumSize >= 0 );

  if( ::IsWindow( m_hWnd ) )
    return false;

  m_nTreeMinimumSize = nTreeMinimumSize;
  m_nFrameMinimumSize = nFrameMinimumSize;
  return true;
}

//////////////////////////////////////////////////////////////////////
//
void CTreePropSheetEx::GetPaneMinimumSizes(int& nTreeMinimumSize,int& nFrameMinimumSize) const
{
  nTreeMinimumSize = m_nTreeMinimumSize;
  nFrameMinimumSize = m_nFrameMinimumSize;
}

//////////////////////////////////////////////////////////////////////
//
void CTreePropSheetEx::SetTreeIsResizable(const bool bIsTreeResizable)
{
	m_bIsTreeResizable = bIsTreeResizable;

  // If the splitter exists, update it.
  if( ::IsWindow( m_hWnd ) && NULL != m_pSplitter.get() )
  {
    m_pSplitter->SetAllowUserResizing( m_bIsTreeResizable );
  }
}

//////////////////////////////////////////////////////////////////////
//
bool CTreePropSheetEx::IsTreeResizable() const
{
	return m_bIsTreeResizable;
}

//////////////////////////////////////////////////////////////////////
//
bool CTreePropSheetEx::SetSplitterWidth(const int nSplitterWidth)
{
  ASSERT( m_nSplitterWidth >= 1 );
	if( ::IsWindow( m_hWnd ) ) 
    return false;

  m_nSplitterWidth = nSplitterWidth;
  return true;
}

//////////////////////////////////////////////////////////////////////
//
int CTreePropSheetEx::GetSplitterWidth() const
{
	return m_nSplitterWidth;
}

//////////////////////////////////////////////////////////////////////
//
void CTreePropSheetEx::SetTreeResizingMode(const enTreeSizingMode eSizingMode)
{
  ASSERT( eSizingMode == TSM_Fixed || eSizingMode == TSM_Proportional );

	// Store the tree sizing mode.
  m_eSizingMode = eSizingMode;
  
  // Update the splitter if it exists.
  if( m_pSplitter.get() )
  {
    const bool vert_splitter_frozen[2] = { m_eSizingMode==TSM_Fixed?true:false, false };
    m_pSplitter->SetFrozenPanes( vert_splitter_frozen );
  }
}

//////////////////////////////////////////////////////////////////////
//
enTreeSizingMode CTreePropSheetEx::GetTreeResizingMode() const
{
	return m_eSizingMode;
}

//////////////////////////////////////////////////////////////////////
//
CTreePropSheetEx::tSplitter* CTreePropSheetEx::GetSplitterObject() const
{
	return m_pSplitter.get();
}

//////////////////////////////////////////////////////////////////////
//
CWnd* CTreePropSheetEx::GetSplitterWnd() const
{
	return static_cast<CWnd*>( m_pSplitter.get() );
}

//////////////////////////////////////////////////////////////////////
//
void CTreePropSheetEx::SetContextMenuMode(const TreePropSheet::enContextMenuMode eContextMenuMode)
{
  m_eContextMenuMode = eContextMenuMode;
}

//////////////////////////////////////////////////////////////////////
//
TreePropSheet::enContextMenuMode CTreePropSheetEx::GetContextMenuMode() const
{
	return m_eContextMenuMode; 
}

//////////////////////////////////////////////////////////////////////
//
void CTreePropSheetEx::SetRealTimeSplitter(const bool bRealtime)
{
	m_bIsRealtimeSplitterMode = bRealtime;

  // Update the splitter if it exists.
  if( m_pSplitter.get() )
  {
    m_pSplitter->SetRealtimeUpdate( m_bIsRealtimeSplitterMode );
  }
}

//////////////////////////////////////////////////////////////////////
//
bool CTreePropSheetEx::IsRealTimeSplitter() const
{
	return m_bIsRealtimeSplitterMode;
}

//////////////////////////////////////////////////////////////////////
// Overrides
//////////////////////////////////////////////////////////////////////

BOOL CTreePropSheetEx::SetTreeWidth(int nWidth)
{
  // If the sheet is not resizable and already dispalyed, we cannot set the width. 
  if( !IsSettingWidthValid() )
  {
    ASSERT( FALSE );
    return false;
  }

  if( nWidth < 0 )
  {
    // ASSERT: Tree width cannot be negative. Maximum range is not checked. 
    ASSERT( FALSE );
    return FALSE;
  }

  m_nPageTreeWidth = nWidth;

  return TRUE;
}

//////////////////////////////////////////////////////////////////////
// Overridables
//////////////////////////////////////////////////////////////////////

CTreePropSheetEx::tSplitterPtr CTreePropSheetEx::CreateSplitterObject() const
{
  const int zMinSizes[2] = { m_nTreeMinimumSize, m_nFrameMinimumSize };
  return tSplitterPtr(new tSplitter( 2, SSP_HORZ, zMinSizes, m_nSplitterWidth ) );
}

//////////////////////////////////////////////////////////////////////
//
CTreePropSheetEx::tLayoutManagerPtr CTreePropSheetEx::CreateLayoutManagerObject()
{
  ASSERT( m_bIsResizable );
  return tLayoutManagerPtr( new tLayoutManager( this ) );
}

//////////////////////////////////////////////////////////////////////
//
CTreePropSheetEx::tLayoutManager* CTreePropSheetEx::GetLayoutManagerObject() const
{
  return m_pResizeHook.get();
}

//////////////////////////////////////////////////////////////////////
// Helpers
//////////////////////////////////////////////////////////////////////

void CTreePropSheetEx::Init()
{
  m_psh.dwFlags &= ~( PSH_HASHELP );

  m_bIsResizable = true;
  m_bIsTreeResizable = true;
  m_bIsRealtimeSplitterMode = true;
  m_bSkipEmptyPages = false;
  m_eSizingMode = TSM_Fixed;
  m_eContextMenuMode = TPS_PropertySheetControls;
  m_sizeSheetMinimumSize = kInitializeSize;
  m_sizeSheetMaximumSize = kInitializeSize;
  m_nSplitterWidth = 5;
  m_nTreeMinimumSize = 148;
  m_nFrameMinimumSize = 148;
}

//////////////////////////////////////////////////////////////////////
//
bool CTreePropSheetEx::IsSettingWidthValid() const
{
	return !IsWindow( m_hWnd ) || IsResizable();
}

//////////////////////////////////////////////////////////////////////
//
void CTreePropSheetEx::UpdateSheetMinimumSize()
{
  // If the window exists, update it.
  if( ::IsWindow( m_hWnd ) &&
      IsResizable() &&
      kInitializeSize != m_sizeSheetMinimumSize &&
      NULL != m_pResizeHook.get() )
  {
    // Just make sure that the provided minimum width is
    // wide enough according to minimum pane width. 
    ASSERT( m_sizeSheetMinimumSize.cx >= m_nTreeMinimumSize + m_nFrameMinimumSize + m_nSplitterWidth );
    m_pResizeHook->SetMinSize( CSize( __max( m_sizeSheetMinimumSize.cx, m_nTreeMinimumSize + m_nFrameMinimumSize + m_nSplitterWidth ),
                                      m_sizeSheetMinimumSize.cy ) );
  }
}

//////////////////////////////////////////////////////////////////////
//
void CTreePropSheetEx::UpdateSheetMaximumSize()
{
  // If the window exists, update it.
  if( ::IsWindow( m_hWnd ) &&
      IsResizable() &&
      kInitializeSize != m_sizeSheetMaximumSize &&
      NULL != m_pResizeHook.get() )
  {
    // Just make sure that the provided maximum size is
    // larger than the minimum size.
    ASSERT( m_sizeSheetMinimumSize.cx <= m_sizeSheetMaximumSize.cx );
    ASSERT( m_sizeSheetMinimumSize.cy <= m_sizeSheetMaximumSize.cy );
    m_pResizeHook->SetMaxSize( CSize( __max( m_sizeSheetMinimumSize.cx, m_sizeSheetMaximumSize.cx ),
                                      __max( m_sizeSheetMinimumSize.cy, m_sizeSheetMaximumSize.cy ) ) );
  }
}

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

BEGIN_MESSAGE_MAP(CTreePropSheetEx, CTreePropSheetBase)
	//{{AFX_MSG_MAP(CTreePropSheetEx)
	ON_WM_CREATE()
	ON_WM_CONTEXTMENU()
	ON_WM_DESTROY()
	ON_WM_HELPINFO()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTreePropSheetEx message handlers
//////////////////////////////////////////////////////////////////////

int CTreePropSheetEx::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CTreePropSheetBase::OnCreate(lpCreateStruct) == -1)
		return -1;
	
  // If the sheet is resizable, update the sheet window style to allow resizing.
	if( IsResizable() )
  {
    // Prepare the property sheet to be resizable.
	  // keep client area
	  CRect rect;
	  GetClientRect(&rect);
    // Do not change the window style if it is a child window.
    if( lpCreateStruct->style & WS_POPUP )
    {
      // set resizable style
	    ModifyStyle(DS_MODALFRAME, WS_POPUP | WS_THICKFRAME );
    }
	  // adjust size to reflect new style
	  ::AdjustWindowRectEx(&rect, GetStyle(),::IsMenu(GetMenu()->GetSafeHmenu()), GetExStyle());
	  SetWindowPos(NULL, 0, 0, rect.Width(), rect.Height(),
                 SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOREPOSITION);
  }

	return 0;
}

//////////////////////////////////////////////////////////////////////
//
BOOL CTreePropSheetEx::OnInitDialog() 
{
	CTreePropSheetBase::OnInitDialog();
	
  // Add the panes to the splitter.
  if( IsTreeViewMode() )
  {
    if( IsResizable() )
    {
      // Create the splitter object.
      m_pSplitter = CreateSplitterObject();
      ASSERT( m_pSplitter.get() );

      // Compute the proper position for the splitter control, that is union of tree and frame.
      CRect rectTree, rectTab, rectSplit;
      GetPageTreeControl()->GetWindowRect( &rectTree );
      GetFrameControl()->GetWnd()->GetWindowRect( &rectTab );
      rectSplit.UnionRect( &rectTree, &rectTab );
      ScreenToClient( &rectSplit );

      // Create the splitter window. 
      m_pSplitter->Create( this, rectSplit );

      // Set the pane
      m_pSplitter->SetPane( 0, GetPageTreeControl() );                // Pane 0 is the tree control
      m_pSplitter->SetPane( 1, GetFrameControl()->GetWnd() );         // Pane 1 is the frame
	    const int vert_splitter_sizes[2] = { rectTree.Width(), rectTab.Width() };
      m_pSplitter->SetPaneSizes( vert_splitter_sizes );
	    // Set frozen panes.
      SetTreeResizingMode( m_eSizingMode );
      // Set realtime mode.
      SetRealTimeSplitter( m_bIsRealtimeSplitterMode );
      // Enable/disable resizing in splitter.
      m_pSplitter->SetAllowUserResizing( m_bIsTreeResizable );
    }
  }

	// Initialize the layout library.
  if( IsResizable() )
  {
    m_pResizeHook = CreateLayoutManagerObject();
    ASSERT( m_pResizeHook.get() );
    m_pResizeHook->Initialize();
    
    // Set the minimum size.
    UpdateSheetMinimumSize();

    // Set the maximum size.
    UpdateSheetMaximumSize();
  }
	
	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

//////////////////////////////////////////////////////////////////////
//
void CTreePropSheetEx::OnContextMenu(CWnd* pWnd, CPoint point) 
{
  /*! \internal Based on the context menu mode and the window, the 
                message is either trapped or passed to the parent.*/
  
  // TPS_None: Eat all messages
  if( TPS_None == m_eContextMenuMode )
    return;

  // TPS_All: Delegate everything to the parent (TreePropSheet mode).
  if( TPS_All == m_eContextMenuMode )
    CTreePropSheetBase::OnContextMenu( pWnd, point );

  // TPS_PropertySheetControls: Eat the messages for the tree and the 
  // frame control. The other windows are own by the property sheets 
  // and therefore should be notified.
  ASSERT( TPS_PropertySheetControls == m_eContextMenuMode );
  if( pWnd->GetSafeHwnd() != GetPageTreeControl()->GetSafeHwnd() &&
      GetFrameControl() && pWnd->GetSafeHwnd() != GetFrameControl()->GetWnd()->GetSafeHwnd() )
    CTreePropSheetBase::OnContextMenu( pWnd, point );
}

//////////////////////////////////////////////////////////////////////
//
BOOL CTreePropSheetEx::OnHelpInfo(HELPINFO* pHelpInfo) 
{
	/* Forward help notification to property page in order to keep behavior
     consistent with CpropertyPage. 
     Thanks to  pablo_mag for the suggestion
     http://www.codeproject.com/property/TreePropSheetEx.asp?msg=973632#xx973632xx */
	
  CPropertyPage* page = GetActivePage();
  if( page != NULL )
  {
    page->SendMessage(WM_HELP, 0, reinterpret_cast<LPARAM>(pHelpInfo) );
    return TRUE;
  }

	return CTreePropSheetBase::OnHelpInfo(pHelpInfo);
}

} // 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