www.pudn.com > TreePropSheetEx_Src.zip > TreePropSheetEx.cpp
// 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#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 ( 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 (pHelpInfo) ); return TRUE; } return CTreePropSheetBase::OnHelpInfo(pHelpInfo); } } // namespace TreePropSheet