474 lines
11 KiB
C++
474 lines
11 KiB
C++
/*
|
|
* This is a part of the BugTrap package.
|
|
* Copyright (c) 2005-2009 IntelleSoft.
|
|
* All rights reserved.
|
|
*
|
|
* Description: Splitter control class.
|
|
* Author: Maksim Pyatkovskiy.
|
|
*
|
|
* This source code is only intended as a supplement to the
|
|
* BugTrap package reference and related electronic documentation
|
|
* provided with the product. See these sources for detailed
|
|
* information regarding the BugTrap package.
|
|
*/
|
|
|
|
#include "StdAfx.h"
|
|
#include "Splitter.h"
|
|
#include "BugTrapUtils.h"
|
|
#include "Globals.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#endif
|
|
|
|
/**
|
|
* @param eDirection - splitter direction.
|
|
* @param bProportionalMode - proportional mode.
|
|
*/
|
|
CSplitter::CSplitter(SPLITTER_DIRECTION eDirection, bool bProportionalMode)
|
|
{
|
|
ClearPanels();
|
|
m_pfnOldSplitterWndProc = NULL;
|
|
m_bProportionalMode = bProportionalMode;
|
|
SetDirection(eDirection);
|
|
m_nSplitterSize = 0;
|
|
}
|
|
|
|
/**
|
|
* @param eDirection - new splitter direction.
|
|
*/
|
|
void CSplitter::SetDirection(SPLITTER_DIRECTION eDirection)
|
|
{
|
|
m_eDirection = eDirection;
|
|
if (m_hwnd)
|
|
ResetSplitterPos();
|
|
else
|
|
ClearSplitterPos();
|
|
}
|
|
|
|
void CSplitter::ResetSplitterPos(void)
|
|
{
|
|
_ASSERTE(m_hwnd != NULL);
|
|
m_nSplitterSize = CalcSplitterSize();
|
|
m_nSplitterPos = m_nIdealPos = m_nSplitterSize / 2;
|
|
UpdateLayout();
|
|
}
|
|
|
|
/**
|
|
* @param rect - splitter bar coordinates.
|
|
*/
|
|
void CSplitter::GetSplitterBarRect(RECT& rect) const
|
|
{
|
|
_ASSERTE(m_hwnd != NULL);
|
|
GetClientRect(m_hwnd, &rect);
|
|
int nBarSize = GetSystemMetrics(m_eDirection == SD_VERTICAL ? SM_CYFRAME : SM_CXFRAME);
|
|
int nSplitterBarEnd = m_nSplitterPos + nBarSize;
|
|
if (m_eDirection == SD_VERTICAL)
|
|
{
|
|
rect.top = m_nSplitterPos;
|
|
if (rect.bottom > nSplitterBarEnd)
|
|
rect.bottom = nSplitterBarEnd;
|
|
}
|
|
else
|
|
{
|
|
rect.left = m_nSplitterPos;
|
|
if (rect.right > nSplitterBarEnd)
|
|
rect.right = nSplitterBarEnd;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param point - point coordinates.
|
|
* @return true if point appears in boundaries of Splitter text.
|
|
*/
|
|
BOOL CSplitter::HitTest(const POINT& point) const
|
|
{
|
|
_ASSERTE(m_hwnd != NULL);
|
|
RECT rect;
|
|
GetSplitterBarRect(rect);
|
|
return PtInRect(&rect, point);
|
|
}
|
|
|
|
/**
|
|
* @param x - point x-coordinate.
|
|
* @param y - point y-coordinate.
|
|
* @return true if point appears in boundaries of Splitter text.
|
|
*/
|
|
BOOL CSplitter::HitTest(int x, int y) const
|
|
{
|
|
POINT point;
|
|
point.x = x;
|
|
point.y = y;
|
|
return HitTest(point);
|
|
}
|
|
|
|
/**
|
|
* @param hdc - drawing context.
|
|
* @param iPanel - panel number.
|
|
* @return true if panel index is valid.
|
|
*/
|
|
BOOL CSplitter::DrawPanel(HDC hdc, int iPanel) const
|
|
{
|
|
_ASSERTE(m_hwnd != NULL);
|
|
_ASSERTE(g_pResManager != NULL);
|
|
if (iPanel >= 0 && iPanel < NUM_PANELS)
|
|
{
|
|
RECT rect;
|
|
CalcPanelRect(iPanel, rect, false);
|
|
HWND hwndPanel = m_arrPanels[iPanel];
|
|
if (hwndPanel == NULL || (GetWindowLong(hwndPanel, GWL_EXSTYLE) & WS_EX_CLIENTEDGE) == 0)
|
|
{
|
|
int nCXEdge = GetSystemMetrics(SM_CXEDGE) * 2;
|
|
int nCYEdge = GetSystemMetrics(SM_CYEDGE) * 2;
|
|
if (rect.right - rect.left >= nCXEdge && rect.bottom - rect.top >= nCYEdge)
|
|
DrawEdge(hdc, &rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
|
|
}
|
|
if (hwndPanel == NULL)
|
|
FillRect(hdc, &rect, g_pResManager->m_hbrAppWorkspace);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @param hdc - drawing context.
|
|
*/
|
|
void CSplitter::DrawSplitter(HDC hdc) const
|
|
{
|
|
_ASSERTE(m_hwnd != NULL);
|
|
DrawSplitterBar(hdc);
|
|
DrawPanel(hdc, 0);
|
|
DrawPanel(hdc, 1);
|
|
}
|
|
|
|
/**
|
|
* @param hdc - drawing context.
|
|
*/
|
|
void CSplitter::DrawSplitterBar(HDC hdc) const
|
|
{
|
|
_ASSERTE(m_hwnd != NULL);
|
|
RECT rect;
|
|
GetSplitterBarRect(rect);
|
|
_ASSERTE(g_pResManager != NULL);
|
|
FillRect(hdc, &rect, g_pResManager->m_hbrButtonFaceBrush);
|
|
}
|
|
|
|
/**
|
|
* @param iPanel - panel number.
|
|
* @param hwndPanel - panel window handle.
|
|
* @return true if panel index is valid.
|
|
*/
|
|
BOOL CSplitter::SetPanel(int iPanel, HWND hwndPanel)
|
|
{
|
|
_ASSERTE(m_hwnd != NULL);
|
|
if (iPanel >= 0 && iPanel < NUM_PANELS)
|
|
{
|
|
m_arrPanels[iPanel] = hwndPanel;
|
|
if (hwndPanel)
|
|
{
|
|
RECT rect;
|
|
CalcPanelRect(iPanel, rect, true);
|
|
HWND hwndPanelParent = GetParent(hwndPanel);
|
|
MapWindowRect(m_hwnd, hwndPanelParent, &rect);
|
|
SetWindowPos(hwndPanel, NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
|
|
}
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @param iPanel - panel number.
|
|
* @param rect - panel rectangle.
|
|
* @param bShrinkRect - true if rectangle must be shrinked.
|
|
* @return true if panel index is valid.
|
|
*/
|
|
BOOL CSplitter::CalcPanelRect(int iPanel, RECT& rect, bool bShrinkRect) const
|
|
{
|
|
_ASSERTE(m_hwnd != NULL);
|
|
if (iPanel >= 0 && iPanel < NUM_PANELS)
|
|
{
|
|
GetClientRect(m_hwnd, &rect);
|
|
if (m_eDirection == SD_VERTICAL)
|
|
{
|
|
if (iPanel == 0)
|
|
rect.bottom = m_nSplitterPos;
|
|
else
|
|
{
|
|
int nBarSize = GetSystemMetrics(SM_CYFRAME);
|
|
int nSplitterBarEnd = m_nSplitterPos + nBarSize;
|
|
rect.top = rect.bottom > nSplitterBarEnd ? nSplitterBarEnd : rect.bottom;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (iPanel == 0)
|
|
rect.right = m_nSplitterPos;
|
|
else
|
|
{
|
|
int nBarSize = GetSystemMetrics(SM_CXFRAME);
|
|
int nSplitterBarEnd = m_nSplitterPos + nBarSize;
|
|
rect.left = rect.right > nSplitterBarEnd ? nSplitterBarEnd : rect.right;
|
|
}
|
|
}
|
|
if (bShrinkRect)
|
|
{
|
|
HWND hwndPanel = m_arrPanels[iPanel];
|
|
if (hwndPanel == NULL || (GetWindowLong(hwndPanel, GWL_EXSTYLE) & WS_EX_CLIENTEDGE) == 0)
|
|
{
|
|
int nCXEdge = GetSystemMetrics(SM_CXEDGE);
|
|
int nCYEdge = GetSystemMetrics(SM_CYEDGE);
|
|
if (rect.right - rect.left >= nCXEdge * 2)
|
|
{
|
|
rect.left += nCXEdge;
|
|
rect.right -= nCXEdge;
|
|
}
|
|
if (rect.bottom - rect.top >= nCYEdge * 2)
|
|
{
|
|
rect.top += nCYEdge;
|
|
rect.bottom -= nCYEdge;
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void CSplitter::UpdateLayout(void)
|
|
{
|
|
_ASSERTE(m_hwnd != NULL);
|
|
InvalidateRect(m_hwnd, NULL, FALSE);
|
|
HDWP hdwp = BeginDeferWindowPos(NUM_PANELS);
|
|
if (hdwp)
|
|
{
|
|
for (int iPanel = 0; iPanel < NUM_PANELS; ++iPanel)
|
|
{
|
|
HWND hwndPanel = m_arrPanels[iPanel];
|
|
if (hwndPanel)
|
|
{
|
|
RECT rect;
|
|
CalcPanelRect(iPanel, rect, true);
|
|
HWND hwndPanelParent = GetParent(hwndPanel);
|
|
MapWindowRect(m_hwnd, hwndPanelParent, &rect);
|
|
DeferWindowPos(hdwp, hwndPanel, NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
|
|
}
|
|
}
|
|
EndDeferWindowPos(hdwp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param nSplitterSize - new splitter size.
|
|
*/
|
|
void CSplitter::ResizeSplitter(int nSplitterSize)
|
|
{
|
|
int nEdgeType, nFrameType;
|
|
if (m_eDirection == SD_VERTICAL)
|
|
{
|
|
nEdgeType = SM_CYEDGE;
|
|
nFrameType = SM_CYFRAME;
|
|
}
|
|
else
|
|
{
|
|
nEdgeType = SM_CXEDGE;
|
|
nFrameType = SM_CXFRAME;
|
|
}
|
|
int nBarSize = GetSystemMetrics(nFrameType);
|
|
int nEdgeSize = GetSystemMetrics(nEdgeType) * 2;
|
|
nSplitterSize -= nBarSize;
|
|
nSplitterSize -= nEdgeSize;
|
|
if (nSplitterSize < 0)
|
|
nSplitterSize = 0;
|
|
if (m_bProportionalMode)
|
|
m_nSplitterPos = m_nSplitterSize > 0 ? MulDiv(m_nIdealPos, nSplitterSize, m_nSplitterSize) : nSplitterSize / 2;
|
|
if (m_nSplitterPos < nEdgeSize)
|
|
m_nSplitterPos = nEdgeSize;
|
|
if (m_nSplitterPos > nSplitterSize)
|
|
m_nSplitterPos = nSplitterSize;
|
|
UpdateLayout();
|
|
}
|
|
|
|
/**
|
|
* @param nSplitterPos - new splitter position.
|
|
*/
|
|
void CSplitter::SetSplitterPos(int nSplitterPos)
|
|
{
|
|
_ASSERTE(m_hwnd != NULL);
|
|
int nEdgeSize = GetSystemMetrics(m_eDirection == SD_VERTICAL ? SM_CYEDGE : SM_CXEDGE) * 2;
|
|
int nSplitterSize = CalcSplitterSize();
|
|
if (nSplitterPos < nEdgeSize)
|
|
nSplitterPos = nEdgeSize;
|
|
if (nSplitterPos > nSplitterSize)
|
|
nSplitterPos = nSplitterSize;
|
|
if (m_nSplitterPos != nSplitterPos)
|
|
{
|
|
m_nSplitterSize = nSplitterSize;
|
|
m_nIdealPos = m_nSplitterPos = nSplitterPos;
|
|
UpdateLayout();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param hwnd - window handle.
|
|
* @param uMsg - message identifier.
|
|
* @param wParam - first message parameter.
|
|
* @param lParam - second message parameter.
|
|
*/
|
|
LRESULT CALLBACK CSplitter::SplitterWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
int x, y, nSplitterPos, nSplitterSize;
|
|
POINT point;
|
|
HCURSOR hCursor;
|
|
PAINTSTRUCT ps;
|
|
HDC hdc;
|
|
DWORD dwResult;
|
|
|
|
CSplitter* _this = (CSplitter*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
_ASSERTE(_this != NULL);
|
|
switch(uMsg)
|
|
{
|
|
case WM_NCHITTEST:
|
|
if (GetCapture() == hwnd)
|
|
return HTCLIENT;
|
|
point.x = GET_X_LPARAM(lParam);
|
|
point.y = GET_Y_LPARAM(lParam);
|
|
ScreenToClient(hwnd, &point);
|
|
return (_this->HitTest(point) ? HTCLIENT : HTTRANSPARENT);
|
|
case WM_SETCURSOR:
|
|
_ASSERTE(g_pResManager != NULL);
|
|
dwResult = GetMessagePos();
|
|
point.x = GET_X_LPARAM(dwResult);
|
|
point.y = GET_Y_LPARAM(dwResult);
|
|
ScreenToClient(hwnd, &point);
|
|
if (_this->HitTest(point))
|
|
hCursor = _this->m_eDirection == SD_VERTICAL ? g_pResManager->m_hUpDownCursor : g_pResManager->m_hLeftRightCursor;
|
|
else
|
|
hCursor = g_pResManager->m_hArrowCursor;
|
|
if (hCursor)
|
|
{
|
|
SetCursor(hCursor);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
case WM_LBUTTONDOWN:
|
|
if (GetCapture() != hwnd)
|
|
{
|
|
x = GET_X_LPARAM(lParam);
|
|
y = GET_Y_LPARAM(lParam);
|
|
if (_this->HitTest(x, y))
|
|
SetCapture(hwnd);
|
|
}
|
|
return 0;
|
|
case WM_MOUSEMOVE:
|
|
if (GetCapture() == hwnd)
|
|
{
|
|
nSplitterPos = _this->m_eDirection == SD_VERTICAL ? GET_Y_LPARAM(lParam) : GET_X_LPARAM(lParam);
|
|
_this->SetSplitterPos(nSplitterPos);
|
|
}
|
|
return 0;
|
|
case WM_LBUTTONUP:
|
|
if (GetCapture() == hwnd)
|
|
ReleaseCapture();
|
|
return 0;
|
|
case WM_CAPTURECHANGED:
|
|
return 0;
|
|
case WM_ERASEBKGND:
|
|
return TRUE;
|
|
case WM_SIZE:
|
|
nSplitterSize = _this->m_eDirection == SD_VERTICAL ? GET_Y_LPARAM(lParam) : GET_X_LPARAM(lParam);
|
|
_this->ResizeSplitter(nSplitterSize);
|
|
return 0;
|
|
case WM_PAINT:
|
|
hdc = (HDC)wParam;
|
|
if (! hdc)
|
|
{
|
|
hdc = BeginPaint(hwnd, &ps);
|
|
if (hdc)
|
|
{
|
|
_this->DrawSplitter(hdc);
|
|
EndPaint(hwnd, &ps);
|
|
}
|
|
}
|
|
else
|
|
_this->DrawSplitter(hdc);
|
|
return 0;
|
|
case WM_PRINTCLIENT:
|
|
hdc = (HDC)wParam;
|
|
_this->DrawSplitter(hdc);
|
|
return 0;
|
|
case WM_GETDLGCODE:
|
|
return DLGC_STATIC;
|
|
default:
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param hwnd - splitter window handle.
|
|
*/
|
|
void CSplitter::Attach(HWND hwnd)
|
|
{
|
|
_ASSERTE(hwnd != NULL);
|
|
_ASSERTE(m_hwnd == NULL);
|
|
m_hwnd = hwnd;
|
|
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)this);
|
|
m_pfnOldSplitterWndProc = SubclassWindow(hwnd, SplitterWndProc);
|
|
ResetSplitterPos();
|
|
InvalidateRect(hwnd, NULL, TRUE);
|
|
}
|
|
|
|
void CSplitter::Detach(void)
|
|
{
|
|
if (m_pfnOldSplitterWndProc)
|
|
{
|
|
SubclassWindow(m_hwnd, m_pfnOldSplitterWndProc);
|
|
SetWindowLongPtr(m_hwnd, GWLP_USERDATA, NULL);
|
|
ClearSplitterPos();
|
|
ClearPanels();
|
|
InvalidateRect(m_hwnd, NULL, TRUE);
|
|
m_pfnOldSplitterWndProc = NULL;
|
|
m_hwnd = NULL;
|
|
}
|
|
}
|
|
|
|
void CSplitter::ClearSplitterPos(void)
|
|
{
|
|
m_nIdealPos = m_nSplitterPos = m_nSplitterSize = 0;
|
|
}
|
|
|
|
void CSplitter::ClearPanels(void)
|
|
{
|
|
m_arrPanels[0] = NULL;
|
|
m_arrPanels[1] = NULL;
|
|
}
|
|
|
|
/**
|
|
* @return splitter size value.
|
|
*/
|
|
int CSplitter::CalcSplitterSize(void) const
|
|
{
|
|
_ASSERTE(m_hwnd != NULL);
|
|
RECT rect;
|
|
GetClientRect(m_hwnd, &rect);
|
|
int nSplitterSize, nEdgeType, nFrameType;
|
|
if (m_eDirection == SD_VERTICAL)
|
|
{
|
|
nSplitterSize = rect.bottom;
|
|
nEdgeType = SM_CYEDGE;
|
|
nFrameType = SM_CYFRAME;
|
|
}
|
|
else
|
|
{
|
|
nSplitterSize = rect.right;
|
|
nEdgeType = SM_CXEDGE;
|
|
nFrameType = SM_CXFRAME;
|
|
}
|
|
int nEdgeSize = GetSystemMetrics(nEdgeType) * 2;
|
|
int nBarSize = GetSystemMetrics(nFrameType);
|
|
nSplitterSize -= nBarSize;
|
|
nSplitterSize -= nEdgeSize;
|
|
if (nSplitterSize < 0)
|
|
nSplitterSize = 0;
|
|
return nSplitterSize;
|
|
}
|