// ResizableGrip.cpp: implementation of the CResizableGrip class.
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2000-2002 by Paolo Messina
// (http://www.geocities.com/ppescher - ppescher@yahoo.com)
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// If you find this code useful, credits would be nice!
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ResizableGrip.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
// Update for CTreePropSheetEx to allow compilation on machines without
// Platform SDK installed (Yves Tkaczyk).
// Define constants that are in the Platform SDK, but not in the default VC6
// installation.
// Thank to Don M for suggestion
// (http://www.codeproject.com/property/TreePropSheetEx.asp?msg=939854#xx939854xx)
#ifndef WS_EX_LAYOUTRTL
#define WS_EX_LAYOUTRTL 0x400000 /* w98, w2k */
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CResizableGrip::CResizableGrip()
{
m_nShowCount = 0;
}
CResizableGrip::~CResizableGrip()
{
}
void CResizableGrip::UpdateSizeGrip()
{
ASSERT(::IsWindow(m_wndGrip.m_hWnd));
// size-grip goes bottom right in the client area
// (any right-to-left adjustment should go here)
RECT rect;
GetResizableWnd()->GetClientRect(&rect);
rect.left = rect.right - m_wndGrip.m_size.cx;
rect.top = rect.bottom - m_wndGrip.m_size.cy;
// must stay below other children
m_wndGrip.SetWindowPos(&CWnd::wndBottom, rect.left, rect.top, 0, 0,
SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOREPOSITION
| (IsSizeGripVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
}
// pbStatus points to a variable, maintained by the caller, that
// holds its visibility status. Initialize the variable with 1
// to allow to temporarily hide the grip, 0 to allow to
// temporarily show the grip (with respect to the dwMask bit).
// NB: visibility is effective only after an update
void CResizableGrip::ShowSizeGrip(DWORD* pStatus, DWORD dwMask /*= 1*/)
{
ASSERT(pStatus != NULL);
if (!(*pStatus & dwMask))
{
m_nShowCount++;
(*pStatus) |= dwMask;
}
}
void CResizableGrip::HideSizeGrip(DWORD* pStatus, DWORD dwMask /*= 1*/)
{
ASSERT(pStatus != NULL);
if (*pStatus & dwMask)
{
m_nShowCount--;
(*pStatus) &= ~dwMask;
}
}
BOOL CResizableGrip::IsSizeGripVisible()
{
// NB: visibility is effective only after an update
return (m_nShowCount > 0);
}
void CResizableGrip::SetSizeGripVisibility(BOOL bVisible)
{
if (bVisible)
m_nShowCount = 1;
else
m_nShowCount = 0;
}
BOOL CResizableGrip::SetSizeGripBkMode(int nBkMode)
{
if (::IsWindow(m_wndGrip.m_hWnd))
{
if (nBkMode == OPAQUE)
m_wndGrip.SetTransparency(FALSE);
else if (nBkMode == TRANSPARENT)
m_wndGrip.SetTransparency(TRUE);
else
return FALSE;
return TRUE;
}
return FALSE;
}
void CResizableGrip::SetSizeGripShape(BOOL bTriangular)
{
m_wndGrip.SetTriangularShape(bTriangular);
}
BOOL CResizableGrip::CreateSizeGrip(BOOL bVisible /*= TRUE*/,
BOOL bTriangular /*= TRUE*/, BOOL bTransparent /*= FALSE*/)
{
// create grip
CRect rect(0 , 0, m_wndGrip.m_size.cx, m_wndGrip.m_size.cy);
BOOL bRet = m_wndGrip.Create(WS_CHILD | WS_CLIPSIBLINGS
| SBS_SIZEGRIP, rect, GetResizableWnd(), 0);
if (bRet)
{
// set options
m_wndGrip.SetTriangularShape(bTriangular);
m_wndGrip.SetTransparency(bTransparent);
SetSizeGripVisibility(bVisible);
// update position
UpdateSizeGrip();
}
return bRet;
}
/////////////////////////////////////////////////////////////////////////////
// CSizeGrip implementation
BOOL CResizableGrip::CSizeGrip::IsRTL()
{
return GetExStyle() & WS_EX_LAYOUTRTL;
}
BOOL CResizableGrip::CSizeGrip::PreCreateWindow(CREATESTRUCT& cs)
{
// set window size
m_size.cx = GetSystemMetrics(SM_CXVSCROLL);
m_size.cy = GetSystemMetrics(SM_CYHSCROLL);
cs.cx = m_size.cx;
cs.cy = m_size.cy;
return CScrollBar::PreCreateWindow(cs);
}
LRESULT CResizableGrip::CSizeGrip::WindowProc(UINT message,
WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_GETDLGCODE:
// fix to prevent the control to gain focus, using arrow keys
// (standard grip returns DLGC_WANTARROWS, like any standard scrollbar)
return DLGC_STATIC;
case WM_NCHITTEST:
// choose proper cursor shape
if (IsRTL())
return HTBOTTOMLEFT;
else
return HTBOTTOMRIGHT;
break;
case WM_SETTINGCHANGE:
{
// update grip's size
CSize sizeOld = m_size;
m_size.cx = GetSystemMetrics(SM_CXVSCROLL);
m_size.cy = GetSystemMetrics(SM_CYHSCROLL);
// resize transparency bitmaps
if (m_bTransparent)
{
CClientDC dc(this);
// destroy bitmaps
m_bmGrip.DeleteObject();
m_bmMask.DeleteObject();
// re-create bitmaps
m_bmGrip.CreateCompatibleBitmap(&dc, m_size.cx, m_size.cy);
m_bmMask.CreateBitmap(m_size.cx, m_size.cy, 1, 1, NULL);
}
// re-calc shape
if (m_bTriangular)
SetTriangularShape(m_bTriangular);
// reposition the grip
CRect rect;
GetWindowRect(rect);
rect.InflateRect(m_size.cx - sizeOld.cx, m_size.cy - sizeOld.cy, 0, 0);
::MapWindowPoints(NULL, GetParent()->GetSafeHwnd(), (LPPOINT)&rect, 2);
MoveWindow(rect, TRUE);
}
break;
case WM_DESTROY:
// perform clean up
if (m_bTransparent)
SetTransparency(FALSE);
break;
case WM_PAINT:
if (m_bTransparent)
{
CPaintDC dc(this);
// select bitmaps
CBitmap *pOldGrip, *pOldMask;
pOldGrip = m_dcGrip.SelectObject(&m_bmGrip);
pOldMask = m_dcMask.SelectObject(&m_bmMask);
// obtain original grip bitmap, make the mask and prepare masked bitmap
CScrollBar::WindowProc(WM_PAINT, (WPARAM)m_dcGrip.GetSafeHdc(), lParam);
m_dcGrip.SetBkColor(m_dcGrip.GetPixel(0, 0));
m_dcMask.BitBlt(0, 0, m_size.cx, m_size.cy, &m_dcGrip, 0, 0, SRCCOPY);
m_dcGrip.BitBlt(0, 0, m_size.cx, m_size.cy, &m_dcMask, 0, 0, 0x00220326);
// draw transparently
dc.BitBlt(0, 0, m_size.cx, m_size.cy, &m_dcMask, 0, 0, SRCAND);
dc.BitBlt(0, 0, m_size.cx, m_size.cy, &m_dcGrip, 0, 0, SRCPAINT);
// unselect bitmaps
m_dcGrip.SelectObject(pOldGrip);
m_dcMask.SelectObject(pOldMask);
return 0;
}
break;
}
return CScrollBar::WindowProc(message, wParam, lParam);
}
void CResizableGrip::CSizeGrip::SetTransparency(BOOL bActivate)
{
// creates or deletes DCs and Bitmaps used for
// implementing a transparent size grip
if (bActivate && !m_bTransparent)
{
m_bTransparent = TRUE;
CClientDC dc(this);
// create memory DCs and bitmaps
m_dcGrip.CreateCompatibleDC(&dc);
m_bmGrip.CreateCompatibleBitmap(&dc, m_size.cx, m_size.cy);
m_dcMask.CreateCompatibleDC(&dc);
m_bmMask.CreateBitmap(m_size.cx, m_size.cy, 1, 1, NULL);
}
else if (!bActivate && m_bTransparent)
{
m_bTransparent = FALSE;
// destroy memory DCs and bitmaps
m_dcGrip.DeleteDC();
m_bmGrip.DeleteObject();
m_dcMask.DeleteDC();
m_bmMask.DeleteObject();
}
}
void CResizableGrip::CSizeGrip::SetTriangularShape(BOOL bEnable)
{
m_bTriangular = bEnable;
if (bEnable)
{
// set a triangular window region
CRect rect;
GetWindowRect(rect);
rect.OffsetRect(-rect.TopLeft());
POINT arrPoints[] =
{
{ rect.left, rect.bottom },
{ rect.right, rect.bottom },
{ rect.right, rect.top }
};
CRgn rgnGrip;
rgnGrip.CreatePolygonRgn(arrPoints, 3, WINDING);
SetWindowRgn((HRGN)rgnGrip.Detach(), IsWindowVisible());
}
else
{
SetWindowRgn((HRGN)NULL, IsWindowVisible());
}
}