// cdxCDynamicControlsManager.cpp: implementation of the cdxCDynamicControlsManager class.
//
//////////////////////////////////////////////////////////////////////
/*
* you should define OEMRESOURCE
* in your project settings (C/C++, General) !
*/
#include "stdafx.h"
#include "cdxCDynamicControlsManager.h"
#include <winuser.h>
#include <afxmt.h>
#ifndef OBM_SIZE
#define OBM_SIZE 32766
#pragma message("*** NOTE: cdxCDynamicControlsManager.cpp: Please define OEMRESOURCE in your project settings !")
// taken from WinresRc.h
// if not used for any reason
#endif
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// Some static variables
/////////////////////////////////////////////////////////////////////////////
#define REGVAL_NOSTATE -1
#define REGVAL_VISIBLE 1
#define REGVAL_HIDDEN 0
#define REGVAL_MAXIMIZED 1
#define REGVAL_ICONIC 0
#define REGVAL_INVALID 0
#define REGVAL_VALID 1
/*
* registry value names
* (for StoreWindowPosition()/RestoreWindowPosition())
*/
static LPCTSTR lpszRegVal_Left = _T("Left"),
lpszRegVal_Right = _T("Right"),
lpszRegVal_Top = _T("Top"),
lpszRegVal_Bottom = _T("Bottom"),
lpszRegVal_Visible = _T("Visibility"),
lpszRegVal_State = _T("State"),
lpszRegVal_Valid = _T("(valid)");
/////////////////////////////////////////////////////////////////////////////
// cdxCDynamicControlsManager::ControlData::ControlEntry
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// construction/destruction
/////////////////////////////////////////////////////////////////////////////
cdxCDynamicControlsManager::ControlData::ControlEntry::ControlEntry(CWnd & ctrl, ControlData & rMaster)
: m_rMaster(rMaster), m_rCtrl(ctrl)
{
if(m_pNext = rMaster.m_pCtrl)
m_pNext->m_pPrev = this;
rMaster.m_pCtrl = this;
m_pPrev = NULL;
// raise total counter
++rMaster.m_rMaster.m_iTotalCnt;
}
cdxCDynamicControlsManager::ControlData::ControlEntry::~ControlEntry()
{
if(m_pPrev)
{
if(m_pPrev->m_pNext = m_pNext)
m_pNext->m_pPrev = m_pPrev;
}
else
{
ASSERT( m_rMaster.m_pCtrl == this );
if(m_rMaster.m_pCtrl = m_pNext)
m_pNext->m_pPrev = NULL;
}
// lower
ASSERT( m_rMaster.m_rMaster.m_iTotalCnt > 0 );
++m_rMaster.m_rMaster.m_iTotalCnt;
}
/////////////////////////////////////////////////////////////////////////////
// cdxCDynamicControlsManager::ControlData
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// construction/destruction
/////////////////////////////////////////////////////////////////////////////
/*
* constructor
* copies all paramaters and gets the controls initial position using
* GetWindowPos().
*
* NOTE that the constructor need ctrl.m_hWnd to exist (in contrast to
* Add()
*/
cdxCDynamicControlsManager::ControlData::ControlData(cdxCDynamicControlsManager & rMaster, CWnd & ctrl, const PositionSetup & rPosSetup)
: m_rMaster(rMaster), m_pCtrl(NULL),
m_pNext(NULL), m_pPrev(NULL),
m_posSetup(rPosSetup)
{
ASSERT(::IsWindow(ctrl.m_hWnd)); // control must have already been created !
ASSERT(rPosSetup.IsValid());
//
// get initial values
//
WINDOWPLACEMENT wpl;
VERIFY( ctrl.GetWindowPlacement(&wpl) );
m_rectOriginal = wpl.rcNormalPosition;
//
// remember control
//
new ControlEntry(ctrl,*this);
ASSERT(m_pCtrl != NULL);
//
// link us to the cdxCDynamicControlsManager's list
//
if(m_pNext = m_rMaster.m_pFirst)
m_pNext->m_pPrev = this;
m_pPrev = NULL;
m_rMaster.m_pFirst = this;
}
/*
* detach from list
* The m_Ctrl deletes all children by itself
*/
cdxCDynamicControlsManager::ControlData::~ControlData()
{
//
// delete all control references
//
while(m_pCtrl)
delete m_pCtrl;
//
// unlink from list
//
if(m_pPrev)
{
if(m_pPrev->m_pNext = m_pNext)
m_pNext->m_pPrev = m_pPrev;
}
else
{
ASSERT(m_rMaster.m_pFirst == this);
if(m_rMaster.m_pFirst = m_pNext)
m_pNext->m_pPrev = NULL;
}
}
/////////////////////////////////////////////////////////////////////////////
// cdxCDynamicControlsManager::ControlData virtuals
/////////////////////////////////////////////////////////////////////////////
/*
* checks whether the CWnd is part of this control data
*/
bool cdxCDynamicControlsManager::ControlData::IsMember(CWnd & ctrl) const
{
for(const ControlEntry *pEntry = m_pCtrl; pEntry; pEntry = pEntry->GetNext())
if(*pEntry == ctrl)
return true;
return false;
}
/*
* removes a CWnd from this chain
*/
bool cdxCDynamicControlsManager::ControlData::Rem(CWnd & ctrl)
{
for(ControlEntry *pEntry = m_pCtrl; pEntry; pEntry = pEntry->GetNext())
if(*pEntry == ctrl)
{
delete pEntry;
return true;
}
return false;
}
/*
* Get current position
*/
CRect cdxCDynamicControlsManager::ControlData::GetCurrentPosition() const
{
if(!IsUsed())
{
ASSERT(false); // all sub-controls have been deleted
return CRect(0,0,0,0);
}
WINDOWPLACEMENT wpl;
VERIFY( m_pCtrl->GetCWnd().GetWindowPlacement(&wpl) );
return CRect(wpl.rcNormalPosition);
}
/*
* modify initial setup
* NOTE: this function does not move the controls.
* You habe to call cdxCDynamicControlsManager::ReorganizeControls past
* using this function
*/
bool cdxCDynamicControlsManager::ControlData::Modify(const CRect & rectOriginal, const PositionSetup & rSetup)
{
if((rectOriginal.left > rectOriginal.right) ||
(rectOriginal.top > rectOriginal.bottom) ||
!rSetup.IsValid())
{
ASSERT(false); // bad function call
return false;
}
m_rectOriginal = rectOriginal;
m_posSetup = rSetup;
return true;
}
/////////////////////////////////////////////////////////////////////////////
// cdxCDynamicControlsManager
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// handling events from CWnd
/////////////////////////////////////////////////////////////////////////////
/*
* this function initializes the following members:
* m_pWnd - the window handle
* m_szCurrent - the current window's size
* m_szMin - the minimum window's size (taken from current size)
* m_szMax - the maximum window's size (set to (0,0) <=> don't change maximum)
* m_wndSizeIcon - the icon (if wanted)
*
* parameters:
* rWnd - the window to supervise
* fd - in which directions can we size the window (does only apply to user-sizing)
* bSizeIcon - do you want a sizable icon ?
* pBaseClientSize- if non-zero, this defines the real (normal) size of the
* window relative to all furher calculations will be made.
* if zero, the current window's size will be taken.
*/
void cdxCDynamicControlsManager::DoInitWindow(CWnd & rWnd, Freedom fd, bool bSizeIcon, const CSize * pBaseClientSize)
{
ASSERT(m_pWnd == NULL); // you MUST NOT call this function twice !
ASSERT(::IsWindow(rWnd.m_hWnd)); // rWnd MUST already exist !!
m_pWnd = &rWnd;
m_Freedom = fd;
//
// get current's window size
//
CRect rect;
m_pWnd->GetWindowRect(&rect);
CRect rectClient;
m_pWnd->GetClientRect(&rectClient);
if(!pBaseClientSize)
{
m_szClientRelative.cx = rectClient.Width();
m_szClientRelative.cy = rectClient.Height();
m_szMin.cx = rect.Width();
m_szMin.cy = rect.Height();
}
else
{
ASSERT((pBaseClientSize->cx > 0) && (pBaseClientSize->cy > 0));
m_szClientRelative = *pBaseClientSize;
m_szMin.cx = m_szClientRelative.cx + (rect.Width() - rectClient.Width());
m_szMin.cy = m_szClientRelative.cy + (rect.Height() - rectClient.Height());
}
m_szMax.cx = 0;
m_szMax.cy = 0;
//
// set up icon if wanted
//
if(bSizeIcon)
{
VERIFY( m_pWndSizeIcon = new cdxCSizeIconCtrl );
VERIFY( m_pWndSizeIcon->Create(m_pWnd,m_idSizeIcon) ); // creates my control; id is SIZE_CONTROL_ID
AddSzControl(*m_pWndSizeIcon,mdRepos,mdRepos);
m_pWndSizeIcon->ShowWindow(SW_SHOW); // finally - show it
}
}
/////////////////////////////////////////////////////////////////////////////
/*
* fill in MINMAXINFO as requested
* Call your CWnd's OnGetMinMaxInfo first !
*
* changed due to a but report by Michel Wassink <mww@mitutoyo.nl>
*/
void cdxCDynamicControlsManager::DoOnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
if(IsReady() && !IsDisabled())
{
lpMMI->ptMinTrackSize.x = m_szMin.cx;
lpMMI->ptMinTrackSize.y = m_szMin.cy;
if(m_Freedom & fdHoriz)
{
if(m_szMax.cx)
lpMMI->ptMaxTrackSize.x = m_szMax.cx;
}
else
lpMMI->ptMaxTrackSize.x = m_szMin.cx;
if(m_Freedom & fdVert)
{
if(m_szMax.cy)
lpMMI->ptMaxTrackSize.y = m_szMax.cy;
}
else
lpMMI->ptMaxTrackSize.y = m_szMin.cy;
}
}
/*
* handle OnSize - we can't rely on cx,cy being client dimensions
* as stated in the documentation ...
*/
void cdxCDynamicControlsManager::DoOnSize(UINT nType, int cx, int cy)
{
if(IsReady() && (nType != SIZE_MINIMIZED) && !IsDisabled())
ReorganizeControls(true);
}
/*
* free all memory
* after having called this function, you can reuse the object
* although I wouldn't recommend to do so :)
*/
void cdxCDynamicControlsManager::DoDestroyWindow()
{
while(m_pFirst)
{
OnDeleteControlPosition(*m_pFirst);
delete m_pFirst;
}
if(m_pWndSizeIcon)
{
if(::IsWindow(m_pWndSizeIcon->m_hWnd))
m_pWndSizeIcon->DestroyWindow();
delete m_pWndSizeIcon;
m_pWndSizeIcon = NULL;
}
m_pWnd = NULL;
}
/////////////////////////////////////////////////////////////////////////////
// control positioning
/////////////////////////////////////////////////////////////////////////////
/*
* Reposition (without current rectangle size)
*
* rectWin - window rectangle (including border)
* rectClient - window rectangle (client area)
* Note that since release 6, rectClient.left and .top might be > zero
* (for scrolling)
* bRedraw - invalidate & update window
*/
void cdxCDynamicControlsManager::ReorganizeControlsAdvanced(const CRect & rectWin, CRect rectClient, bool bRedraw)
{
ASSERT(IsReady());
if(!GetTotalChildCnt())
return;
//
// we won't go smaller with the whole window than
// m_szMin
//
if(rectWin.Width() < m_szMin.cx)
rectClient.right += (m_szMin.cx - rectWin.Width());
if(rectWin.Height() < m_szMin.cy)
rectClient.bottom += (m_szMin.cy - rectWin.Height());
//
// we new replace all controls
//
CSize szDelta;
szDelta.cx = rectClient.Width() - m_szClientRelative.cx;
szDelta.cy = rectClient.Height() - m_szClientRelative.cy;
CPoint pntOffset = rectClient.TopLeft();
// newly added code by Rodger Bernstein
AFX_SIZEPARENTPARAMS layout;
ControlData *sz;
bool bManual = true;
if(!( layout.hDWP = ::BeginDeferWindowPos(GetTotalChildCnt()) ))
{
TRACE(_T("*** ERROR[cdxCDynamicControlsManager::ReorganizeControlsAdvanced()]: BeginDeferWindowPos() failed.\n"));
}
else
{
for(sz = m_pFirst; sz; sz = sz->GetNext())
sz->OnSize(szDelta, &layout, &pntOffset);
if(!::EndDeferWindowPos(layout.hDWP))
{
TRACE(_T("*** ERROR[cdxCDynamicControlsManager::ReorganizeControlsAdvanced()]: EndDeferWindowPos() failed.\n"));
}
else
bManual = false;
}
if(bManual)
for(sz = m_pFirst; sz; sz = sz->GetNext())
sz->OnSize(szDelta, NULL);
if(bRedraw && m_pWnd->IsWindowVisible())
{
m_pWnd->RedrawWindow(NULL, NULL, RDW_UPDATENOW | RDW_NOERASE);
}
}
/////////////////////////////////////////////////////////////////////////////
// misc
/////////////////////////////////////////////////////////////////////////////
/*
* change minimum and maximum height of window.
*
* szMin - new minimum size (use GetMinSize() to leave it as being before)
* szMax - new maximum size ( " GetMaxSize() ")
* Set to CSize(0,0) if you don't want a maximum size.
* bResizeIfNecessary - call FixWindowSize() past calculating new sizes.
*
* returns false if szMin and szMax are illegal (e.g. szMin > szMax)
*/
bool cdxCDynamicControlsManager::SetMinMaxSize(const CSize & szMin, const CSize & szMax, bool bResizeIfNecessary)
{
ASSERT(IsReady()); // DoInitWindow() not called ?
if((szMax.cx && (szMin.cx > szMax.cx)) ||
(szMax.cy && (szMin.cy > szMax.cy)))
{
return false;
}
m_szMin = szMin;
m_szMax = szMax;
if(bResizeIfNecessary)
FixWindowSize();
return true;
}
/*
* this function ensure that the window's size is between m_szMin and m_szMax.
* returns true if window size has been changed
*/
bool cdxCDynamicControlsManager::FixWindowSize()
{
ASSERT(IsReady()); // use DoInitWindow() first !
CSize szCurrent = GetWindowSize(*m_pWnd),
szDelta;
if(m_szMax.cx && (szCurrent.cx > m_szMax.cx))
szDelta.cx = m_szMax.cx - szCurrent.cx; // is negative
else
if(szCurrent.cx < m_szMin.cx)
szDelta.cx = m_szMin.cx - szCurrent.cx; // is positive
else
szDelta.cx = 0;
if(m_szMax.cy && (szCurrent.cy > m_szMax.cy))
szDelta.cy = m_szMax.cy - szCurrent.cy; // is negative
else
if(szCurrent.cy < m_szMin.cy)
szDelta.cy = m_szMin.cy - szCurrent.cy; // is positive
else
szDelta.cy = 0;
if(!szDelta.cx && !szDelta.cy)
return false; // nothing to do
StretchWindow(*m_pWnd,szDelta);
return true;
}
/////////////////////////////////////////////////////////////////////////////
/*
* hide and show icon
*/
void cdxCDynamicControlsManager::HideSizeIcon()
{
if(m_pWndSizeIcon && ::IsWindow(m_pWndSizeIcon->m_hWnd))
m_pWndSizeIcon->ShowWindow(SW_HIDE);
}
void cdxCDynamicControlsManager::ShowSizeIcon()
{
if(m_pWndSizeIcon && ::IsWindow(m_pWndSizeIcon->m_hWnd))
m_pWndSizeIcon->ShowWindow(SW_SHOW);
}
/////////////////////////////////////////////////////////////////////////////
// static functions: window sizing
/////////////////////////////////////////////////////////////////////////////
/*
* stretches the window by szDelta (i.e. if szDelta is 100, the window is enlarged by 100 pixels)
* stretching means that the center point of the window remains
*
* returns false if the window would be smaller than (1,1)
*
* NOTE: this function does NOT care of the min/max dimensions of a window
* Use MoveWindow() if you need to take care of it.
*
* STATIC
*/
bool cdxCDynamicControlsManager::StretchWindow(CWnd & rWnd, const CSize & szDelta)
{
ASSERT(::IsWindow(rWnd.m_hWnd));
WINDOWPLACEMENT wpl;
rWnd.GetWindowPlacement(&wpl);
wpl.rcNormalPosition.left -= szDelta.cx / 2;
wpl.rcNormalPosition.right += (szDelta.cx + 1) / 2;
wpl.rcNormalPosition.top -= szDelta.cy / 2;
wpl.rcNormalPosition.bottom += (szDelta.cy + 1) / 2;
// wpl.flags = SW_SHOWNA|SW_SHOWNOACTIVATE;
if((wpl.rcNormalPosition.left >= wpl.rcNormalPosition.right) ||
(wpl.rcNormalPosition.top >= wpl.rcNormalPosition.bottom))
return false;
VERIFY( rWnd.SetWindowPos( NULL,
wpl.rcNormalPosition.left,
wpl.rcNormalPosition.top,
wpl.rcNormalPosition.right - wpl.rcNormalPosition.left,
wpl.rcNormalPosition.bottom - wpl.rcNormalPosition.top,
SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER) );
return true;
}
/*
* stretch window by a percent value
* the algorithm calculates the new size for both dimensions by:
*
* newWid = oldWid + (oldWid * iAddPcnt) / 100
*
* NOTE: iAddPcnt may even be nagtive, but it MUST be greater than -100.
* NOTE: this function does NOT care of the min/max dimensions of a window
*
* The function will return false if the new size would be empty.
*
* STATIC
*/
bool cdxCDynamicControlsManager::StretchWindow(CWnd & rWnd, int iAddPcnt)
{
ASSERT(::IsWindow(rWnd.m_hWnd));
CSize szDelta = GetWindowSize(rWnd);
szDelta.cx = (szDelta.cx * iAddPcnt) / 100;
szDelta.cy = (szDelta.cy * iAddPcnt) / 100;
return StretchWindow(rWnd,szDelta);
}
/*
* get current window's size
*
* STATIC
*/
CSize cdxCDynamicControlsManager::GetWindowSize(CWnd & rWnd)
{
ASSERT(::IsWindow(rWnd.m_hWnd));
CRect rect;
rWnd.GetWindowRect(&rect);
return CSize(rect.Width(),rect.Height());
}
/////////////////////////////////////////////////////////////////////////////
// static functions: window & registry
/////////////////////////////////////////////////////////////////////////////
/*
* stores a window's position and visiblity to the registry.
*
* return false if any error occured
*
* STATIC
*/
bool cdxCDynamicControlsManager::StoreWindowPosition(CWnd & rWnd, LPCTSTR lpszProfile)
{
ASSERT(::IsWindow(rWnd.m_hWnd) && lpszProfile && *lpszProfile);
// can't use an empty profile section string; see CWinApp::GetProfileInt() for further information
WINDOWPLACEMENT wpl;
VERIFY( rWnd.GetWindowPlacement(&wpl) );
BOOL bVisible = rWnd.IsWindowVisible();
int iState = REGVAL_NOSTATE;
if(rWnd.IsIconic())
iState = REGVAL_ICONIC;
else
if(rWnd.IsZoomed())
iState = REGVAL_MAXIMIZED;
return AfxGetApp()->WriteProfileInt(lpszProfile, lpszRegVal_Valid, REGVAL_INVALID) && // invalidate first
AfxGetApp()->WriteProfileInt(lpszProfile, lpszRegVal_Left, wpl.rcNormalPosition.left) &&
AfxGetApp()->WriteProfileInt(lpszProfile, lpszRegVal_Right, wpl.rcNormalPosition.right) &&
AfxGetApp()->WriteProfileInt(lpszProfile, lpszRegVal_Top, wpl.rcNormalPosition.top) &&
AfxGetApp()->WriteProfileInt(lpszProfile, lpszRegVal_Bottom, wpl.rcNormalPosition.bottom) &&
AfxGetApp()->WriteProfileInt(lpszProfile, lpszRegVal_Visible, bVisible ? REGVAL_VISIBLE : REGVAL_HIDDEN) &&
AfxGetApp()->WriteProfileInt(lpszProfile, lpszRegVal_State, iState) &&
AfxGetApp()->WriteProfileInt(lpszProfile, lpszRegVal_Valid, REGVAL_VALID); // validate position
}
/*
* load the registry data stored by StoreWindowPosition()
* returns true if data have been found in the registry
*
* STATIC
*/
bool cdxCDynamicControlsManager::RestoreWindowPosition(CWnd & rWnd, LPCTSTR lpszProfile, UINT restoreFlags)
{
ASSERT(::IsWindow(rWnd.m_hWnd) && lpszProfile && *lpszProfile);
// can't use an empty profile section string; see CWinApp::GetProfileInt() for further information
//
// first, we check whether the position had been saved successful any time before
//
if( AfxGetApp()->GetProfileInt(lpszProfile,lpszRegVal_Valid,REGVAL_INVALID) != REGVAL_VALID )
return false;
//
// get old position
//
WINDOWPLACEMENT wpl;
VERIFY( rWnd.GetWindowPlacement(&wpl) );
//
// read registry
//
int iState = AfxGetApp()->GetProfileInt(lpszProfile, lpszRegVal_State, REGVAL_NOSTATE);
//
// get window's previous normal position
//
wpl.rcNormalPosition.left = AfxGetApp()->GetProfileInt(lpszProfile, lpszRegVal_Left, wpl.rcNormalPosition.left);
wpl.rcNormalPosition.right = AfxGetApp()->GetProfileInt(lpszProfile, lpszRegVal_Right, wpl.rcNormalPosition.right);
wpl.rcNormalPosition.top = AfxGetApp()->GetProfileInt(lpszProfile, lpszRegVal_Top, wpl.rcNormalPosition.top);
wpl.rcNormalPosition.bottom = AfxGetApp()->GetProfileInt(lpszProfile, lpszRegVal_Bottom, wpl.rcNormalPosition.bottom);
if(wpl.rcNormalPosition.left > wpl.rcNormalPosition.right)
{
long l = wpl.rcNormalPosition.right;
wpl.rcNormalPosition.right = wpl.rcNormalPosition.left;
wpl.rcNormalPosition.left = l;
}
if(wpl.rcNormalPosition.top > wpl.rcNormalPosition.bottom)
{
long l = wpl.rcNormalPosition.bottom;
wpl.rcNormalPosition.bottom = wpl.rcNormalPosition.top;
wpl.rcNormalPosition.top = l;
}
//
// get restore stuff
//
UINT showCmd = SW_SHOWNA;
if(restoreFlags & rflg_state)
{
if(iState == REGVAL_MAXIMIZED)
showCmd = SW_MAXIMIZE;
else
if(iState == REGVAL_ICONIC)
showCmd = SW_MINIMIZE;
}
//
// use MoveWindow() which takes care of WM_GETMINMAXINFO
//
rWnd.MoveWindow( wpl.rcNormalPosition.left,wpl.rcNormalPosition.top,
wpl.rcNormalPosition.right - wpl.rcNormalPosition.left,
wpl.rcNormalPosition.bottom - wpl.rcNormalPosition.top,
showCmd == SW_SHOWNA);
if(showCmd != SW_SHOWNA)
{
// read updated position
VERIFY( rWnd.GetWindowPlacement(&wpl) );
wpl.showCmd = showCmd;
rWnd.SetWindowPlacement(&wpl);
}
//
// get visiblity
//
if(restoreFlags & rflg_visibility)
{
int i = AfxGetApp()->GetProfileInt(lpszProfile, lpszRegVal_Visible, REGVAL_NOSTATE);
if(i == REGVAL_VISIBLE)
rWnd.ShowWindow(SW_SHOW);
else
if(i == REGVAL_HIDDEN)
rWnd.ShowWindow(SW_HIDE);
}
return true;
}
syntax highlighted by Code2HTML, v. 0.9.1