Click here to Skip to main content
15,894,460 members
Articles / Desktop Programming / MFC

Property Sheet View

Rate me:
Please Sign up or sign in to vote.
4.76/5 (26 votes)
11 Jun 2001Zlib 361.8K   11.3K   105  
A "Property Sheet"-like view class for MFC.
// cdxCDynamicControlsManager.h: interface for the cdxCDynamicControlsManager class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_CDXCDYNAMICCONTROLSMANAGER_H__6517AE13_5D12_11D2_BE4C_000000000000__INCLUDED_)
#define AFX_CDXCDYNAMICCONTROLSMANAGER_H__6517AE13_5D12_11D2_BE4C_000000000000__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#include	"cdxCSizeIconCtrl.h"
typedef cdxCSizeIconCtrl	cdxCSizeCtrl;

//
// cdxCDynamicControlsManager.h : header file
// -----------------------------------------------------------------------
// Author:  Hans B�hler (hans.buehler@student.hu-berlin.de)
//          codex design (http://www-pool.mathematik.hu-berlin.de/~codex
// Version: 1.3
// Release: 3 (Jan 1999 to www.codeguru.com)
// -----------------------------------------------------------------------
// Changes for V1.1:
// - cdxCSizeCtrl is now only a typedef on cdxCSizeIconCtrl which is been
//   but in two extra files (header and impl.) to make it available to
//   the programmer even if you don't use cdxCDynamicControls.
//   The include/impl file for cdxCSizeIconCtrl must be available.
// - GetSizeIconBitmap() is been deleted.
// - ICON_CONTROL_ID has been changed to AFX_IDW_SIZE_BOX
// Changes for V1.2:
// - cdxCDynamicControlsManager::DoOnGetMinMaxInfo() has been modified
//   (thanks to a bug report by Michel Wassink <mww@mitutoyo.nl>):
//   Now, if you don't call SetMaxSize(), the maximum position of a window
//   will not be changed.
//	  BUG: Under W95 and W98, resizing didn't work properly REMOVED.
// Changes for V1.3:
// - FindSzControl() and RemSzControl() have been added.
// Changes for V1.4:
// - RestoreWindowPosition() is been speed up.
// - FixWindowSize() has been debugged
// - RemSzControl() is been made more comfortable.
//   You can now remove controls properly from the dynamic controls manager.
//   Moreover, the embedded ControlPosition class has now some more virtual
//   functions for your own use.
//   For example, you can modify the way a control reacts later now.
// - Enable() / Disable() have been added.
//   Using them you can temporarily disable the automatic repositioning.
// -----------------------------------------------------------------------
// Comments welcome.
//

/*
 * cdxCDynamicControlsManager
 * ==========================
 * Makes any CWnd derived class capable of dynamic control resizing
 * and repositioning.
 * Moreover, it can set a window's max/min tracking size (the size
 * the user can change) and add a nice sizing icon to the windows
 * lower right corner (if the window does not have one - as dialogs).
 *
 * To make any CWnd derived capable of automatically displaying
 * its controls, you embed a member of this class in your window
 * (or you derive your class from both this class and your window
 * base class - that depends on how you want to use the member
 * functions of this class).
 *
 * Then, the following functions must be called
 *
 *		DoInitWindow()			-	Must be called after the window became
 *										valid (i.e. CWnd::m_hWnd is non-zero)
 *										and has its initial (minimum) size.
 *		DoOnSize()				-	by the OnSize() message handler
 *		DoOnGetMinMaxInfo()	-	by the OnGetMinMaxInfo() message handler
 *		DoDestroyWindow()		-	by DestroyWindow().
 *
 * See cdxCSizingDialog.h for an example.
 * 
 * NOTE:
 * Unfortunately, we cannot derive this class from CObject, because
 * those macros DECLARE_xxx are too lame to handle multipile derived
 * classes if both classes have been derived from the same base-class
 * CObject.
 */

class cdxCDynamicControlsManager
{
	//
	// various constants
	//
public:
	enum Mode		// flags for AddSzControl()
	{
		mdNone		=	0,					// does nothing
		mdResize		=	1,					// resize in that dimension
		mdRepos		=	2,					// reposition
		mdRelative	=	3,					// center (size by delta/2 and repos by delta/2)
		md__Last		=	mdRelative
	};

	enum Freedom
	{
		fdNone		=	0,					// might be used but I don't imagine what you want from this ??
		fdHoriz		=	0x01,				// horizantally sizable only
		fdVert		=	0x02,				// vertically sizable only
		fdAll			=	fdHoriz|fdVert	// sizable in all directions
	};

	enum RestoreFlags
	{
		rflg_none			=	0,			// only load window position
		rflg_state			=	0x01,		// make window iconic/zoomed if been before
		rflg_visibility	=	0x02,		// hide/show window as been before
		rflg_all				=	rflg_state|rflg_visibility
	};

	enum
	{
		ICON_CONTROL_ID	=	AFX_IDW_SIZE_BOX
	};

	//
	// a positioning parameter
	//
public:
	class PositionSetup
	{
	public:
		BYTE	m_dX1pcnt,m_dX2pcnt,			// how positioning should work (see docs)
				m_dY1pcnt,m_dY2pcnt;
	public:
		PositionSetup(BYTE dX1pcnt = 0, BYTE dX2pcnt = 0, BYTE dY1pcnt = 0, BYTE dY2pcnt = 0) : m_dX1pcnt(dX1pcnt), m_dX2pcnt(dX2pcnt), m_dY1pcnt(dY1pcnt), m_dY2pcnt(dY2pcnt) {}
		~PositionSetup() {}

		// validity check
		bool IsValid() const { return (m_dX1pcnt <= m_dX2pcnt) && (m_dY1pcnt <= m_dY2pcnt); }

		// transform
		CRect Transform(const CRect & rectOriginal, const CSize & szDelta) const;

		// quick-use
		CRect operator()(const CRect & rectOriginal, const CSize & szDelta) const { return Transform(rectOriginal,szDelta); }
	};

	//
	// an astract handle to a sizeable control that you can
	// use to add further controls to
	// see discussion of AddSzControl()
	//
public:
	class ControlPosition
	{
	protected:
		ControlPosition() {}
	public:
		virtual ~ControlPosition() {}

	public:
		virtual bool IsMember(CWnd & ctrl) const = NULL;
		virtual bool IsUsed() const = NULL;

		virtual void Add(CWnd & ctrl) = NULL;
		virtual bool Rem(CWnd & ctrl) = NULL;

		virtual bool Modify(const CRect & rectOriginal, const PositionSetup & rSetup) = NULL;
		virtual CRect GetCurrentPosition() const = NULL;
		virtual CRect GetOriginalPosition() const = NULL;
		virtual PositionSetup GetPositionSetup() const = NULL; 
	};

	//
	// internal storage class for controls and their
	// original positions and their behaviour settings
	//
private:
	class ControlData : public ControlPosition
	{
		//
		// all controls with the same positioning arguments
		// (used by Add())
		// Note that the window is not need to be already created
		//
	private:
		struct ControlEntry
		{
		private:
			ControlData		& m_rMaster;			// container
			ControlEntry	*m_pNext,*m_pPrev;	// next, prev
			CWnd				& m_rCtrl;				// the control

		public:
			ControlEntry(CWnd & ctrl, ControlData & rMaster);
			virtual ~ControlEntry();

			void Position(int x, int y, int wid, int hi, bool bAll);

			void Add(CWnd & ctrl, int x, int y, int wid, int hi);

			ControlEntry *GetNext() { return m_pNext; }
			CWnd & GetCWnd() { return m_rCtrl; }
			const ControlEntry *GetNext() const { return m_pNext; }
			const CWnd & GetCWnd() const { return m_rCtrl; }

			bool operator==(const CWnd & ctrl) const { return &m_rCtrl == &ctrl; }
		};

		friend struct ControlEntry;

	private:
		cdxCDynamicControlsManager	& m_rMaster;			// the master class
		ControlData						*m_pNext,*m_pPrev;	// a linked list (root in m_rMaster.m_pFirst)

		ControlEntry					*m_pCtrl;				// control link list
		PositionSetup					m_posSetup;
		CRect								m_rectOriginal;		// original position of control(s)

	public:
		ControlData(cdxCDynamicControlsManager & rMaster, CWnd & ctrl, const PositionSetup & rPosSetup);
		virtual ~ControlData();

		virtual bool IsUsed() const { return m_pCtrl != NULL; }

		//
		// access to CWnds
		//

		virtual bool IsMember(CWnd & ctrl) const;
		virtual void Add(CWnd & ctrl) { new ControlEntry(ctrl,*this); }
		virtual bool Rem(CWnd & ctrl);

		//
		// positioning
		//

		virtual bool Modify(const CRect & rectOriginal, const PositionSetup & rSetup);
		virtual CRect GetCurrentPosition() const;
		virtual CRect GetOriginalPosition() const { return CRect(m_rectOriginal); }
		virtual PositionSetup GetPositionSetup() const { return PositionSetup(m_posSetup); }

		//
		// helpers
		//

		ControlData *GetNext() { return m_pNext; }
		const ControlData *GetNext() const { return m_pNext; }

		//
		// events
		//

		void OnSize(const CSize & szDelta);
	};

	//
	// my members
	//

	friend class ControlData;

private:
	ControlData		*m_pFirst;
	CWnd				*m_pWnd;					// Use Init() !!!!!!!!!
	CSize				m_szClientRelative,	// original's window size (client !!) - used in OnSize() to calculate delta size
						m_szMin,					// minimum size (whole window)
						m_szMax;					// maximum (whole window)
	Freedom			m_Freedom;				// what is allowed
	cdxCSizeCtrl	*m_pWndSizeIcon;		// the icon control
	int				m_iDisabledCnt;		// counts Disable() and Enable()
public:
	UINT				m_idSizeIcon;			// ID of the icon control (you can set this to change the default, ICON_CONTROL_ID)

public:
	cdxCDynamicControlsManager() : m_pFirst(NULL), m_pWnd(NULL), m_Freedom(fdAll), m_pWndSizeIcon(NULL), m_idSizeIcon(ICON_CONTROL_ID), m_iDisabledCnt(0) {}
	virtual ~cdxCDynamicControlsManager() { DoDestroyWindow(); }

	//
	// check status
	//

	bool IsReady() const { return (m_pWnd != NULL) && ::IsWindow(m_pWnd->m_hWnd); }

	//
	// get some basics
	//

	const CSize & GetMinSize() const { return m_szMin; }
	const CSize & GetMaxSize() const { return m_szMax; }
	Freedom GetFreedom() const { return m_Freedom; }

	//
	// wanna change some basics ?
	//
	bool SetMinMaxSize(const CSize & szMin, const CSize & szMax, bool bResizeIfNecessary = true);
	bool FixWindowSize();
	void SetFreedom(Freedom fd) { m_Freedom = fd; }
	void HideSizeIcon();
	void ShowSizeIcon();

	//
	// add controls to handle
	//
	ControlPosition *AddSzControl(CWnd & ctrl, Mode modeX, Mode modeY);
	ControlPosition *AddSzXControl(CWnd & ctrl, Mode modeX) { return AddSzControl(ctrl,modeX,mdNone); }
	ControlPosition *AddSzYControl(CWnd & ctrl, Mode modeY) { return AddSzControl(ctrl,mdNone,modeY); }

	ControlPosition *AddSzControlEx(CWnd & ctrl, BYTE dX1pcnt, BYTE dX2pcnt, BYTE dY1pcnt, BYTE dY2pcnt) { return AddSzControlEx(ctrl,PositionSetup(dX1pcnt,dX2pcnt,dY1pcnt,dY2pcnt)); }
	ControlPosition *AddSzXControlEx(CWnd & ctrl, BYTE dX1pcnt, BYTE dX2pcnt) { return AddSzControlEx(ctrl,dX1pcnt,dX2pcnt,0,0); }
	ControlPosition *AddSzYControlEx(CWnd & ctrl, BYTE dY1pcnt, BYTE dY2pcnt) { return AddSzControlEx(ctrl,0,0,dY1pcnt,dY2pcnt); }

	virtual ControlPosition *AddSzControlEx(CWnd & ctrl, const PositionSetup & rSetup);

	//
	// advanced (new to V1.3)
	//
	ControlPosition *FindSzControl(CWnd & ctrl);
	const ControlPosition *FindSzControl(CWnd & ctrl) const;
	bool RemSzControl(CWnd & ctrl, bool bAutoDeleteUnusedControlPos = true);

	//
	// advanced (new to V1,4)
	//
	virtual bool Enable(bool bForce = false) { if(!bForce) { --m_iDisabledCnt; ASSERT(m_iDisabledCnt >= 0); } else m_iDisabledCnt = 0; return m_iDisabledCnt == 0; }
	virtual void Disable() { ++m_iDisabledCnt; }
	virtual bool IsDisabled() const { return m_iDisabledCnt > 0; }

	//
	// these must be called by the appropiate message handlers of the window
	// class you're deriving from
	//
public:
	void DoInitWindow(CWnd & rWnd, Freedom fd = fdAll, bool bSizeIcon = false, const CSize * pBaseClientSize = NULL);
	void DoOnGetMinMaxInfo(MINMAXINFO FAR* lpMMI);
	void DoOnSize(UINT nType, int cx, int cy);
	void DoDestroyWindow();

	//
	// some helpers
	//
	void ReorganizeControls(bool bRedraw = true);
	void ReorganizeControlsAdvanced(const CRect & rectWin, CRect rectClient, bool bRedraw = true);
	bool StretchWindow(const CSize & szDelta) { ASSERT(IsReady()); return StretchWindow(*m_pWnd,szDelta); }
	bool StretchWindow(int iAddPcnt) { ASSERT(IsReady()); return StretchWindow(*m_pWnd,iAddPcnt); }
	CSize GetWindowSize() { ASSERT(IsReady()); return GetWindowSize(*m_pWnd); }
	bool RestoreWindowPosition(LPCTSTR lpszProfile, UINT restoreFlags = rflg_none) { ASSERT(IsReady()); return RestoreWindowPosition(*m_pWnd,lpszProfile,restoreFlags); }
	bool StoreWindowPosition(LPCTSTR lpszProfile) { ASSERT(IsReady()); return StoreWindowPosition(*m_pWnd,lpszProfile); }

	//
	// helpers; static
	//
public:
	static bool StretchWindow(CWnd & rWnd, const CSize & szDelta);
	static bool StretchWindow(CWnd & rWnd, int iAddPcnt);
	static CSize GetWindowSize(CWnd & rWnd);
	static bool RestoreWindowPosition(CWnd & rWnd, LPCTSTR lpszProfile, UINT restoreFlags = rflg_none);
	static bool StoreWindowPosition(CWnd & rWnd, LPCTSTR lpszProfile);

	//
	// some virtuals
	//
protected:
	virtual void OnDeleteControlPosition(ControlPosition & rWillBeDeleted) {}

	//
	// misc
	//
public:
	/* removed */ //static CBitmap & GetSizeIconBitmap(CSize * pSzBmp = NULL);
	static CImageList & GetSizeIconImageList(CSize * pSzBmp = NULL) { if(pSzBmp) *pSzBmp = cdxCSizeIconCtrl::M_ilImage.Size(); return cdxCSizeIconCtrl::M_ilImage; }
};

/*
 * cdxCSizeCtrl
 * ============
 * Is now a typedef to cdxCSizeIconCtrl - see above.
 */

/////////////////////////////////////////////////////////////////////////////
// cdxCDynamicControlsManager::PositionSetup inlines
/////////////////////////////////////////////////////////////////////////////

/*
 * this function transforms a control's original position (rectOriginal) into
 * its new rectangle by taking the the difference between the original window's
 * size (szDelta).
 */

inline CRect cdxCDynamicControlsManager::PositionSetup::Transform(const CRect & rectOriginal, const CSize & szDelta) const
{
	CRect	rectNew;

	rectNew.left	=	rectOriginal.left   + (szDelta.cx * (int)m_dX1pcnt) / 100;
	rectNew.right	=	rectOriginal.right  + (szDelta.cx * (int)m_dX2pcnt) / 100;
	rectNew.top		=	rectOriginal.top    + (szDelta.cy * (int)m_dY1pcnt) / 100;
	rectNew.bottom	=	rectOriginal.bottom + (szDelta.cy * (int)m_dY2pcnt) / 100;

	return rectNew;
}

/////////////////////////////////////////////////////////////////////////////
// cdxCDynamicControlsManager::ControlData::ControlEntry inlines
/////////////////////////////////////////////////////////////////////////////


/*
 * add a control that has the same coordinates as the
 * control embedded in the ControlData object.
 * The coordinates are needed to immediately place the
 * control to the original control's position.
 */

inline void cdxCDynamicControlsManager::ControlData::ControlEntry::Add(CWnd & ctrl, int x, int y, int wid, int hi)
{
	VERIFY( m_pNext = new ControlEntry(ctrl,m_rMaster) );
	m_pNext->Position(x,y,wid,hi,false);
}

/*
 * apply new position to all "ControlEntry" controls
 * we don't change the z-order here !
 */

inline void cdxCDynamicControlsManager::ControlData::ControlEntry::Position(int x, int y, int wid, int hi, bool bAll)
{
	if(::IsWindow(m_rCtrl.m_hWnd))		// those window don't need to exist :)
	{
		VERIFY( m_rCtrl.SetWindowPos(&CWnd::wndBottom,x,y,wid,hi,
						SWP_NOCOPYBITS|SWP_NOOWNERZORDER|
						SWP_NOACTIVATE|SWP_NOZORDER) );
	}
	if(m_pNext && bAll)
		m_pNext->Position(x,y,wid,hi,true);
}

/////////////////////////////////////////////////////////////////////////////
// cdxCDynamicControlsManager::ControlData inlines
/////////////////////////////////////////////////////////////////////////////
/*
 * called by cdxCDynamicControlsManager::ReorganizeControls() if the size of the window has been changed.
 * repositions all controls applied to this ControlData
 */

inline void cdxCDynamicControlsManager::ControlData::OnSize(const CSize & szDelta)
{
	if(m_pCtrl)
	{
		CRect	rectNew	=	m_posSetup(m_rectOriginal,szDelta);
		m_pCtrl->Position(rectNew.left,rectNew.top,rectNew.Width(),rectNew.Height(),true);
	}
}

/////////////////////////////////////////////////////////////////////////////
// cdxCDynamicControlsManager inlines
/////////////////////////////////////////////////////////////////////////////

/*
 * add a control - we leave that work
 * to the embedded ControlData class
 */

inline cdxCDynamicControlsManager::ControlPosition *cdxCDynamicControlsManager::AddSzControlEx(CWnd & ctrl, const PositionSetup & rSetup)
{
	ASSERT(IsReady());			// don't called DoInitWindow() before ?
	ASSERT(rSetup.IsValid());

	ControlData	*si	=	new ControlData(*this, ctrl, rSetup);
	ASSERT(si != NULL);			// if you don't throw exceptions :)
	return si;
}

/*
 * find a control's ControlPosition
 */

inline const cdxCDynamicControlsManager::ControlPosition *cdxCDynamicControlsManager::FindSzControl(CWnd & ctrl) const
{
	ASSERT(::IsWindow(ctrl.m_hWnd));	// will work for exiting windows only !

	for(const ControlData *si = m_pFirst; si; si = si->GetNext())
		if(si->IsMember(ctrl))
			return si;

	return NULL;
}

inline cdxCDynamicControlsManager::ControlPosition *cdxCDynamicControlsManager::FindSzControl(CWnd & ctrl)
{
	ASSERT(::IsWindow(ctrl.m_hWnd));	// will work for exiting windows only !

	for(ControlData *si = m_pFirst; si; si = si->GetNext())
		if(si->IsMember(ctrl))
			return si;

	return NULL;
}

/*
 * delete an entry for a control
 * ctrl									-	the control
 * bAutoDeleteUnusedControlPos	-	if true, and unused ControlPosition (no more
 *												CWnds are bound to it) will be deleted.
 *												Note that you can use OnDeleteControlPosition()
 *												to find out which one will be deleted if any.
 *
 * returns true of the control has been found and deleted
 */

inline bool cdxCDynamicControlsManager::RemSzControl(CWnd & ctrl, bool bAutoDeleteUnusedControlPos)
{
	for(ControlData *si = m_pFirst; si; si = si->GetNext())
		if(si->Rem(ctrl))
		{
			if(!si->IsUsed() && bAutoDeleteUnusedControlPos)
			{
				OnDeleteControlPosition(*si);
				delete si;
			}
			return true;
		}

	return false;
}

/*
 * adding controls by my nice constants
 */

inline cdxCDynamicControlsManager::ControlPosition *cdxCDynamicControlsManager::AddSzControl(CWnd & ctrl, Mode modeX, Mode modeY)
{
	BYTE	dX1pcnt	=	0,
			dX2pcnt	=	0,
			dY1pcnt	=	0,
			dY2pcnt	=	0;

	switch(modeX)
	{
		default	:			ASSERT(false);			// unknown value for modeX
		case	mdNone	:	break;
		case	mdRepos	:	dX1pcnt	=	dX2pcnt	=	100;
								break;
		case	mdResize	:	dX2pcnt	=	100;
								break;
		case	mdRelative:	dX1pcnt	=	dX2pcnt	=	100 / 2;
								break;
	}

	switch(modeY)
	{
		default	:			ASSERT(false);			// unknown value for modeY
		case	mdNone	:	break;
		case	mdRepos	:	dY1pcnt	=	dY2pcnt	=	100;
								break;
		case	mdResize	:	dY2pcnt	=	100;
								break;
		case	mdRelative:	dY1pcnt	=	dY2pcnt	=	100 / 2;
								break;
	}

	return AddSzControlEx(ctrl,dX1pcnt,dX2pcnt,dY1pcnt,dY2pcnt);
}

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

/*
 * Reposition
 */

inline void cdxCDynamicControlsManager::ReorganizeControls(bool bRedraw)
{
	ASSERT(IsReady());

	CRect	clrect;
	m_pWnd->GetClientRect(&clrect);
	CRect	winrect;
	m_pWnd->GetWindowRect(&winrect);
	ReorganizeControlsAdvanced(winrect,clrect,bRedraw);
}

#endif // !defined(AFX_CDXCDYNAMICCONTROLSMANAGER_H__6517AE13_5D12_11D2_BE4C_000000000000__INCLUDED_)

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 zlib/libpng License


Written By
Product Manager PSI Logistics GmbH
Germany Germany
I started programming computers in 1979. After years of management and administration application programming, I started to work mainly in the telecommunication sector in 1986. While working at Cycos AG (formerly known as PP-COM GmbH) from 1992 to 1999 I was involved in the development of core and GUI components of their telematic server MRS for ISDN. After that I worked for a telecommunication company named Infitel International N.V. that produces software for open telecommunication services. After a long stop at Cycos AG, I worked for several other companies like Siemens Enterprise Communications, Axxom Software AG and PSI Logistics GmbH.

If you are curious about my other Computer related activities and previous projects, visit YeaSoft International

Comments and Discussions