554 lines
13 KiB
C++
554 lines
13 KiB
C++
// XTResize.cpp: implementation of the CXTResize class.
|
|
//
|
|
// This file is a part of the XTREME CONTROLS MFC class library.
|
|
// (c)1998-2008 Codejock Software, All Rights Reserved.
|
|
//
|
|
// THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
|
|
// RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
|
|
// CONSENT OF CODEJOCK SOFTWARE.
|
|
//
|
|
// THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
|
|
// IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
|
|
// YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
|
|
// SINGLE COMPUTER.
|
|
//
|
|
// CONTACT INFORMATION:
|
|
// support@codejock.com
|
|
// http://www.codejock.com
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "Common/XTPDrawHelpers.h"
|
|
|
|
#include "XTResizeRect.h"
|
|
#include "XTResizePoint.h"
|
|
#include "XTResize.h"
|
|
#include "XTResizeGroupBox.h"
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#define new DEBUG_NEW
|
|
#endif
|
|
|
|
#ifndef OBM_SIZE
|
|
#define OBM_SIZE 32766
|
|
#endif
|
|
|
|
#define ENTRY_WINDOWPLACEMENT _T("WindowPlacement")
|
|
|
|
#define IDC_SIZEICON 0x7FFF
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CXTResize::CXTResize(CWnd* pWnd, const UINT nFlags) :
|
|
m_pWnd(pWnd),
|
|
m_nFlagsXX(nFlags),
|
|
m_szMin(0, 0),
|
|
m_szMax(0, 0),
|
|
m_szWindow(0, 0),
|
|
m_szInitWindow(0, 0),
|
|
m_strSection(_T(""))
|
|
{
|
|
|
|
}
|
|
|
|
CXTResize::~CXTResize()
|
|
{
|
|
RemoveAllControls();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL CXTResize::RemoveResize(const UINT nID)
|
|
{
|
|
// search for an item with the given id
|
|
int iItem;
|
|
for (iItem = (int)m_arrItems.GetSize(); iItem--;)
|
|
{
|
|
CWnd* pWnd = m_arrItems[iItem]->m_pWnd;
|
|
ASSERT_VALID(pWnd);
|
|
|
|
if (pWnd->GetDlgCtrlID() == (int) nID)
|
|
{
|
|
CXTResizeItem* pItem = m_arrItems[iItem];
|
|
if (pItem != NULL)
|
|
{
|
|
m_arrItems.RemoveAt(iItem);
|
|
SAFE_DELETE(pItem);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void CXTResize::RemoveAllControls()
|
|
{
|
|
// free allocated memory
|
|
int iIndex;
|
|
for (iIndex = 0; iIndex < m_arrItems.GetSize(); ++iIndex)
|
|
{
|
|
CXTResizeItem* pItem = m_arrItems.GetAt(iIndex);
|
|
SAFE_DELETE(pItem);
|
|
}
|
|
|
|
// empty array
|
|
m_arrItems.RemoveAll();
|
|
}
|
|
|
|
void CXTResize::SetResize(const UINT nID, const HWND hWnd, const XT_RESIZERECT& rrcSizing)
|
|
{
|
|
CXTResizeItem* pItem = 0;
|
|
|
|
// search for an item with the given id
|
|
int i;
|
|
for (i = (int)m_arrItems.GetSize(); i--;)
|
|
{
|
|
CWnd* pWnd = m_arrItems[i]->m_pWnd;
|
|
if (pWnd && ::IsWindow(pWnd->m_hWnd) && (pWnd->GetDlgCtrlID() == (int)nID))
|
|
{
|
|
pItem = m_arrItems[i];
|
|
}
|
|
}
|
|
|
|
// if we didn't find it then create one
|
|
if (pItem == 0)
|
|
{
|
|
HWND hWndChild = hWnd;
|
|
if (hWndChild == NULL)
|
|
{
|
|
CWnd* pWnd = m_pWnd->GetDlgItem(nID);
|
|
if (pWnd && ::IsWindow(pWnd->m_hWnd))
|
|
{
|
|
hWndChild = pWnd->m_hWnd;
|
|
}
|
|
}
|
|
|
|
bool bDelete = false;
|
|
|
|
CWnd* pWnd = m_pWnd->FromHandlePermanent(hWndChild);
|
|
if (pWnd == NULL)
|
|
{
|
|
// there is no permanent window, create one
|
|
pWnd = new CWnd; // CXTResizeItem handles the delete
|
|
pWnd->Attach(hWndChild);
|
|
|
|
bDelete = true;
|
|
}
|
|
|
|
CRect rcWindow;
|
|
pWnd->GetWindowRect(rcWindow);
|
|
m_pWnd->ScreenToClient(rcWindow);
|
|
|
|
pItem = new CXTResizeItem(pWnd, rrcSizing, rcWindow, bDelete);
|
|
pItem->MakeTransparent(this);
|
|
|
|
m_arrItems.Add(pItem);
|
|
}
|
|
else
|
|
{
|
|
// the item already exists, just add the new sizing option
|
|
pItem->m_rrcSizing += rrcSizing;
|
|
}
|
|
// we should only allow sizing within the rect {0, 0, 1, 1}
|
|
ASSERT((CXTResizeRect(0, 0, 1, 1) & pItem->m_rrcSizing) == pItem->m_rrcSizing);
|
|
}
|
|
|
|
void CXTResize::Init()
|
|
{
|
|
if (m_pWnd == NULL)
|
|
return;
|
|
|
|
// clear the control list when initializing. The reason for this is we may have a
|
|
// static window that calls Init() multiple times for example CDialog::DoModal().
|
|
RemoveAllControls();
|
|
|
|
// get the dialog size
|
|
CRect rcWindow;
|
|
m_pWnd->GetClientRect(rcWindow);
|
|
m_szWindow = m_szInitWindow = rcWindow.Size();
|
|
|
|
if (!HasFlag(xtResizeNoMinsize))
|
|
{
|
|
// set minimum size to current window size
|
|
m_szMin = m_szWindow;
|
|
}
|
|
|
|
// set the clip children style to prevent flickering
|
|
if (!HasFlag(xtResizeNoClipChildren))
|
|
{
|
|
m_pWnd->ModifyStyle(0, WS_CLIPCHILDREN);
|
|
}
|
|
|
|
// add the size icon
|
|
if (!HasFlag(xtResizeNoSizeIcon))
|
|
{
|
|
// Create the size icon if it already doesn't exist.
|
|
if (!::IsWindow(m_scSizeIcon.m_hWnd))
|
|
{
|
|
int cxGrip = GetSystemMetrics(SM_CXVSCROLL);
|
|
int cyGrip = GetSystemMetrics(SM_CYHSCROLL);
|
|
|
|
CRect rcIcon(rcWindow);
|
|
rcIcon.OffsetRect(-1, -1);
|
|
rcIcon.left = rcIcon.right - cxGrip;
|
|
rcIcon.top = rcIcon.bottom - cyGrip;
|
|
|
|
m_scSizeIcon.Create(WS_CHILD | WS_VISIBLE | SBS_SIZEGRIP,
|
|
rcIcon, m_pWnd, IDC_SIZEICON);
|
|
}
|
|
|
|
m_scSizeIcon.SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0,
|
|
SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED);
|
|
|
|
SetResize(IDC_SIZEICON, SZ_REPOS(1));
|
|
}
|
|
}
|
|
|
|
void CXTResize::Offset(CPoint ptOffset)
|
|
{
|
|
if (m_arrItems.GetSize() > 0)
|
|
{
|
|
|
|
HDWP hDWP = ::BeginDeferWindowPos((int)m_arrItems.GetSize());
|
|
|
|
int i;
|
|
for (i = 0; i < (int)m_arrItems.GetSize(); i++)
|
|
{
|
|
CXTResizeRect rrcSizing = m_arrItems[i]->m_rrcSizing;
|
|
m_arrItems[i]->m_rrcSizing = CRect(ptOffset.x, ptOffset.y, ptOffset.x, ptOffset.y);
|
|
m_arrItems[i]->m_bInitialSize = TRUE;
|
|
|
|
Defer(hDWP, m_arrItems[i], 1, 1);
|
|
|
|
m_arrItems[i]->m_rrcSizing = rrcSizing;
|
|
}
|
|
|
|
::EndDeferWindowPos(hDWP);
|
|
|
|
// refresh group box items.
|
|
for (i = 0; i < m_arrItems.GetSize(); i++)
|
|
{
|
|
if (m_arrItems[i]->m_bIsGroupBox)
|
|
m_arrItems[i]->m_pWnd->Invalidate();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CXTResize::AjustResizeRect(CSize&)
|
|
{
|
|
}
|
|
|
|
void CXTResize::Size()
|
|
{
|
|
if (m_arrItems.GetSize() > 0)
|
|
{
|
|
CSize szWindow = CXTPClientRect(m_pWnd).Size();
|
|
if (szWindow.cx == 0 || szWindow.cy == 0)
|
|
return;
|
|
|
|
AjustResizeRect(szWindow);
|
|
|
|
CSize szDelta = szWindow - m_szWindow;
|
|
|
|
HDWP hDWP = ::BeginDeferWindowPos((int)m_arrItems.GetSize());
|
|
|
|
int i;
|
|
for (i = 0; i < (int)m_arrItems.GetSize(); i++)
|
|
{
|
|
Defer(hDWP, m_arrItems[i], szDelta.cx, szDelta.cy);
|
|
}
|
|
|
|
::EndDeferWindowPos(hDWP);
|
|
|
|
// refresh group box items.
|
|
for (i = 0; i < m_arrItems.GetSize(); i++)
|
|
{
|
|
if (m_arrItems[i]->m_bIsGroupBox)
|
|
m_arrItems[i]->m_pWnd->Invalidate();
|
|
}
|
|
|
|
m_szWindow = szWindow;
|
|
}
|
|
}
|
|
|
|
void CXTResize::Reset()
|
|
{
|
|
int iSize = (int)m_arrItems.GetSize();
|
|
int i;
|
|
for (i = 0; i < iSize; i++)
|
|
{
|
|
CXTResizeItem* pItem = m_arrItems[i];
|
|
pItem->m_bInitialSize = FALSE;
|
|
pItem->m_rrcWindow = pItem->m_rrcInitWindow;
|
|
}
|
|
}
|
|
|
|
BOOL CXTResize::Defer(HDWP& hDWP, CXTResizeItem* pItem, int dx, int dy)
|
|
{
|
|
HWND hWnd = pItem->m_pWnd->m_hWnd;
|
|
if (::IsWindow(hWnd))
|
|
{
|
|
if (!pItem->m_bInitialSize)
|
|
{
|
|
CRect rcWindow;
|
|
m_pWnd->GetClientRect(rcWindow);
|
|
|
|
dx = rcWindow.Width() - m_szInitWindow.cx;
|
|
dy = rcWindow.Height() - m_szInitWindow.cy;
|
|
|
|
pItem->m_bInitialSize = TRUE;
|
|
}
|
|
|
|
CXTResizeRect rrcItem = pItem->m_rrcWindow;
|
|
|
|
rrcItem.left += dx * pItem->m_rrcSizing.left;
|
|
rrcItem.top += dy * pItem->m_rrcSizing.top;
|
|
rrcItem.right += dx * pItem->m_rrcSizing.right;
|
|
rrcItem.bottom += dy * pItem->m_rrcSizing.bottom;
|
|
|
|
if (rrcItem != pItem->m_rrcWindow)
|
|
{
|
|
int x = (int) rrcItem.left;
|
|
int y = (int) rrcItem.top;
|
|
int cx = (int) rrcItem.Width();
|
|
int cy = (int) rrcItem.Height();
|
|
|
|
// Set positioning flags
|
|
UINT uFlags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER;
|
|
|
|
CRect rOld;
|
|
CRect rNew = rrcItem;
|
|
|
|
// get the size of the dialog item in client coordinates.
|
|
pItem->m_pWnd->GetWindowRect(&rOld);
|
|
m_pWnd->ScreenToClient(&rOld);
|
|
|
|
// if the x-y coordinates have not changed, there is no reason
|
|
// to move the dialog item.
|
|
if (rNew.TopLeft() == rOld.TopLeft())
|
|
uFlags |= SWP_NOMOVE;
|
|
|
|
// if the cx-cy size has not changed, there is no reason to
|
|
// size the dialog item. If size has changed, don't
|
|
// copy bits of the client area (i.e. have them invalidated/redrawn)
|
|
if (rNew.Size() == rOld.Size())
|
|
uFlags |= SWP_NOSIZE;
|
|
else
|
|
uFlags |= SWP_NOCOPYBITS;
|
|
|
|
hDWP = ::DeferWindowPos(hDWP, hWnd, 0, x, y, cx, cy, uFlags);
|
|
if (hDWP == NULL)
|
|
{
|
|
TRACE(_T("DeferWindowPos failed for ID %i\n"), GetDlgCtrlID(hWnd));
|
|
return FALSE;
|
|
}
|
|
pItem->m_rrcWindow = rrcItem;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
void CXTResize::GetMinMaxInfo(MINMAXINFO* pMMI)
|
|
{
|
|
CXTPClientRect rcClient(m_pWnd);
|
|
CXTPWindowRect rcWindow(m_pWnd);
|
|
if (rcClient.IsRectEmpty())
|
|
return;
|
|
|
|
CSize szBorder(rcWindow.Width() - rcClient.Width(), rcWindow.Height() - rcClient.Height());
|
|
|
|
if (m_szWindow != CSize(0, 0))
|
|
{
|
|
if (HasFlag(xtResizeNoHorizontal))
|
|
{
|
|
pMMI->ptMaxTrackSize.x = pMMI->ptMaxSize.x = m_szWindow.cx + szBorder.cx;
|
|
}
|
|
if (HasFlag(xtResizeNoVertical))
|
|
{
|
|
pMMI->ptMaxTrackSize.y = pMMI->ptMaxSize.y = m_szWindow.cy + szBorder.cy;
|
|
}
|
|
}
|
|
if (m_szMin.cx != 0) pMMI->ptMinTrackSize.x = m_szMin.cx + szBorder.cx;
|
|
if (m_szMin.cy != 0) pMMI->ptMinTrackSize.y = m_szMin.cy + szBorder.cy;
|
|
if (m_szMax.cx != 0) pMMI->ptMaxTrackSize.x = m_szMax.cx + szBorder.cx;
|
|
if (m_szMax.cy != 0) pMMI->ptMaxTrackSize.y = m_szMax.cy + szBorder.cy;
|
|
}
|
|
|
|
BOOL CXTResize::AutoLoadPlacement(LPCTSTR pszSection)
|
|
{
|
|
m_strSection = pszSection;
|
|
ASSERT(!m_strSection.IsEmpty());
|
|
return LoadPlacement(m_strSection);
|
|
}
|
|
|
|
BOOL CXTResize::LoadPlacement(LPCTSTR pszSection)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
UINT nBytes = 0;
|
|
BYTE* pBytes = 0;
|
|
AfxGetApp()->GetProfileBinary(pszSection, ENTRY_WINDOWPLACEMENT, &pBytes, &nBytes);
|
|
if (nBytes == sizeof(WINDOWPLACEMENT))
|
|
{
|
|
bResult = m_pWnd->SetWindowPlacement((WINDOWPLACEMENT*) pBytes);
|
|
}
|
|
if (pBytes && nBytes) delete[] pBytes;
|
|
|
|
return bResult;
|
|
}
|
|
|
|
BOOL CXTResize::SavePlacement(LPCTSTR pszSection)
|
|
{
|
|
WINDOWPLACEMENT wp;
|
|
if (!m_pWnd->GetWindowPlacement(&wp))
|
|
return FALSE;
|
|
|
|
AfxGetApp()->WriteProfileBinary(pszSection, ENTRY_WINDOWPLACEMENT, (BYTE*) &wp, sizeof(wp));
|
|
return TRUE;
|
|
}
|
|
|
|
void CXTResize::SetFlag(XTResize eFlag)
|
|
{
|
|
m_nFlagsXX &= (eFlag^0xFFFFFFFF);
|
|
m_nFlagsXX |= eFlag;
|
|
}
|
|
|
|
void CXTResize::SetResize(CWnd* pWnd, const CXTResizeRect& rrcSizing, CRect rcWindow)
|
|
{
|
|
CXTResizeItem *pItem = new CXTResizeItem(pWnd, rrcSizing, rcWindow, FALSE);
|
|
m_arrItems.Add(pItem);
|
|
}
|
|
|
|
void CXTResize::UpdateControlRect(CWnd* pWnd)
|
|
{
|
|
if(!pWnd)
|
|
return;
|
|
|
|
for (int i = (int)m_arrItems.GetSize(); i--;)
|
|
{
|
|
if(m_arrItems[i]->m_pWnd->m_hWnd == pWnd->m_hWnd)
|
|
{
|
|
CRect rect;
|
|
pWnd->GetWindowRect(&rect);
|
|
m_pWnd->ScreenToClient(&rect);
|
|
m_arrItems[i]->m_rrcWindow = rect;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// CXTResizeItem
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CXTResizeItem::CXTResizeItem(CWnd* pWnd, const CXTResizeRect& rrcSizing, CRect& rcWindow, BOOL bAutoDelete)
|
|
: m_pWnd(pWnd)
|
|
, m_rrcSizing(rrcSizing)
|
|
, m_rrcWindow(rcWindow)
|
|
, m_rrcInitWindow(rcWindow)
|
|
, m_bAutoDelete(bAutoDelete)
|
|
, m_bInitialSize(FALSE)
|
|
, m_bIsGroupBox(FALSE)
|
|
{
|
|
|
|
}
|
|
|
|
CXTResizeItem::~CXTResizeItem()
|
|
{
|
|
if (m_bAutoDelete)
|
|
{
|
|
m_pWnd->Detach();
|
|
SAFE_DELETE(m_pWnd);
|
|
}
|
|
}
|
|
|
|
bool CXTResizeItem::MakeTransparent(CXTResize* pXTResize)
|
|
{
|
|
if (pXTResize->HasFlag(xtResizeNoTransparentGroup))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (m_pWnd && ::IsWindow(m_pWnd->m_hWnd))
|
|
{
|
|
TCHAR szClassName[8];
|
|
::GetClassName(m_pWnd->m_hWnd, szClassName, 8);
|
|
|
|
// not a CButton derived class.
|
|
if (_tcsicmp(szClassName, _T("Button")) != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// get the window style if not a group box, return.
|
|
DWORD dwStyle = ::GetWindowLong(m_pWnd->m_hWnd, GWL_STYLE);
|
|
if ((dwStyle & (BS_GROUPBOX | WS_TABSTOP)) == BS_GROUPBOX)
|
|
{
|
|
// we don't want CXTResizeGroupBox transparent.
|
|
if (m_pWnd->IsKindOf(RUNTIME_CLASS(CXTResizeGroupBox)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Get the extended style for the group box.
|
|
DWORD dwStyleEx = ::GetWindowLong(m_pWnd->m_hWnd, GWL_EXSTYLE);
|
|
|
|
// add the WS_EX_TRANSPARENT flag to the group box.
|
|
::SetWindowLong(m_pWnd->m_hWnd, GWL_EXSTYLE,
|
|
dwStyleEx | WS_EX_TRANSPARENT);
|
|
|
|
// apply the style for the window.
|
|
::SetWindowPos(m_pWnd->m_hWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE |
|
|
SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
|
|
|
|
m_bIsGroupBox = TRUE;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// CXTSizeIcon
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CXTSizeIcon::CXTSizeIcon()
|
|
{
|
|
// Load the size cursor
|
|
m_hCursor = AfxGetApp()->LoadStandardCursor(IDC_SIZENWSE);
|
|
}
|
|
|
|
CXTSizeIcon::~CXTSizeIcon()
|
|
{
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CXTSizeIcon, CScrollBar)
|
|
//{{AFX_MSG_MAP(CXTSizeIcon)
|
|
ON_WM_SETCURSOR()
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CXTSizeIcon message handlers
|
|
|
|
BOOL CXTSizeIcon::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
|
|
{
|
|
// Set the cursor to the size cursor.
|
|
if (nHitTest == HTCLIENT && m_hCursor != NULL)
|
|
{
|
|
::SetCursor(m_hCursor);
|
|
return TRUE;
|
|
}
|
|
|
|
return CScrollBar::OnSetCursor(pWnd, nHitTest, message);
|
|
}
|