1
// cdxCDynamicControlsManager.cpp: implementation of the cdxCDynamicControlsManager class.
3
//////////////////////////////////////////////////////////////////////
6
* you should define OEMRESOURCE
7
* in your project settings (C/C++, General) !
11
#include "cdxCDynamicControlsManager.h"
17
#define OBM_SIZE 32766
18
#pragma message("*** NOTE: cdxCDynamicControlsManager.cpp: Please define OEMRESOURCE in your project settings !")
19
// taken from WinresRc.h
20
// if not used for any reason
26
static char THIS_FILE[] = __FILE__;
29
/////////////////////////////////////////////////////////////////////////////
30
// Some static variables
31
/////////////////////////////////////////////////////////////////////////////
33
#define REGVAL_NOSTATE -1
34
#define REGVAL_VISIBLE 1
35
#define REGVAL_HIDDEN 0
36
#define REGVAL_MAXIMIZED 1
37
#define REGVAL_ICONIC 0
38
#define REGVAL_INVALID 0
39
#define REGVAL_VALID 1
42
* registry value names
43
* (for StoreWindowPosition()/RestoreWindowPosition())
46
static LPCTSTR lpszRegVal_Left = _T("Left"),
47
lpszRegVal_Right = _T("Right"),
48
lpszRegVal_Top = _T("Top"),
49
lpszRegVal_Bottom = _T("Bottom"),
50
lpszRegVal_Visible = _T("Visibility"),
51
lpszRegVal_State = _T("State"),
52
lpszRegVal_Valid = _T("(valid)");
54
/////////////////////////////////////////////////////////////////////////////
55
// cdxCDynamicControlsManager::ControlData::ControlEntry
56
/////////////////////////////////////////////////////////////////////////////
58
/////////////////////////////////////////////////////////////////////////////
59
// construction/destruction
60
/////////////////////////////////////////////////////////////////////////////
62
cdxCDynamicControlsManager::ControlData::ControlEntry::ControlEntry(CWnd & ctrl, ControlData & rMaster)
63
: m_rMaster(rMaster), m_rCtrl(ctrl)
65
if(m_pNext = rMaster.m_pCtrl)
66
m_pNext->m_pPrev = this;
67
rMaster.m_pCtrl = this;
70
// raise total counter
71
++rMaster.m_rMaster.m_iTotalCnt;
74
cdxCDynamicControlsManager::ControlData::ControlEntry::~ControlEntry()
78
if(m_pPrev->m_pNext = m_pNext)
79
m_pNext->m_pPrev = m_pPrev;
83
ASSERT( m_rMaster.m_pCtrl == this );
84
if(m_rMaster.m_pCtrl = m_pNext)
85
m_pNext->m_pPrev = NULL;
89
ASSERT( m_rMaster.m_rMaster.m_iTotalCnt > 0 );
90
++m_rMaster.m_rMaster.m_iTotalCnt;
93
/////////////////////////////////////////////////////////////////////////////
94
// cdxCDynamicControlsManager::ControlData
95
/////////////////////////////////////////////////////////////////////////////
97
/////////////////////////////////////////////////////////////////////////////
98
// construction/destruction
99
/////////////////////////////////////////////////////////////////////////////
103
* copies all paramaters and gets the controls initial position using
106
* NOTE that the constructor need ctrl.m_hWnd to exist (in contrast to
110
cdxCDynamicControlsManager::ControlData::ControlData(cdxCDynamicControlsManager & rMaster, CWnd & ctrl, const PositionSetup & rPosSetup)
111
: m_rMaster(rMaster), m_pCtrl(NULL),
112
m_pNext(NULL), m_pPrev(NULL),
113
m_posSetup(rPosSetup)
115
ASSERT(::IsWindow(ctrl.m_hWnd)); // control must have already been created !
116
ASSERT(rPosSetup.IsValid());
119
// get initial values
123
VERIFY( ctrl.GetWindowPlacement(&wpl) );
125
m_rectOriginal = wpl.rcNormalPosition;
131
new ControlEntry(ctrl,*this);
132
ASSERT(m_pCtrl != NULL);
135
// link us to the cdxCDynamicControlsManager's list
138
if(m_pNext = m_rMaster.m_pFirst)
139
m_pNext->m_pPrev = this;
141
m_rMaster.m_pFirst = this;
146
* The m_Ctrl deletes all children by itself
149
cdxCDynamicControlsManager::ControlData::~ControlData()
152
// delete all control references
164
if(m_pPrev->m_pNext = m_pNext)
165
m_pNext->m_pPrev = m_pPrev;
169
ASSERT(m_rMaster.m_pFirst == this);
170
if(m_rMaster.m_pFirst = m_pNext)
171
m_pNext->m_pPrev = NULL;
175
/////////////////////////////////////////////////////////////////////////////
176
// cdxCDynamicControlsManager::ControlData virtuals
177
/////////////////////////////////////////////////////////////////////////////
180
* checks whether the CWnd is part of this control data
183
bool cdxCDynamicControlsManager::ControlData::IsMember(CWnd & ctrl) const
185
for(const ControlEntry *pEntry = m_pCtrl; pEntry; pEntry = pEntry->GetNext())
193
* removes a CWnd from this chain
196
bool cdxCDynamicControlsManager::ControlData::Rem(CWnd & ctrl)
198
for(ControlEntry *pEntry = m_pCtrl; pEntry; pEntry = pEntry->GetNext())
209
* Get current position
212
CRect cdxCDynamicControlsManager::ControlData::GetCurrentPosition() const
216
ASSERT(false); // all sub-controls have been deleted
217
return CRect(0,0,0,0);
221
VERIFY( m_pCtrl->GetCWnd().GetWindowPlacement(&wpl) );
222
return CRect(wpl.rcNormalPosition);
226
* modify initial setup
227
* NOTE: this function does not move the controls.
228
* You habe to call cdxCDynamicControlsManager::ReorganizeControls past
229
* using this function
232
bool cdxCDynamicControlsManager::ControlData::Modify(const CRect & rectOriginal, const PositionSetup & rSetup)
234
if((rectOriginal.left > rectOriginal.right) ||
235
(rectOriginal.top > rectOriginal.bottom) ||
238
ASSERT(false); // bad function call
242
m_rectOriginal = rectOriginal;
247
/////////////////////////////////////////////////////////////////////////////
248
// cdxCDynamicControlsManager
249
/////////////////////////////////////////////////////////////////////////////
251
/////////////////////////////////////////////////////////////////////////////
252
// handling events from CWnd
253
/////////////////////////////////////////////////////////////////////////////
256
* this function initializes the following members:
257
* m_pWnd - the window handle
258
* m_szCurrent - the current window's size
259
* m_szMin - the minimum window's size (taken from current size)
260
* m_szMax - the maximum window's size (set to (0,0) <=> don't change maximum)
261
* m_wndSizeIcon - the icon (if wanted)
264
* rWnd - the window to supervise
265
* fd - in which directions can we size the window (does only apply to user-sizing)
266
* bSizeIcon - do you want a sizable icon ?
267
* pBaseClientSize- if non-zero, this defines the real (normal) size of the
268
* window relative to all furher calculations will be made.
269
* if zero, the current window's size will be taken.
272
void cdxCDynamicControlsManager::DoInitWindow(CWnd & rWnd, Freedom fd, bool bSizeIcon, const CSize * pBaseClientSize)
274
ASSERT(m_pWnd == NULL); // you MUST NOT call this function twice !
275
ASSERT(::IsWindow(rWnd.m_hWnd)); // rWnd MUST already exist !!
281
// get current's window size
285
m_pWnd->GetWindowRect(&rect);
287
m_pWnd->GetClientRect(&rectClient);
291
m_szClientRelative.cx = rectClient.Width();
292
m_szClientRelative.cy = rectClient.Height();
294
m_szMin.cx = rect.Width();
295
m_szMin.cy = rect.Height();
299
ASSERT((pBaseClientSize->cx > 0) && (pBaseClientSize->cy > 0));
301
m_szClientRelative = *pBaseClientSize;
302
m_szMin.cx = m_szClientRelative.cx + (rect.Width() - rectClient.Width());
303
m_szMin.cy = m_szClientRelative.cy + (rect.Height() - rectClient.Height());
310
// set up icon if wanted
315
VERIFY( m_pWndSizeIcon = new cdxCSizeIconCtrl );
316
VERIFY( m_pWndSizeIcon->Create(m_pWnd,m_idSizeIcon) ); // creates my control; id is SIZE_CONTROL_ID
318
AddSzControl(*m_pWndSizeIcon,mdRepos,mdRepos);
319
m_pWndSizeIcon->ShowWindow(SW_SHOW); // finally - show it
323
/////////////////////////////////////////////////////////////////////////////
326
* fill in MINMAXINFO as requested
327
* Call your CWnd's OnGetMinMaxInfo first !
329
* changed due to a but report by Michel Wassink <mww@mitutoyo.nl>
332
void cdxCDynamicControlsManager::DoOnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
334
if(IsReady() && !IsDisabled())
336
lpMMI->ptMinTrackSize.x = m_szMin.cx;
337
lpMMI->ptMinTrackSize.y = m_szMin.cy;
339
if(m_Freedom & fdHoriz)
342
lpMMI->ptMaxTrackSize.x = m_szMax.cx;
345
lpMMI->ptMaxTrackSize.x = m_szMin.cx;
347
if(m_Freedom & fdVert)
350
lpMMI->ptMaxTrackSize.y = m_szMax.cy;
353
lpMMI->ptMaxTrackSize.y = m_szMin.cy;
358
* handle OnSize - we can't rely on cx,cy being client dimensions
359
* as stated in the documentation ...
362
void cdxCDynamicControlsManager::DoOnSize(UINT nType, int cx, int cy)
364
if(IsReady() && (nType != SIZE_MINIMIZED) && !IsDisabled())
365
ReorganizeControls(true);
370
* after having called this function, you can reuse the object
371
* although I wouldn't recommend to do so :)
374
void cdxCDynamicControlsManager::DoDestroyWindow()
378
OnDeleteControlPosition(*m_pFirst);
384
if(::IsWindow(m_pWndSizeIcon->m_hWnd))
385
m_pWndSizeIcon->DestroyWindow();
386
delete m_pWndSizeIcon;
387
m_pWndSizeIcon = NULL;
393
/////////////////////////////////////////////////////////////////////////////
394
// control positioning
395
/////////////////////////////////////////////////////////////////////////////
398
* Reposition (without current rectangle size)
400
* rectWin - window rectangle (including border)
401
* rectClient - window rectangle (client area)
402
* Note that since release 6, rectClient.left and .top might be > zero
404
* bRedraw - invalidate & update window
407
void cdxCDynamicControlsManager::ReorganizeControlsAdvanced(const CRect & rectWin, CRect rectClient, bool bRedraw)
411
if(!GetTotalChildCnt())
415
// we won't go smaller with the whole window than
419
if(rectWin.Width() < m_szMin.cx)
420
rectClient.right += (m_szMin.cx - rectWin.Width());
421
if(rectWin.Height() < m_szMin.cy)
422
rectClient.bottom += (m_szMin.cy - rectWin.Height());
425
// we new replace all controls
429
szDelta.cx = rectClient.Width() - m_szClientRelative.cx;
430
szDelta.cy = rectClient.Height() - m_szClientRelative.cy;
432
CPoint pntOffset = rectClient.TopLeft();
434
// newly added code by Rodger Bernstein
436
AFX_SIZEPARENTPARAMS layout;
440
if(!( layout.hDWP = ::BeginDeferWindowPos(GetTotalChildCnt()) ))
442
TRACE(_T("*** ERROR[cdxCDynamicControlsManager::ReorganizeControlsAdvanced()]: BeginDeferWindowPos() failed.\n"));
446
for(sz = m_pFirst; sz; sz = sz->GetNext())
447
sz->OnSize(szDelta, &layout, &pntOffset);
449
if(!::EndDeferWindowPos(layout.hDWP))
451
TRACE(_T("*** ERROR[cdxCDynamicControlsManager::ReorganizeControlsAdvanced()]: EndDeferWindowPos() failed.\n"));
458
for(sz = m_pFirst; sz; sz = sz->GetNext())
459
sz->OnSize(szDelta, NULL);
461
if(bRedraw && m_pWnd->IsWindowVisible())
463
m_pWnd->RedrawWindow(NULL, NULL, RDW_UPDATENOW | RDW_NOERASE);
467
/////////////////////////////////////////////////////////////////////////////
469
/////////////////////////////////////////////////////////////////////////////
472
* change minimum and maximum height of window.
474
* szMin - new minimum size (use GetMinSize() to leave it as being before)
475
* szMax - new maximum size ( " GetMaxSize() ")
476
* Set to CSize(0,0) if you don't want a maximum size.
477
* bResizeIfNecessary - call FixWindowSize() past calculating new sizes.
479
* returns false if szMin and szMax are illegal (e.g. szMin > szMax)
482
bool cdxCDynamicControlsManager::SetMinMaxSize(const CSize & szMin, const CSize & szMax, bool bResizeIfNecessary)
484
ASSERT(IsReady()); // DoInitWindow() not called ?
486
if((szMax.cx && (szMin.cx > szMax.cx)) ||
487
(szMax.cy && (szMin.cy > szMax.cy)))
495
if(bResizeIfNecessary)
502
* this function ensure that the window's size is between m_szMin and m_szMax.
503
* returns true if window size has been changed
506
bool cdxCDynamicControlsManager::FixWindowSize()
508
ASSERT(IsReady()); // use DoInitWindow() first !
510
CSize szCurrent = GetWindowSize(*m_pWnd),
513
if(m_szMax.cx && (szCurrent.cx > m_szMax.cx))
514
szDelta.cx = m_szMax.cx - szCurrent.cx; // is negative
516
if(szCurrent.cx < m_szMin.cx)
517
szDelta.cx = m_szMin.cx - szCurrent.cx; // is positive
521
if(m_szMax.cy && (szCurrent.cy > m_szMax.cy))
522
szDelta.cy = m_szMax.cy - szCurrent.cy; // is negative
524
if(szCurrent.cy < m_szMin.cy)
525
szDelta.cy = m_szMin.cy - szCurrent.cy; // is positive
529
if(!szDelta.cx && !szDelta.cy)
530
return false; // nothing to do
532
StretchWindow(*m_pWnd,szDelta);
536
/////////////////////////////////////////////////////////////////////////////
542
void cdxCDynamicControlsManager::HideSizeIcon()
544
if(m_pWndSizeIcon && ::IsWindow(m_pWndSizeIcon->m_hWnd))
545
m_pWndSizeIcon->ShowWindow(SW_HIDE);
548
void cdxCDynamicControlsManager::ShowSizeIcon()
550
if(m_pWndSizeIcon && ::IsWindow(m_pWndSizeIcon->m_hWnd))
551
m_pWndSizeIcon->ShowWindow(SW_SHOW);
554
/////////////////////////////////////////////////////////////////////////////
555
// static functions: window sizing
556
/////////////////////////////////////////////////////////////////////////////
559
* stretches the window by szDelta (i.e. if szDelta is 100, the window is enlarged by 100 pixels)
560
* stretching means that the center point of the window remains
562
* returns false if the window would be smaller than (1,1)
564
* NOTE: this function does NOT care of the min/max dimensions of a window
565
* Use MoveWindow() if you need to take care of it.
570
bool cdxCDynamicControlsManager::StretchWindow(CWnd & rWnd, const CSize & szDelta)
572
ASSERT(::IsWindow(rWnd.m_hWnd));
575
rWnd.GetWindowPlacement(&wpl);
577
wpl.rcNormalPosition.left -= szDelta.cx / 2;
578
wpl.rcNormalPosition.right += (szDelta.cx + 1) / 2;
579
wpl.rcNormalPosition.top -= szDelta.cy / 2;
580
wpl.rcNormalPosition.bottom += (szDelta.cy + 1) / 2;
581
// wpl.flags = SW_SHOWNA|SW_SHOWNOACTIVATE;
583
if((wpl.rcNormalPosition.left >= wpl.rcNormalPosition.right) ||
584
(wpl.rcNormalPosition.top >= wpl.rcNormalPosition.bottom))
587
VERIFY( rWnd.SetWindowPos( NULL,
588
wpl.rcNormalPosition.left,
589
wpl.rcNormalPosition.top,
590
wpl.rcNormalPosition.right - wpl.rcNormalPosition.left,
591
wpl.rcNormalPosition.bottom - wpl.rcNormalPosition.top,
592
SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER) );
598
* stretch window by a percent value
599
* the algorithm calculates the new size for both dimensions by:
601
* newWid = oldWid + (oldWid * iAddPcnt) / 100
603
* NOTE: iAddPcnt may even be nagtive, but it MUST be greater than -100.
604
* NOTE: this function does NOT care of the min/max dimensions of a window
606
* The function will return false if the new size would be empty.
611
bool cdxCDynamicControlsManager::StretchWindow(CWnd & rWnd, int iAddPcnt)
613
ASSERT(::IsWindow(rWnd.m_hWnd));
615
CSize szDelta = GetWindowSize(rWnd);
617
szDelta.cx = (szDelta.cx * iAddPcnt) / 100;
618
szDelta.cy = (szDelta.cy * iAddPcnt) / 100;
620
return StretchWindow(rWnd,szDelta);
624
* get current window's size
629
CSize cdxCDynamicControlsManager::GetWindowSize(CWnd & rWnd)
631
ASSERT(::IsWindow(rWnd.m_hWnd));
634
rWnd.GetWindowRect(&rect);
635
return CSize(rect.Width(),rect.Height());
638
/////////////////////////////////////////////////////////////////////////////
639
// static functions: window & registry
640
/////////////////////////////////////////////////////////////////////////////
643
* stores a window's position and visiblity to the registry.
645
* return false if any error occured
650
bool cdxCDynamicControlsManager::StoreWindowPosition(CWnd & rWnd, LPCTSTR lpszProfile)
652
ASSERT(::IsWindow(rWnd.m_hWnd) && lpszProfile && *lpszProfile);
653
// can't use an empty profile section string; see CWinApp::GetProfileInt() for further information
656
VERIFY( rWnd.GetWindowPlacement(&wpl) );
657
BOOL bVisible = rWnd.IsWindowVisible();
658
int iState = REGVAL_NOSTATE;
661
iState = REGVAL_ICONIC;
664
iState = REGVAL_MAXIMIZED;
666
return AfxGetApp()->WriteProfileInt(lpszProfile, lpszRegVal_Valid, REGVAL_INVALID) && // invalidate first
667
AfxGetApp()->WriteProfileInt(lpszProfile, lpszRegVal_Left, wpl.rcNormalPosition.left) &&
668
AfxGetApp()->WriteProfileInt(lpszProfile, lpszRegVal_Right, wpl.rcNormalPosition.right) &&
669
AfxGetApp()->WriteProfileInt(lpszProfile, lpszRegVal_Top, wpl.rcNormalPosition.top) &&
670
AfxGetApp()->WriteProfileInt(lpszProfile, lpszRegVal_Bottom, wpl.rcNormalPosition.bottom) &&
671
AfxGetApp()->WriteProfileInt(lpszProfile, lpszRegVal_Visible, bVisible ? REGVAL_VISIBLE : REGVAL_HIDDEN) &&
672
AfxGetApp()->WriteProfileInt(lpszProfile, lpszRegVal_State, iState) &&
673
AfxGetApp()->WriteProfileInt(lpszProfile, lpszRegVal_Valid, REGVAL_VALID); // validate position
677
* load the registry data stored by StoreWindowPosition()
678
* returns true if data have been found in the registry
683
bool cdxCDynamicControlsManager::RestoreWindowPosition(CWnd & rWnd, LPCTSTR lpszProfile, UINT restoreFlags)
685
ASSERT(::IsWindow(rWnd.m_hWnd) && lpszProfile && *lpszProfile);
686
// can't use an empty profile section string; see CWinApp::GetProfileInt() for further information
689
// first, we check whether the position had been saved successful any time before
692
if( AfxGetApp()->GetProfileInt(lpszProfile,lpszRegVal_Valid,REGVAL_INVALID) != REGVAL_VALID )
700
VERIFY( rWnd.GetWindowPlacement(&wpl) );
706
int iState = AfxGetApp()->GetProfileInt(lpszProfile, lpszRegVal_State, REGVAL_NOSTATE);
709
// get window's previous normal position
712
wpl.rcNormalPosition.left = AfxGetApp()->GetProfileInt(lpszProfile, lpszRegVal_Left, wpl.rcNormalPosition.left);
713
wpl.rcNormalPosition.right = AfxGetApp()->GetProfileInt(lpszProfile, lpszRegVal_Right, wpl.rcNormalPosition.right);
714
wpl.rcNormalPosition.top = AfxGetApp()->GetProfileInt(lpszProfile, lpszRegVal_Top, wpl.rcNormalPosition.top);
715
wpl.rcNormalPosition.bottom = AfxGetApp()->GetProfileInt(lpszProfile, lpszRegVal_Bottom, wpl.rcNormalPosition.bottom);
717
if(wpl.rcNormalPosition.left > wpl.rcNormalPosition.right)
719
long l = wpl.rcNormalPosition.right;
720
wpl.rcNormalPosition.right = wpl.rcNormalPosition.left;
721
wpl.rcNormalPosition.left = l;
723
if(wpl.rcNormalPosition.top > wpl.rcNormalPosition.bottom)
725
long l = wpl.rcNormalPosition.bottom;
726
wpl.rcNormalPosition.bottom = wpl.rcNormalPosition.top;
727
wpl.rcNormalPosition.top = l;
734
UINT showCmd = SW_SHOWNA;
736
if(restoreFlags & rflg_state)
738
if(iState == REGVAL_MAXIMIZED)
739
showCmd = SW_MAXIMIZE;
741
if(iState == REGVAL_ICONIC)
742
showCmd = SW_MINIMIZE;
746
// use MoveWindow() which takes care of WM_GETMINMAXINFO
749
rWnd.MoveWindow( wpl.rcNormalPosition.left,wpl.rcNormalPosition.top,
750
wpl.rcNormalPosition.right - wpl.rcNormalPosition.left,
751
wpl.rcNormalPosition.bottom - wpl.rcNormalPosition.top,
752
showCmd == SW_SHOWNA);
754
if(showCmd != SW_SHOWNA)
756
// read updated position
758
VERIFY( rWnd.GetWindowPlacement(&wpl) );
759
wpl.showCmd = showCmd;
760
rWnd.SetWindowPlacement(&wpl);
767
if(restoreFlags & rflg_visibility)
769
int i = AfxGetApp()->GetProfileInt(lpszProfile, lpszRegVal_Visible, REGVAL_NOSTATE);
770
if(i == REGVAL_VISIBLE)
771
rWnd.ShowWindow(SW_SHOW);
773
if(i == REGVAL_HIDDEN)
774
rWnd.ShowWindow(SW_HIDE);