// ResizableDialog.cpp : implementation file // ///////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2000 by Paolo Messina // (ppescher@yahoo.com) // // Free for non-commercial use. // You may change the code to your needs, // provided that credits to the original // author is given in the modified files. // ///////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "ResizableDialog.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CResizableDialog inline void CResizableDialog::Construct() { m_bInitDone = FALSE; m_bUseMinTrack = TRUE; m_bUseMaxTrack = FALSE; m_bUseMaxRect = FALSE; m_bShowGrip = TRUE; m_bEnableSaveRestore = FALSE; m_szGripSize.cx = GetSystemMetrics(SM_CXVSCROLL); m_szGripSize.cy = GetSystemMetrics(SM_CYHSCROLL); } CResizableDialog::CResizableDialog() { Construct(); } CResizableDialog::CResizableDialog(UINT nIDTemplate, CWnd* pParentWnd) : CDialog(nIDTemplate, pParentWnd) { Construct(); } CResizableDialog::CResizableDialog(LPCTSTR lpszTemplateName, CWnd* pParentWnd) : CDialog(lpszTemplateName, pParentWnd) { Construct(); } CResizableDialog::~CResizableDialog() { // for safety m_arrLayout.RemoveAll(); } BEGIN_MESSAGE_MAP(CResizableDialog, CDialog) //{{AFX_MSG_MAP(CResizableDialog) ON_WM_NCHITTEST() ON_WM_GETMINMAXINFO() ON_WM_SIZE() ON_WM_DESTROY() ON_WM_PAINT() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CResizableDialog message handlers BOOL CResizableDialog::OnInitDialog() { CDialog::OnInitDialog(); UpdateGripPos(); // gets the template size as the min track size CRect rc; GetWindowRect(&rc); m_ptMinTrackSize.x = rc.Width(); m_ptMinTrackSize.y = rc.Height(); m_bInitDone = TRUE; return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } void CResizableDialog::OnDestroy() { CDialog::OnDestroy(); if (m_bEnableSaveRestore) SaveWindowRect(); // remove old windows m_arrLayout.RemoveAll(); } void CResizableDialog::OnPaint() { CPaintDC dc(this); // device context for painting if (m_bShowGrip && !IsZoomed()) { // draw size-grip dc.DrawFrameControl(&m_rcGripRect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP); } } void CResizableDialog::OnSize(UINT nType, int cx, int cy) { CWnd::OnSize(nType, cx, cy); if (nType == SIZE_MAXHIDE || nType == SIZE_MAXSHOW) return; // arrangement not needed if (m_bInitDone) { ArrangeLayout(); } } UINT CResizableDialog::OnNcHitTest(CPoint point) { CPoint pt = point; ScreenToClient(&pt); // if in size grip and in client area if (m_bShowGrip && m_rcGripRect.PtInRect(pt) && pt.x >= 0 && pt.y >= 0) return HTBOTTOMRIGHT; return CDialog::OnNcHitTest(point); } void CResizableDialog::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) { if (!m_bInitDone) return; if (m_bUseMinTrack) lpMMI->ptMinTrackSize = m_ptMinTrackSize; if (m_bUseMaxTrack) lpMMI->ptMaxTrackSize = m_ptMaxTrackSize; if (m_bUseMaxRect) { lpMMI->ptMaxPosition = m_ptMaxPos; lpMMI->ptMaxSize = m_ptMaxSize; } } // layout functions void CResizableDialog::AddAnchor(HWND wnd, CSize tl_type, CSize br_type) { ASSERT(wnd != NULL && ::IsWindow(wnd)); ASSERT(::IsChild(*this, wnd)); ASSERT(tl_type != NOANCHOR); // get control's window class CString st; GetClassName(wnd, st.GetBufferSetLength(MAX_PATH), MAX_PATH); st.ReleaseBuffer(); st.MakeUpper(); // add the style 'clipsiblings' to a GroupBox // to avoid unnecessary repainting of controls inside if (st == "BUTTON") { DWORD style = GetWindowLong(wnd, GWL_STYLE); if (style & BS_GROUPBOX) SetWindowLong(wnd, GWL_STYLE, style | WS_CLIPSIBLINGS); } // wnd classes that don't redraw client area correctly // when the hor scroll pos changes due to a resizing BOOL hscroll = FALSE; if (st == "LISTBOX") hscroll = TRUE; // wnd classes that need refresh when resized BOOL refresh = FALSE; if (st == "STATIC") { DWORD style = GetWindowLong(wnd, GWL_STYLE); switch (style & SS_TYPEMASK) { case SS_LEFT: case SS_CENTER: case SS_RIGHT: // word-wrapped text needs refresh refresh = TRUE; } // centered images or text need refresh if (style & SS_CENTERIMAGE) refresh = TRUE; // simple text never needs refresh if (style & SS_TYPEMASK == SS_SIMPLE) refresh = FALSE; } // get dialog's and control's rect CRect wndrc, objrc; GetClientRect(&wndrc); ::GetWindowRect(wnd, &objrc); ScreenToClient(&objrc); CSize tl_margin, br_margin; if (br_type == NOANCHOR) br_type = tl_type; // calculate margin for the top-left corner tl_margin.cx = objrc.left - wndrc.Width() * tl_type.cx / 100; tl_margin.cy = objrc.top - wndrc.Height() * tl_type.cy / 100; // calculate margin for the bottom-right corner br_margin.cx = objrc.right - wndrc.Width() * br_type.cx / 100; br_margin.cy = objrc.bottom - wndrc.Height() * br_type.cy / 100; // add to the list Layout obj(wnd, tl_type, tl_margin, br_type, br_margin, hscroll, refresh); m_arrLayout.Add(obj); } void CResizableDialog::ArrangeLayout() { // update size-grip InvalidateRect(&m_rcGripRect); UpdateGripPos(); InvalidateRect(&m_rcGripRect); // init some vars CRect wndrc; GetClientRect(&wndrc); int i, count = m_arrLayout.GetSize(); HDWP hdwp = BeginDeferWindowPos(count); for (i=0; iGetWindowRect(&objrc); ScreenToClient(&objrc); // calculate new top-left corner newrc.left = obj.tl_margin.cx + wndrc.Width() * obj.tl_type.cx / 100; newrc.top = obj.tl_margin.cy + wndrc.Height() * obj.tl_type.cy / 100; // calculate new bottom-right corner newrc.right = obj.br_margin.cx + wndrc.Width() * obj.br_type.cx / 100; newrc.bottom = obj.br_margin.cy + wndrc.Height() * obj.br_type.cy / 100; if (!newrc.EqualRect(&objrc)) { if (obj.adj_hscroll) { // needs repainting, due to horiz scrolling int diff = newrc.Width() - objrc.Width(); int max = wnd->GetScrollLimit(SB_HORZ); obj.need_refresh = FALSE; if (max > 0 && wnd->GetScrollPos(SB_HORZ) > max - diff) { obj.need_refresh = TRUE; } } // set flags DWORD flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION; if (newrc.TopLeft() == objrc.TopLeft()) flags |= SWP_NOMOVE; if (newrc.Size() == objrc.Size()) flags |= SWP_NOSIZE; DeferWindowPos(hdwp, obj.hwnd, NULL, newrc.left, newrc.top, newrc.Width(), newrc.Height(), flags); } } // go re-arrange child windows EndDeferWindowPos(hdwp); // refresh those that need for (i=0; iInvalidate(); wnd->UpdateWindow(); } } } void CResizableDialog::UpdateGripPos() { // size-grip goes bottom right in the client area GetClientRect(&m_rcGripRect); m_rcGripRect.left = m_rcGripRect.right - m_szGripSize.cx; m_rcGripRect.top = m_rcGripRect.bottom - m_szGripSize.cy; } // protected members void CResizableDialog::ShowSizeGrip(BOOL bShow) { if (m_bShowGrip != bShow) { m_bShowGrip = bShow; InvalidateRect(&m_rcGripRect); } } void CResizableDialog::SetMaximizedRect(const CRect& rc) { m_bUseMaxRect = TRUE; m_ptMaxPos = rc.TopLeft(); m_ptMaxSize.x = rc.Width(); m_ptMaxSize.y = rc.Height(); } void CResizableDialog::ResetMaximizedRect() { m_bUseMaxRect = FALSE; } void CResizableDialog::SetMinTrackSize(const CSize& size) { m_bUseMinTrack = TRUE; m_ptMinTrackSize.x = size.cx; m_ptMinTrackSize.y = size.cy; } void CResizableDialog::ResetMinTrackSize() { m_bUseMinTrack = FALSE; } void CResizableDialog::SetMaxTrackSize(const CSize& size) { m_bUseMaxTrack = TRUE; m_ptMaxTrackSize.x = size.cx; m_ptMaxTrackSize.y = size.cy; } void CResizableDialog::ResetMaxTrackSize() { m_bUseMaxTrack = FALSE; } // NOTE: this must be called after all the other settings // to have the dialog and its controls displayed properly void CResizableDialog::EnableSaveRestore(LPCTSTR pszSection, LPCTSTR pszEntry) { m_sSection = pszSection; m_sEntry = pszEntry; m_bEnableSaveRestore = TRUE; LoadWindowRect(); } // used to save/restore window's size and position // either in the registry or a private .INI file // depending on your application settings #define PROFILE_FMT _T("%d,%d,%d,%d,%d,%d") void CResizableDialog::SaveWindowRect() { CString data; WINDOWPLACEMENT wp; ZeroMemory(&wp, sizeof(WINDOWPLACEMENT)); wp.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(&wp); RECT& rc = wp.rcNormalPosition; // alias data.Format(PROFILE_FMT, rc.left, rc.top, rc.right, rc.bottom, wp.showCmd, wp.flags); AfxGetApp()->WriteProfileString(m_sSection, m_sEntry, data); } void CResizableDialog::LoadWindowRect() { CString data; WINDOWPLACEMENT wp; data = AfxGetApp()->GetProfileString(m_sSection, m_sEntry); if (data.IsEmpty()) // never saved before return; ZeroMemory(&wp, sizeof(WINDOWPLACEMENT)); wp.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(&wp); RECT& rc = wp.rcNormalPosition; // alias if (_stscanf(data, PROFILE_FMT, &rc.left, &rc.top, &rc.right, &rc.bottom, &wp.showCmd, &wp.flags) == 6) { SetWindowPlacement(&wp); } }