You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1765 lines
49 KiB
1765 lines
49 KiB
//---------------------------------------------------------------------------------------
|
|
// File : Pager.cpp
|
|
// Description :
|
|
// This file implements the pager control
|
|
//---------------------------------------------------------------------------------------
|
|
#include "ctlspriv.h"
|
|
#include "pager.h"
|
|
|
|
#define MINBUTTONSIZE 12
|
|
|
|
//Timer Flags
|
|
#define PGT_SCROLL 1
|
|
|
|
void DrawScrollArrow(HDC hdc, LPRECT lprc, WORD wControlState);
|
|
|
|
//Public Functions
|
|
//---------------------------------------------------------------------------------------
|
|
extern "C" {
|
|
|
|
//This function registers the pager window class
|
|
BOOL InitPager(HINSTANCE hinst)
|
|
{
|
|
WNDCLASS wc;
|
|
TraceMsg(TF_PAGER, "Init Pager");
|
|
|
|
wc.lpfnWndProc = CPager::PagerWndProc;
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hIcon = NULL;
|
|
wc.lpszMenuName = NULL;
|
|
wc.hInstance = hinst;
|
|
wc.lpszClassName = WC_PAGESCROLLER;
|
|
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); // NULL;
|
|
wc.style = CS_GLOBALCLASS;
|
|
wc.cbWndExtra = sizeof(LPVOID);
|
|
wc.cbClsExtra = 0;
|
|
|
|
return (RegisterClass(&wc) || (GetLastError() == ERROR_CLASS_ALREADY_EXISTS));
|
|
}
|
|
|
|
|
|
}; // extern "C"
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
CPager::CPager()
|
|
{
|
|
_clrBk = g_clrBtnFace;
|
|
|
|
//Initialize Static Members
|
|
_iButtonSize = (int) g_cxScrollbar * 3 / 4;
|
|
if (_iButtonSize < MINBUTTONSIZE) {
|
|
_iButtonSize = MINBUTTONSIZE;
|
|
}
|
|
|
|
_ptLastMove.x = -1;
|
|
_ptLastMove.y = -1;
|
|
|
|
_cLinesPerTimeout = 0;
|
|
_cPixelsPerLine = 0;
|
|
_cTimeout = GetDoubleClickTime() / 8;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// Static Pager Window Procedure
|
|
|
|
|
|
LRESULT CPager::PagerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CPager *pp = (CPager*)GetWindowPtr(hwnd, 0);
|
|
if (uMsg == WM_CREATE) {
|
|
ASSERT(!pp);
|
|
pp = new CPager();
|
|
if (!pp)
|
|
return 0L;
|
|
}
|
|
|
|
if (pp) {
|
|
return pp->v_WndProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
//---------------------------------------------------------------------------------------
|
|
LRESULT CPager::PagerDragCallback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CPager *pp = (CPager*)GetWindowPtr(hwnd, 0);
|
|
|
|
if (pp) {
|
|
return pp->_DragCallback(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// CControl Class Implementation
|
|
//---------------------------------------------------------------------------------------
|
|
|
|
|
|
LRESULT CControl::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LRESULT lres = 0;
|
|
|
|
if (uMsg == WM_THEMECHANGED) // Check for theme changes
|
|
{
|
|
if (_hTheme)
|
|
CloseThemeData(_hTheme);
|
|
|
|
_hTheme = OpenThemeData(ci.hwnd, GetThemeClass());
|
|
|
|
InvalidateRect(ci.hwnd, NULL, TRUE);
|
|
}
|
|
|
|
switch (uMsg) {
|
|
|
|
case WM_CREATE:
|
|
SetWindowPtr(hwnd, 0, this);
|
|
CIInitialize(&ci, hwnd, (CREATESTRUCT*)lParam);
|
|
|
|
_hTheme = OpenThemeData(ci.hwnd, GetThemeClass());
|
|
|
|
return v_OnCreate();
|
|
|
|
case WM_NCCALCSIZE:
|
|
if (v_OnNCCalcSize(wParam, lParam, &lres))
|
|
break;
|
|
goto DoDefault;
|
|
|
|
case WM_SIZE:
|
|
v_OnSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
|
break;
|
|
|
|
case WM_NOTIFYFORMAT:
|
|
return CIHandleNotifyFormat(&ci, lParam);
|
|
|
|
case WM_NOTIFY:
|
|
return v_OnNotify(wParam, lParam);
|
|
|
|
case WM_STYLECHANGED:
|
|
v_OnStyleChanged(wParam, lParam);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
return v_OnCommand(wParam, lParam);
|
|
|
|
case WM_NCPAINT:
|
|
v_OnNCPaint();
|
|
goto DoDefault;
|
|
|
|
case WM_PRINTCLIENT:
|
|
case WM_PAINT:
|
|
_OnPaint((HDC)wParam);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
SetWindowLongPtr(hwnd, 0, 0);
|
|
|
|
if (_hTheme)
|
|
CloseThemeData(_hTheme);
|
|
|
|
delete this;
|
|
break;
|
|
|
|
case TB_SETPARENT:
|
|
{
|
|
HWND hwndOld = ci.hwndParent;
|
|
|
|
ci.hwndParent = (HWND)wParam;
|
|
return (LRESULT)hwndOld;
|
|
}
|
|
|
|
|
|
default:
|
|
if (CCWndProc(&ci, uMsg, wParam, lParam, &lres))
|
|
return lres;
|
|
DoDefault:
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
return lres;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
BOOL CControl::v_OnNCCalcSize(WPARAM wParam, LPARAM lParam, LRESULT *plres)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
DWORD CControl::v_OnStyleChanged(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LPSTYLESTRUCT lpss = (LPSTYLESTRUCT) lParam;
|
|
DWORD dwChanged = 0;
|
|
if (wParam == GWL_STYLE) {
|
|
ci.style = lpss->styleNew;
|
|
|
|
dwChanged = (lpss->styleOld ^ lpss->styleNew);
|
|
} else if (wParam == GWL_EXSTYLE) {
|
|
//
|
|
// Save the new ex-style bits
|
|
//
|
|
dwChanged = (lpss->styleOld ^ lpss->styleNew);
|
|
ci.dwExStyle = lpss->styleNew;
|
|
}
|
|
|
|
TraceMsg(TF_PAGER, "cctl.v_osc: style=%x ret dwChged=%x", ci.style, dwChanged);
|
|
return dwChanged;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
void CControl::_OnPaint(HDC hdc)
|
|
{
|
|
if (hdc)
|
|
{
|
|
v_OnPaint(hdc);
|
|
}
|
|
else
|
|
{
|
|
PAINTSTRUCT ps;
|
|
hdc = BeginPaint(ci.hwnd, &ps);
|
|
v_OnPaint(hdc);
|
|
EndPaint(ci.hwnd, &ps);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// CPager Class Implementation
|
|
//---------------------------------------------------------------------------------------
|
|
inline int CPager::_GetButtonSize()
|
|
{
|
|
return _iButtonSize;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
|
|
LRESULT CPager::_DragCallback(HWND hwnd, UINT code, WPARAM wp, LPARAM lp)
|
|
{
|
|
LRESULT lres = -1;
|
|
switch (code)
|
|
{
|
|
case DPX_DRAGHIT:
|
|
if (lp)
|
|
{
|
|
POINT pt;
|
|
int iButton;
|
|
pt.x = ((POINTL *)lp)->x;
|
|
pt.y = ((POINTL *)lp)->y;
|
|
|
|
MapWindowPoints(NULL, ci.hwnd, &pt, 1);
|
|
|
|
iButton = _HitTest(pt.x, pt.y);
|
|
|
|
if (iButton >= 0)
|
|
{
|
|
if(!_fTimerSet)
|
|
{
|
|
_fTimerSet = TRUE;
|
|
_iButtonTrack = iButton;
|
|
SetTimer(ci.hwnd, PGT_SCROLL, _cTimeout, NULL);
|
|
}
|
|
|
|
} else {
|
|
_KillTimer();
|
|
_iButtonTrack = -1;
|
|
}
|
|
}
|
|
else
|
|
lres = -1;
|
|
break;
|
|
|
|
case DPX_LEAVE:
|
|
_KillTimer();
|
|
_iButtonTrack = -1;
|
|
break;
|
|
|
|
default:
|
|
lres = -1;
|
|
break;
|
|
}
|
|
return lres;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
void CPager::_NeedScrollbars(RECT rc)
|
|
{
|
|
int parentheight;
|
|
int childheight;
|
|
POINT ptPos = _ptPos;
|
|
|
|
if( ci.style & PGS_HORZ ) {
|
|
FlipRect(&rc);
|
|
FlipPoint(&ptPos);
|
|
}
|
|
|
|
//Get Parent Window height
|
|
parentheight = RECTHEIGHT(rc);
|
|
|
|
//Get Child Window height
|
|
rc = _rcChildIdeal;
|
|
if (ci.style & PGS_HORZ ) {
|
|
FlipRect(&rc);
|
|
}
|
|
|
|
childheight = RECTHEIGHT(rc);
|
|
|
|
TraceMsg(TF_PAGER, "cps.nsb: cyChild=%d cyParent=%d _yPos=%d", childheight, parentheight, ptPos.y);
|
|
|
|
if (childheight < parentheight )
|
|
{
|
|
ptPos.y = 0;
|
|
}
|
|
|
|
int iButton = _HitTestCursor();
|
|
//See if we need top scrollbar
|
|
if (ptPos.y > 0 ) {
|
|
|
|
// if this button is the one that is hot tracked and the style is not PGS_AUTOSCROLL
|
|
// then we set the state to PGF_HOT otherwise the state is set to PGF_NORMAL
|
|
_dwState[PGB_TOPORLEFT] |= PGF_NORMAL;
|
|
_dwState[PGB_TOPORLEFT] &= ~PGF_GRAYED;
|
|
|
|
} else {
|
|
if (!(ci.style & PGS_AUTOSCROLL) && (iButton == PGB_TOPORLEFT || _iButtonTrack == PGB_TOPORLEFT)) {
|
|
_dwState[PGB_TOPORLEFT] |= PGF_GRAYED;
|
|
} else {
|
|
_dwState[PGB_TOPORLEFT] = PGF_INVISIBLE;
|
|
}
|
|
}
|
|
|
|
if (_dwState[PGB_TOPORLEFT] != PGF_INVISIBLE)
|
|
{
|
|
parentheight -= _GetButtonSize();
|
|
}
|
|
|
|
//See if we need botton scrollbar
|
|
if ((childheight - ptPos.y) > parentheight ) {
|
|
//We need botton scroll bar
|
|
|
|
// if this button is the one that is hot tracked and the style is not PGS_AUTOSCROLL
|
|
// then we set the state to PGF_HOT otherwise the state is set to PGF_NORMAL
|
|
_dwState[PGB_BOTTOMORRIGHT] |= PGF_NORMAL;
|
|
_dwState[PGB_BOTTOMORRIGHT] &= ~PGF_GRAYED;
|
|
|
|
} else {
|
|
|
|
if (!(ci.style & PGS_AUTOSCROLL) && (iButton == PGB_BOTTOMORRIGHT || _iButtonTrack == PGB_BOTTOMORRIGHT)) {
|
|
_dwState[PGB_BOTTOMORRIGHT] |= PGF_GRAYED;
|
|
} else {
|
|
_dwState[PGB_BOTTOMORRIGHT] = PGF_INVISIBLE;
|
|
}
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------------------
|
|
BOOL CPager::v_OnNCCalcSize(WPARAM wParam, LPARAM lParam, LRESULT *plres)
|
|
{
|
|
*plres = DefWindowProc(ci.hwnd, WM_NCCALCSIZE, wParam, lParam ) ;
|
|
if (wParam) {
|
|
BOOL bHorzMirror = ((ci.dwExStyle & RTL_MIRRORED_WINDOW) && (ci.style & PGS_HORZ));
|
|
DWORD dwStateOld[2];
|
|
NCCALCSIZE_PARAMS* pnp = (NCCALCSIZE_PARAMS*)lParam;
|
|
_rcDefClient = pnp->rgrc[0];
|
|
InflateRect(&_rcDefClient, -_iBorder, -_iBorder);
|
|
_GetChildSize();
|
|
|
|
dwStateOld[0] = _dwState[0];
|
|
dwStateOld[1] = _dwState[1];
|
|
_NeedScrollbars(pnp->rgrc[0]);
|
|
|
|
// invalidate only if something has changed to force a new size
|
|
if ((dwStateOld[0] != _dwState[0] && (dwStateOld[0] == PGF_INVISIBLE || _dwState[0] == PGF_INVISIBLE)) ||
|
|
(dwStateOld[1] != _dwState[1] && (dwStateOld[1] == PGF_INVISIBLE || _dwState[1] == PGF_INVISIBLE))
|
|
) {
|
|
RedrawWindow(ci.hwnd, NULL,NULL,RDW_INVALIDATE|RDW_ERASE);
|
|
}
|
|
|
|
// Check and change for horizontal mode
|
|
if( ci.style & PGS_HORZ ) {
|
|
FlipRect(&(pnp->rgrc[0]));
|
|
}
|
|
|
|
if( _dwState[PGB_TOPORLEFT] != PGF_INVISIBLE ) {
|
|
//
|
|
// Check for RTL mirrored window
|
|
//
|
|
if (bHorzMirror)
|
|
pnp->rgrc[0].bottom -= _GetButtonSize();
|
|
else
|
|
pnp->rgrc[0].top += _GetButtonSize();
|
|
} else
|
|
pnp->rgrc[0].top += _iBorder;
|
|
|
|
if( _dwState[PGB_BOTTOMORRIGHT] != PGF_INVISIBLE ) {
|
|
//
|
|
// Check for RTL mirrored window
|
|
//
|
|
if (bHorzMirror)
|
|
pnp->rgrc[0].top += _GetButtonSize();
|
|
else
|
|
pnp->rgrc[0].bottom -= _GetButtonSize();
|
|
} else
|
|
pnp->rgrc[0].bottom -= _iBorder;
|
|
|
|
if (pnp->rgrc[0].bottom < pnp->rgrc[0].top)
|
|
pnp->rgrc[0].bottom = pnp->rgrc[0].top;
|
|
|
|
//Change back
|
|
if( ci.style & PGS_HORZ ) {
|
|
FlipRect(&(pnp->rgrc[0]));
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int CPager::_HitTestCursor()
|
|
{
|
|
POINT pt;
|
|
GetCursorPos(&pt);
|
|
return _HitTestScreen(&pt);
|
|
}
|
|
|
|
int CPager::_HitTestScreen(POINT* ppt)
|
|
{
|
|
RECT rc, rc1;
|
|
GetWindowRect(ci.hwnd, &rc);
|
|
|
|
if (!PtInRect(&rc, *ppt)) {
|
|
return -1;
|
|
}
|
|
//Get the button Rects;
|
|
rc = _GetButtonRect(PGB_TOPORLEFT);
|
|
rc1 = _GetButtonRect(PGB_BOTTOMORRIGHT);
|
|
|
|
|
|
if (PtInRect(&rc, *ppt)) {
|
|
return (_dwState[PGB_TOPORLEFT] != PGF_INVISIBLE ? PGB_TOPORLEFT : -1);
|
|
}else if (PtInRect(&rc1, *ppt)) {
|
|
return (_dwState[PGB_BOTTOMORRIGHT] != PGF_INVISIBLE ? PGB_BOTTOMORRIGHT : -1);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
int CPager::_HitTest(int x, int y)
|
|
{
|
|
POINT pt;
|
|
|
|
pt.x = x;
|
|
pt.y = y;
|
|
|
|
ClientToScreen(ci.hwnd, &pt);
|
|
return _HitTestScreen(&pt);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// _DrawBlank is theme aware
|
|
void CPager::_DrawBlank(HDC hdc, int button)
|
|
{
|
|
RECT rc;
|
|
UINT uFlags = 0;
|
|
int iHeight;
|
|
BOOL fRelDC = FALSE;
|
|
|
|
if (!_hTheme)
|
|
{
|
|
if (hdc == NULL) {
|
|
hdc = GetWindowDC(ci.hwnd);
|
|
fRelDC = TRUE;
|
|
}
|
|
|
|
GetWindowRect(ci.hwnd, &rc);
|
|
MapWindowRect(NULL, ci.hwnd, &rc);
|
|
|
|
// client to window coordinates
|
|
OffsetRect(&rc, -rc.left, -rc.top);
|
|
|
|
//Check for horizontal mode
|
|
if( ci.style & PGS_HORZ ) {
|
|
FlipRect(&rc);
|
|
}
|
|
|
|
iHeight = _dwState[button] == PGF_INVISIBLE ? _iBorder : _GetButtonSize();
|
|
switch(button) {
|
|
case PGB_TOPORLEFT:
|
|
rc.bottom = rc.top + iHeight;
|
|
break;
|
|
|
|
case PGB_BOTTOMORRIGHT:
|
|
rc.top = rc.bottom - iHeight;
|
|
break;
|
|
}
|
|
|
|
if( ci.style & PGS_HORZ ) {
|
|
FlipRect(&rc);
|
|
}
|
|
|
|
FillRectClr(hdc, &rc, _clrBk);
|
|
if (fRelDC)
|
|
ReleaseDC(ci.hwnd, hdc);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// _DrawButton is theme aware
|
|
void CPager::_DrawButton(HDC hdc, int button)
|
|
{
|
|
RECT rc;
|
|
UINT uFlags = 0;
|
|
BOOL fRelDC = FALSE;
|
|
GetWindowRect(ci.hwnd, &rc);
|
|
MapWindowRect(NULL, ci.hwnd, &rc);
|
|
int state = _dwState[button];
|
|
|
|
int iPartId;
|
|
int iStateId;
|
|
|
|
if (state == PGF_INVISIBLE)
|
|
return;
|
|
|
|
if (hdc == NULL) {
|
|
hdc = GetWindowDC(ci.hwnd);
|
|
fRelDC = TRUE;
|
|
}
|
|
|
|
// All states for all Pager parts are the same enumeration value for
|
|
// their meaning. Use the UP part version for all parts
|
|
|
|
iStateId = UPS_NORMAL;
|
|
|
|
if (state & PGF_GRAYED ) {
|
|
uFlags |= DCHF_INACTIVE;
|
|
|
|
iStateId = UPS_DISABLED;
|
|
|
|
} else if (state & PGF_DEPRESSED ) {
|
|
uFlags |= DCHF_PUSHED;
|
|
|
|
iStateId = UPS_PRESSED;
|
|
|
|
} else if (state & PGF_HOT ) {
|
|
uFlags |= DCHF_HOT;
|
|
|
|
iStateId = UPS_HOT;
|
|
}
|
|
|
|
// screen to window coordinates
|
|
OffsetRect(&rc, -rc.left, -rc.top);
|
|
|
|
//Check for horizontal mode
|
|
if( ci.style & PGS_HORZ ) {
|
|
FlipRect(&rc);
|
|
}
|
|
|
|
if( ci.style & PGS_HORZ )
|
|
uFlags |= DCHF_HORIZONTAL;
|
|
|
|
if (button == PGB_BOTTOMORRIGHT)
|
|
uFlags |= DCHF_FLIPPED;
|
|
|
|
switch(button) {
|
|
case PGB_TOPORLEFT:
|
|
rc.bottom = rc.top + _GetButtonSize();
|
|
rc.left += _iBorder;
|
|
rc.right -= _iBorder;
|
|
|
|
if (_hTheme)
|
|
{
|
|
iPartId = (ci.style & PGS_HORZ) ? PGRP_DOWNHORZ : PGRP_DOWN;
|
|
rc.bottom += 1;
|
|
}
|
|
break;
|
|
|
|
case PGB_BOTTOMORRIGHT:
|
|
rc.top = rc.bottom - _GetButtonSize();
|
|
rc.left += _iBorder;
|
|
rc.right -= _iBorder;
|
|
|
|
if (_hTheme)
|
|
{
|
|
iPartId = (ci.style & PGS_HORZ) ? PGRP_UPHORZ : PGRP_UP;
|
|
rc.top -= 1;
|
|
}
|
|
break;
|
|
default:
|
|
if (_hTheme)
|
|
iPartId = PGRP_UP;
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
if( ci.style & PGS_HORZ ) {
|
|
FlipRect(&rc);
|
|
}
|
|
|
|
if (_hTheme)
|
|
{
|
|
DrawThemeBackground(_hTheme, hdc, iPartId, iStateId, &rc, 0);
|
|
}
|
|
else
|
|
{
|
|
SetBkColor(hdc, _clrBk);
|
|
DrawScrollArrow(hdc, &rc, uFlags, CLR_INVALID);
|
|
}
|
|
|
|
if (fRelDC)
|
|
ReleaseDC(ci.hwnd, hdc);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
void CPager::v_OnNCPaint()
|
|
{
|
|
HDC hdc = GetWindowDC(ci.hwnd);
|
|
_DrawBlank(hdc, PGB_TOPORLEFT);
|
|
_DrawButton(hdc, PGB_TOPORLEFT);
|
|
|
|
_DrawBlank(hdc, PGB_BOTTOMORRIGHT);
|
|
_DrawButton(hdc, PGB_BOTTOMORRIGHT);
|
|
ReleaseDC(ci.hwnd, hdc);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
void CPager::v_OnPaint(HDC hdc)
|
|
{
|
|
}
|
|
//---------------------------------------------------------------------------------------
|
|
BOOL CPager::_OnPrint(HDC hdc, UINT uFlags)
|
|
{
|
|
//We'll be partying with the hdc in this function so save it.
|
|
int iDC = SaveDC(hdc);
|
|
|
|
//Print only the Non Client Area.
|
|
if (uFlags & PRF_NONCLIENT) {
|
|
int cx = 0;
|
|
int cy = 0;
|
|
RECT rc;
|
|
|
|
|
|
//Draw the top/left button
|
|
_DrawBlank(hdc, PGB_TOPORLEFT);
|
|
_DrawButton(hdc, PGB_TOPORLEFT);
|
|
|
|
//Draw the bottom/left button
|
|
_DrawBlank(hdc, PGB_BOTTOMORRIGHT);
|
|
_DrawButton(hdc, PGB_BOTTOMORRIGHT);
|
|
|
|
//Is the top button visible
|
|
if (_dwState[PGB_TOPORLEFT] != PGF_INVISIBLE) {
|
|
//yes, find the space taken
|
|
if ( ci.style & PGS_HORZ ) {
|
|
cx = _GetButtonSize();
|
|
}else {
|
|
cy = _GetButtonSize();
|
|
}
|
|
|
|
}
|
|
//Restrict the child draw area to our client area
|
|
GetClientRect(ci.hwnd, &rc);
|
|
IntersectClipRect(hdc, cx, cy, cx + RECTWIDTH(rc), cy + RECTHEIGHT(rc));
|
|
|
|
//Since We have drawn the non client area, Nuke the PRF_NONCLIENT flag
|
|
uFlags &= ~PRF_NONCLIENT;
|
|
|
|
}
|
|
|
|
//Pass it to the def window proc for default processing
|
|
DefWindowProc(ci.hwnd, WM_PRINT, (WPARAM)hdc, (LPARAM)uFlags);
|
|
//Restore the saved DC
|
|
RestoreDC(hdc, iDC);
|
|
return TRUE;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
LRESULT CPager::v_OnCommand(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
// forward to parent
|
|
return SendMessage(ci.hwndParent, WM_COMMAND, wParam, lParam);
|
|
}
|
|
//---------------------------------------------------------------------------------------
|
|
LRESULT CPager::v_OnNotify(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
// forward to parent
|
|
LPNMHDR lpNmhdr = (LPNMHDR)lParam;
|
|
|
|
return SendNotifyEx(ci.hwndParent, (HWND) -1,
|
|
lpNmhdr->code, lpNmhdr, ci.bUnicode);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
DWORD CPager::v_OnStyleChanged(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
DWORD dwChanged = CControl::v_OnStyleChanged(wParam, lParam);
|
|
|
|
if (dwChanged & PGS_DRAGNDROP) {
|
|
if ((ci.style & PGS_DRAGNDROP) && !_hDragProxy) {
|
|
|
|
_hDragProxy = CreateDragProxy(ci.hwnd, PagerDragCallback, TRUE);
|
|
|
|
} else if (! (ci.style & PGS_DRAGNDROP) && _hDragProxy) {
|
|
|
|
DestroyDragProxy(_hDragProxy);
|
|
}
|
|
}
|
|
|
|
if (dwChanged)
|
|
CCInvalidateFrame(ci.hwnd); // SWP_FRAMECHANGED etc.
|
|
return dwChanged;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
|
|
LRESULT CPager::v_OnCreate()
|
|
{
|
|
if (ci.style & PGS_DRAGNDROP)
|
|
_hDragProxy = CreateDragProxy(ci.hwnd, PagerDragCallback, TRUE);
|
|
|
|
return TRUE;
|
|
}
|
|
//---------------------------------------------------------------------------------------
|
|
void CPager::_GetChildSize()
|
|
{
|
|
if (_hwndChild) {
|
|
|
|
RECT rc;
|
|
NMPGCALCSIZE nmpgcalcsize;
|
|
int width , height;
|
|
rc = _rcDefClient;
|
|
|
|
if( ci.style & PGS_HORZ ) {
|
|
nmpgcalcsize.dwFlag = PGF_CALCWIDTH;
|
|
} else {
|
|
nmpgcalcsize.dwFlag = PGF_CALCHEIGHT;
|
|
}
|
|
nmpgcalcsize.iWidth = RECTWIDTH(rc); // pager width
|
|
nmpgcalcsize.iHeight = RECTHEIGHT(rc); // best-guess for child
|
|
|
|
CCSendNotify(&ci, PGN_CALCSIZE, &nmpgcalcsize.hdr);
|
|
|
|
if( ci.style & PGS_HORZ ) {
|
|
width = nmpgcalcsize.iWidth;
|
|
height = RECTHEIGHT(rc);
|
|
} else {
|
|
width = RECTWIDTH(rc);
|
|
height = nmpgcalcsize.iHeight;
|
|
}
|
|
|
|
GetWindowRect(_hwndChild, &rc);
|
|
MapWindowRect(NULL, ci.hwnd, &rc);
|
|
if( ci.style & PGS_HORZ ) {
|
|
rc.top = _iBorder;
|
|
} else {
|
|
rc.left = _iBorder;
|
|
}
|
|
rc.right = rc.left + width;
|
|
rc.bottom = rc.top + height;
|
|
_rcChildIdeal = rc;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
void CPager::v_OnSize(int x, int y)
|
|
{
|
|
if (_hwndChild) {
|
|
RECT rc = _rcChildIdeal;
|
|
_SetChildPos(&rc, 0); // SetWindowPos
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
//*** _SetChildPos -- SetWindowPos of child, w/ validation
|
|
// NOTES
|
|
// 'validation' means in sane state -- min size, and not off end.
|
|
// WARNING: we don't update *prcChild.
|
|
void CPager::_SetChildPos(IN RECT * prcChild, UINT uFlags)
|
|
{
|
|
POINT ptPos = _ptPos;
|
|
RECT rcChild = *prcChild;
|
|
RECT rcPager;
|
|
|
|
ASSERT(!(uFlags & SWP_NOMOVE)); // won't work
|
|
|
|
ASSERT(IS_VALID_HANDLE(_hwndChild, WND));
|
|
|
|
rcPager = _rcDefClient;
|
|
|
|
|
|
if ( ci.style & PGS_HORZ ) {
|
|
FlipPoint(&ptPos);
|
|
FlipRect(&rcChild);
|
|
FlipRect(&rcPager);
|
|
}
|
|
|
|
|
|
int yNew = ptPos.y;
|
|
|
|
if (RECTHEIGHT(rcChild) < RECTHEIGHT(rcPager)) {
|
|
// force to min height
|
|
|
|
// this handles the case where: i have an ISFBand that fills up the
|
|
// whole pager, i stretch the pager width, and the ISFBand reformats
|
|
// to take less height, so it shrinks its height and ends up shorter
|
|
// than the pager.
|
|
TraceMsg(TF_PAGER, "cps.s: h=%d h'=%d", RECTHEIGHT(rcChild), RECTHEIGHT(rcPager));
|
|
ASSERT(!(uFlags & SWP_NOSIZE)); // won't work
|
|
rcChild.bottom = rcChild.top + RECTHEIGHT(rcPager);
|
|
yNew = 0;
|
|
}
|
|
|
|
// Maximum we can scroll is child height minus pager height.
|
|
// Here rcPager also includes scrollbutton so we need to add that also
|
|
/*
|
|
___________ Button Width
|
|
|
|
|
V ---------------- Max we can scroll (yMax)
|
|
__ |
|
|
/ \V
|
|
- ---------pager-----------
|
|
| |-------------------------|--------------------------------
|
|
| || | |
|
|
| || child | |
|
|
| |-------------------------|--------------------------------
|
|
- -------------------------
|
|
\/\/
|
|
Border | |
|
|
<----- -------------->We need to take care of this gap.
|
|
\-----------------------------/
|
|
^
|
|
|______ RECTHEIGHT(rcChild) - RECTHEIGHT(rcPager)
|
|
|
|
rcPager
|
|
We need to add the difference between the button size and border to
|
|
*/
|
|
int yMax = RECTHEIGHT(rcChild) - RECTHEIGHT(rcPager) + (_GetButtonSize() - _iBorder);
|
|
|
|
// make sure we don't end up off the top/end, and we always show
|
|
// at least 1 page worth (if we have that much)
|
|
// n.b. pager can override client's policy
|
|
if (yNew < 0) {
|
|
// 1st page
|
|
yNew = 0;
|
|
} else if (yNew > yMax) {
|
|
// last page
|
|
yNew = yMax;
|
|
}
|
|
|
|
int yOffset = yNew;
|
|
|
|
// When the top button is grayed we do not want to display our child away from the button .
|
|
// it should be drawn right below the button. For this we tweak the position of the child window.
|
|
|
|
//Check for the condition of grayed top button in which case we need to set position even behind
|
|
// so that the child window falls below the grayed button
|
|
if( _dwState[PGB_TOPORLEFT] & PGF_GRAYED )
|
|
{
|
|
yOffset += (_GetButtonSize() - _iBorder);
|
|
}
|
|
|
|
//yOffset is the tweaked value. Its just for making the child window to appear below the grayed button
|
|
|
|
OffsetRect(&rcChild, 0, -yOffset - rcChild.top);
|
|
|
|
//yNew is the actual logical positon of the window .
|
|
ptPos.y = yNew;
|
|
|
|
|
|
if (ci.style & PGS_HORZ) {
|
|
// restore for copy and SWP
|
|
FlipPoint(&ptPos);
|
|
FlipRect(&rcChild);
|
|
}
|
|
|
|
_ptPos = ptPos;
|
|
|
|
SetWindowPos(_hwndChild, NULL, rcChild.left, rcChild.top, RECTWIDTH(rcChild), RECTHEIGHT(rcChild), uFlags);
|
|
|
|
return;
|
|
}
|
|
//---------------------------------------------------------------------------------------
|
|
//*** PGFToPGNDirection -- convert PGB_TOPORLEFT/btmorright to up/down/left/right
|
|
// NOTES
|
|
// REARCHIT maybe PGN_* should we just take the PGF flags?
|
|
DWORD CPager::_PGFToPGNDirection(DWORD dwDir)
|
|
{
|
|
ASSERT(dwDir == PGB_TOPORLEFT || dwDir == PGB_BOTTOMORRIGHT);
|
|
if (ci.style & PGS_HORZ) {
|
|
return (dwDir == PGB_TOPORLEFT) ? PGF_SCROLLLEFT : PGF_SCROLLRIGHT;
|
|
}
|
|
else {
|
|
return (dwDir == PGB_TOPORLEFT) ? PGF_SCROLLUP : PGF_SCROLLDOWN;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------------------
|
|
void CPager::_Scroll(DWORD dwDirection)
|
|
{
|
|
RECT rc;
|
|
NMPGSCROLL nmpgscroll;
|
|
int iXoffset =0, iYoffset=0;
|
|
WORD fwKeys = 0;
|
|
int iNewPos ;
|
|
|
|
// if grayed, you can't scroll.
|
|
if (_dwState[dwDirection] & PGF_GRAYED)
|
|
return;
|
|
|
|
if (GetKeyState(VK_CONTROL) < 0 )
|
|
fwKeys |= PGK_CONTROL;
|
|
|
|
if (GetKeyState(VK_SHIFT) < 0 )
|
|
fwKeys |= PGK_SHIFT;
|
|
|
|
if (GetKeyState(VK_MENU) < 0 )
|
|
fwKeys |= PGK_MENU;
|
|
|
|
dwDirection = _PGFToPGNDirection(dwDirection);
|
|
|
|
// set some defaults
|
|
GetClientRect(ci.hwnd, &rc);
|
|
nmpgscroll.fwKeys = fwKeys;
|
|
nmpgscroll.rcParent = rc;
|
|
nmpgscroll.iXpos = _ptPos.x;
|
|
nmpgscroll.iYpos = _ptPos.y;
|
|
nmpgscroll.iDir = dwDirection;
|
|
|
|
int iScroll = (ci.style & PGS_HORZ) ? RECTWIDTH(rc) : RECTHEIGHT(rc);
|
|
if (_cLinesPerTimeout)
|
|
iScroll = _cLinesPerTimeout * _cPixelsPerLine;
|
|
|
|
nmpgscroll.iScroll = iScroll;
|
|
|
|
// let client override
|
|
CCSendNotify(&ci, PGN_SCROLL, &nmpgscroll.hdr);
|
|
|
|
// do it
|
|
switch (dwDirection)
|
|
{
|
|
case PGF_SCROLLDOWN:
|
|
iNewPos = _ptPos.y + nmpgscroll.iScroll;
|
|
break;
|
|
|
|
case PGF_SCROLLUP:
|
|
iNewPos = _ptPos.y - nmpgscroll.iScroll;
|
|
break;
|
|
|
|
case PGF_SCROLLRIGHT:
|
|
iNewPos = _ptPos.x + nmpgscroll.iScroll;
|
|
break;
|
|
|
|
case PGF_SCROLLLEFT:
|
|
iNewPos = _ptPos.x - nmpgscroll.iScroll;
|
|
break;
|
|
}
|
|
|
|
_OnSetPos(iNewPos);
|
|
|
|
}
|
|
//---------------------------------------------------------------------------------------
|
|
void CPager::_OnLButtonChange(UINT uMsg,LPARAM lParam)
|
|
{
|
|
POINT pt;
|
|
int iButton;
|
|
pt.x = GET_X_LPARAM(lParam);
|
|
pt.y = GET_Y_LPARAM(lParam);
|
|
|
|
iButton = _HitTest(pt.x, pt.y);
|
|
|
|
if( uMsg == WM_LBUTTONDOWN ) {
|
|
|
|
// Check the button is valid and is not grayed
|
|
// if it is grayed then dont do anything
|
|
if (iButton >= 0) {
|
|
SetCapture(ci.hwnd);
|
|
_fOwnsButtonDown = TRUE;
|
|
_iButtonTrack = iButton;
|
|
_dwState[iButton] |= PGF_DEPRESSED;
|
|
_DrawButton(NULL, iButton);
|
|
_Scroll(iButton);
|
|
SetTimer(ci.hwnd, PGT_SCROLL, _cTimeout * 4, NULL);
|
|
}
|
|
|
|
} else {
|
|
if (_iButtonTrack >= 0) {
|
|
_dwState[_iButtonTrack] &= ~PGF_DEPRESSED;
|
|
_DrawButton(NULL, _iButtonTrack);
|
|
_iButtonTrack = -1;
|
|
}
|
|
_KillTimer();
|
|
|
|
if (iButton < 0)
|
|
_OnMouseLeave();
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------------------
|
|
RECT CPager :: _GetButtonRect(int iButton)
|
|
{
|
|
RECT rc;
|
|
|
|
GetWindowRect(ci.hwnd, &rc);
|
|
|
|
if( ci.style & PGS_HORZ ) {
|
|
FlipRect(&rc);
|
|
}
|
|
|
|
//
|
|
// Mirror the rects if the parent is mirrored
|
|
//
|
|
if (((ci.dwExStyle & RTL_MIRRORED_WINDOW) && (ci.style & PGS_HORZ))) {
|
|
switch (iButton) {
|
|
case PGB_TOPORLEFT:
|
|
iButton = PGB_BOTTOMORRIGHT;
|
|
break;
|
|
|
|
case PGB_BOTTOMORRIGHT:
|
|
iButton = PGB_TOPORLEFT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch(iButton) {
|
|
case PGB_TOPORLEFT:
|
|
rc.bottom = rc.top + _GetButtonSize();
|
|
rc.left += _iBorder;
|
|
rc.right -= _iBorder;
|
|
break;
|
|
|
|
case PGB_BOTTOMORRIGHT:
|
|
rc.top = rc.bottom - _GetButtonSize();
|
|
rc.left += _iBorder;
|
|
rc.right -= _iBorder;
|
|
break;
|
|
}
|
|
|
|
if( ci.style & PGS_HORZ ) {
|
|
FlipRect(&rc);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
void CPager :: _OnMouseLeave()
|
|
{
|
|
//Whether we leave the window (WM_MOUSELEAVE) or Leave one of the scroll buttons (WM_MOUSEMOVE)
|
|
// We do the same thing.
|
|
|
|
// We are leaving the pager window.
|
|
if (GetCapture() == ci.hwnd) {
|
|
CCReleaseCapture(&ci);
|
|
}
|
|
|
|
// if we are tracking some button then release that mouse and that button
|
|
if (_iButtonTrack >= 0) {
|
|
_iButtonTrack = -1;
|
|
}
|
|
|
|
int iButton = _HitTest(_ptLastMove.x, _ptLastMove.y);
|
|
_OnHotItemChange(iButton, FALSE);
|
|
|
|
if (_dwState[PGB_TOPORLEFT] & (PGF_HOT | PGF_DEPRESSED)) {
|
|
_dwState[PGB_TOPORLEFT] &= ~(PGF_HOT | PGF_DEPRESSED);
|
|
_DrawButton(NULL, PGB_TOPORLEFT);
|
|
}
|
|
|
|
if (_dwState[PGB_BOTTOMORRIGHT] & (PGF_HOT | PGF_DEPRESSED)) {
|
|
_dwState[PGB_BOTTOMORRIGHT] &= ~(PGF_HOT | PGF_DEPRESSED);
|
|
_DrawButton(NULL, PGB_BOTTOMORRIGHT);
|
|
}
|
|
|
|
_KillTimer();
|
|
_fOwnsButtonDown = FALSE;
|
|
//If any of the button is in gray state then it needs to be removed.
|
|
if ((_dwState[PGB_TOPORLEFT] & PGF_GRAYED) || (_dwState[PGB_BOTTOMORRIGHT] & PGF_GRAYED)) {
|
|
//This forces a recalc for scrollbars and removes those that are not needed
|
|
CCInvalidateFrame(ci.hwnd);
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
void CPager::_OnMouseMove(WPARAM wParam, LPARAM lparam)
|
|
{
|
|
RECT rc;
|
|
POINT pt;
|
|
int iButton;
|
|
|
|
pt.x = GET_X_LPARAM(lparam);
|
|
pt.y = GET_Y_LPARAM(lparam);
|
|
|
|
// Ignore zero-mouse moves
|
|
if (pt.x == _ptLastMove.x && pt.y == _ptLastMove.y)
|
|
return;
|
|
|
|
_ptLastMove = pt;
|
|
iButton = _HitTest(pt.x, pt.y);
|
|
|
|
if (_iButtonTrack >= 0 )
|
|
{
|
|
|
|
if (_dwState[_iButtonTrack] != PGF_INVISIBLE)
|
|
{
|
|
//Some Button is pressed right now
|
|
ClientToScreen(ci.hwnd, &pt);
|
|
rc = _GetButtonRect(_iButtonTrack);
|
|
|
|
DWORD dwOldState = _dwState[_iButtonTrack];
|
|
if (PtInRect(&rc, pt))
|
|
{
|
|
_dwState[_iButtonTrack] |= PGF_DEPRESSED;
|
|
}
|
|
else
|
|
{
|
|
_dwState[_iButtonTrack] &= ~PGF_DEPRESSED;
|
|
}
|
|
|
|
if (dwOldState != _dwState[_iButtonTrack])
|
|
_DrawButton(NULL, _iButtonTrack);
|
|
}
|
|
|
|
// if we were tracking it, but the mouse is up and gone
|
|
if (GetCapture() == ci.hwnd && !((wParam & MK_LBUTTON) || (ci.style & PGS_AUTOSCROLL)) && iButton != _iButtonTrack)
|
|
_OnMouseLeave();
|
|
|
|
}
|
|
else
|
|
{
|
|
// No button is pressed .
|
|
if( iButton >= 0 )
|
|
{
|
|
|
|
//Capture the mouse so that we can keep track of when the mouse is leaving our button
|
|
SetCapture(ci.hwnd);
|
|
|
|
// notify parent that we entered a scroll button
|
|
_OnHotItemChange(iButton, TRUE);
|
|
|
|
// if the style is PGS_AUTOSCROLL then we dont make the button hot when hovering
|
|
// over button.
|
|
|
|
//Is PGS_AUTOSCROLL set
|
|
_dwState[iButton] |= PGF_HOT;
|
|
if (ci.style & PGS_AUTOSCROLL)
|
|
{
|
|
_dwState[iButton] |= PGF_DEPRESSED;
|
|
}
|
|
|
|
//If the lbutton is down and the mouse is over one of the button then
|
|
// someone is trying to do drag and drop so autoscroll to help them.
|
|
// Make sure the lbutton down did not happen in the button before scrolling
|
|
if ( ((wParam & MK_LBUTTON) &&
|
|
(_iButtonTrack < 0)) ||
|
|
(ci.style & PGS_AUTOSCROLL) )
|
|
{
|
|
_iButtonTrack = iButton;
|
|
SetTimer(ci.hwnd, PGT_SCROLL, _cTimeout, NULL);
|
|
}
|
|
_DrawButton(NULL, iButton);
|
|
}
|
|
else
|
|
{
|
|
|
|
//Mouse is not over any button or it has left one of the scroll buttons.
|
|
//In either case call _OnMouseLeave
|
|
|
|
_OnMouseLeave();
|
|
}
|
|
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------------------
|
|
void CPager::_OnSetChild(HWND hwnd, HWND hwndChild)
|
|
{
|
|
ASSERT(IS_VALID_HANDLE(hwndChild, WND));
|
|
|
|
RECT rc;
|
|
_hwndChild = hwndChild;
|
|
_ptPos.x = 0;
|
|
_ptPos.y = 0;
|
|
_fReCalcSend = FALSE;
|
|
if (GetCapture() == ci.hwnd)
|
|
{
|
|
CCReleaseCapture(&ci);
|
|
}
|
|
_iButtonTrack = -1;
|
|
GetClientRect(hwnd, &rc);
|
|
|
|
_OnReCalcSize();
|
|
}
|
|
//---------------------------------------------------------------------------------------
|
|
void CPager::_OnReCalcSize()
|
|
{
|
|
RECT rc;
|
|
CCInvalidateFrame(ci.hwnd); // SWP_FRAMECHANGED etc.
|
|
_fReCalcSend = FALSE;
|
|
rc = _rcChildIdeal;
|
|
_SetChildPos(&rc, 0); // SetWindowPos
|
|
|
|
}
|
|
//---------------------------------------------------------------------------------------
|
|
void CPager::_OnSetPos(int iPos)
|
|
{
|
|
RECT rc = _rcChildIdeal;
|
|
|
|
if( ci.style & PGS_HORZ ) {
|
|
FlipRect(&rc);
|
|
FlipPoint(&_ptPos);
|
|
}
|
|
|
|
int height;
|
|
if (iPos < 0)
|
|
iPos = 0;
|
|
|
|
height = RECTHEIGHT(rc);
|
|
|
|
if( iPos < 0 || iPos > height || _ptPos.y == iPos ) {
|
|
//Invalid Position specified or no change . Igonore it.
|
|
return;
|
|
}
|
|
|
|
_ptPos.y = iPos;
|
|
|
|
if( ci.style & PGS_HORZ ) {
|
|
FlipRect(&rc);
|
|
FlipPoint(&_ptPos);
|
|
}
|
|
|
|
CCInvalidateFrame(ci.hwnd);
|
|
_SetChildPos(&rc , 0);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
int CPager::_OnGetPos()
|
|
{
|
|
if( ci.style & PGS_HORZ ) {
|
|
return _ptPos.x;
|
|
}else{
|
|
return _ptPos.y;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------------------
|
|
DWORD CPager::_GetButtonState(int iButton)
|
|
{
|
|
|
|
DWORD dwState = 0;
|
|
// Is the button id valid ?
|
|
if ((iButton == PGB_TOPORLEFT) || (iButton == PGB_BOTTOMORRIGHT))
|
|
{
|
|
//yes , Get the current state of the button
|
|
dwState = _dwState[iButton];
|
|
}
|
|
return dwState;
|
|
}
|
|
//---------------------------------------------------------------------------------------
|
|
void CPager::_OnTimer(UINT id)
|
|
{
|
|
switch (id)
|
|
{
|
|
case PGT_SCROLL:
|
|
if (_iButtonTrack >= 0)
|
|
{
|
|
// set it again because we do it faster every subsequent time
|
|
SetTimer(ci.hwnd, PGT_SCROLL, _cTimeout, NULL);
|
|
if (_HitTestCursor() == _iButtonTrack)
|
|
{
|
|
_Scroll(_iButtonTrack);
|
|
}
|
|
else if (!_fOwnsButtonDown)
|
|
{
|
|
// if we don't own the mouse tracking (ie, the user didn't button down on us to begin with,
|
|
// then we're done once we leave the button
|
|
_OnMouseLeave();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CPager::_KillTimer()
|
|
{
|
|
KillTimer(ci.hwnd, PGT_SCROLL);
|
|
_fTimerSet = FALSE;
|
|
}
|
|
//---------------------------------------------------------------------------------------
|
|
int CPager::_OnSetBorder(int iBorder)
|
|
{
|
|
int iOld = _iBorder;
|
|
int iNew = iBorder;
|
|
|
|
//Border can't be negative
|
|
if (iNew < 0 )
|
|
{
|
|
iNew = 0;
|
|
}
|
|
|
|
//Border can't be bigger than the button size
|
|
if (iNew > _GetButtonSize())
|
|
{
|
|
iNew = _GetButtonSize();
|
|
}
|
|
|
|
_iBorder = iNew;
|
|
CCInvalidateFrame(ci.hwnd);
|
|
RECT rc = _rcChildIdeal;
|
|
_SetChildPos(&rc, 0); // SetWindowPos
|
|
return iOld;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
int CPager::_OnSetButtonSize(int iSize)
|
|
{
|
|
int iOldSize = _iButtonSize;
|
|
_iButtonSize = iSize;
|
|
|
|
if (_iButtonSize < MINBUTTONSIZE)
|
|
{
|
|
_iButtonSize = MINBUTTONSIZE;
|
|
}
|
|
|
|
// Border can't be bigger than button size
|
|
if (_iBorder > _iButtonSize)
|
|
{
|
|
_iBorder = _iButtonSize;
|
|
}
|
|
|
|
CCInvalidateFrame(ci.hwnd);
|
|
RECT rc = _rcChildIdeal;
|
|
_SetChildPos(&rc, 0); // SetWindowPos
|
|
return iOldSize;
|
|
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
void CPager::_OnHotItemChange(int iButton, BOOL fEnter)
|
|
{
|
|
NMPGHOTITEM nmhot = {0};
|
|
|
|
if (fEnter)
|
|
{
|
|
nmhot.idNew = iButton;
|
|
nmhot.dwFlags = HICF_ENTERING;
|
|
}
|
|
else
|
|
{
|
|
nmhot.idOld = iButton;
|
|
nmhot.dwFlags = HICF_LEAVING;
|
|
}
|
|
|
|
CCSendNotify(&ci, PGN_HOTITEMCHANGE, &nmhot.hdr);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
LRESULT CPager::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg) {
|
|
case PGM_GETDROPTARGET:
|
|
if (!_hDragProxy)
|
|
_hDragProxy = CreateDragProxy(ci.hwnd, PagerDragCallback, FALSE);
|
|
|
|
GetDragProxyTarget(_hDragProxy, (IDropTarget**)lParam);
|
|
break;
|
|
|
|
case PGM_SETSCROLLINFO:
|
|
_cLinesPerTimeout = LOWORD(lParam);
|
|
_cPixelsPerLine = HIWORD(lParam);
|
|
_cTimeout = (UINT)wParam;
|
|
break;
|
|
|
|
case PGM_SETCHILD:
|
|
_OnSetChild(hwnd, (HWND)lParam);
|
|
break;
|
|
|
|
case PGM_RECALCSIZE:
|
|
if (!_fReCalcSend )
|
|
{
|
|
_fReCalcSend = TRUE;
|
|
PostMessage(hwnd, PGMP_RECALCSIZE, wParam, lParam);
|
|
}
|
|
break;
|
|
|
|
case PGMP_RECALCSIZE:
|
|
_OnReCalcSize();
|
|
break;
|
|
|
|
case PGM_FORWARDMOUSE:
|
|
// forward mouse messages
|
|
_fForwardMouseMsgs = BOOLIFY(wParam);
|
|
break;
|
|
|
|
|
|
case PGM_SETBKCOLOR:
|
|
{
|
|
COLORREF clr = _clrBk;
|
|
if ((COLORREF) lParam == CLR_DEFAULT)
|
|
_clrBk = g_clrBtnFace;
|
|
else
|
|
_clrBk = (COLORREF)lParam;
|
|
_fBkColorSet = TRUE;
|
|
CCInvalidateFrame(ci.hwnd);
|
|
//Force a paint
|
|
RedrawWindow(ci.hwnd, NULL,NULL,RDW_INVALIDATE|RDW_ERASE);
|
|
return clr;
|
|
}
|
|
|
|
case PGM_GETBKCOLOR:
|
|
return (LRESULT)_clrBk;
|
|
|
|
case PGM_SETBORDER:
|
|
return _OnSetBorder((int)lParam);
|
|
|
|
case PGM_GETBORDER:
|
|
return (LRESULT)_iBorder;
|
|
|
|
case PGM_SETPOS:
|
|
_OnSetPos((int)lParam);
|
|
break;
|
|
|
|
case PGM_GETPOS:
|
|
return _OnGetPos();
|
|
|
|
case PGM_SETBUTTONSIZE:
|
|
return _OnSetButtonSize((int)lParam);
|
|
|
|
case PGM_GETBUTTONSIZE:
|
|
return _GetButtonSize();
|
|
|
|
case PGM_GETBUTTONSTATE:
|
|
return _GetButtonState((int)lParam);
|
|
|
|
case WM_PRINTCLIENT:
|
|
CCForwardPrint(&ci, (HDC)wParam);
|
|
return 0;
|
|
|
|
case WM_PRINT:
|
|
return _OnPrint((HDC)wParam, (UINT)lParam);
|
|
|
|
case WM_NCHITTEST:
|
|
{
|
|
POINT pt;
|
|
pt.x = GET_X_LPARAM(lParam);
|
|
pt.y = GET_Y_LPARAM(lParam);
|
|
if (_HitTestScreen(&pt) == -1)
|
|
return HTTRANSPARENT;
|
|
return HTCLIENT;
|
|
}
|
|
|
|
case WM_SYSCOLORCHANGE:
|
|
if (!_fBkColorSet)
|
|
{
|
|
InitGlobalColors();
|
|
_clrBk = g_clrBtnFace;
|
|
CCInvalidateFrame(ci.hwnd);
|
|
}
|
|
break;
|
|
|
|
case WM_SETFOCUS:
|
|
SetFocus(_hwndChild);
|
|
return 0;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
//Fall Through
|
|
case WM_LBUTTONUP:
|
|
if(!(ci.style & PGS_AUTOSCROLL)) {
|
|
_OnLButtonChange(uMsg,lParam);
|
|
}
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
// Only forward if the point is within the client rect of pager.
|
|
if (_fForwardMouseMsgs && _hwndChild)
|
|
{
|
|
POINT pt;
|
|
RECT rcClient;
|
|
|
|
GetClientRect(ci.hwnd, &rcClient);
|
|
|
|
pt.x = GET_X_LPARAM(lParam);
|
|
pt.y = GET_Y_LPARAM(lParam);
|
|
|
|
// Is this point in our client rect?
|
|
if (PtInRect(&rcClient, pt))
|
|
{
|
|
// Yes; then convert coords and forward it
|
|
pt.x += _ptPos.x;
|
|
pt.y += _ptPos.y;
|
|
|
|
SendMessage(_hwndChild, WM_MOUSEMOVE, wParam, MAKELPARAM(pt.x, pt.y));
|
|
}
|
|
}
|
|
|
|
_OnMouseMove(wParam,lParam);
|
|
break;
|
|
|
|
case WM_MOUSELEAVE :
|
|
_OnMouseLeave();
|
|
break;
|
|
|
|
case WM_ERASEBKGND:
|
|
{
|
|
LRESULT lres = CCForwardEraseBackground(ci.hwnd, (HDC) wParam);
|
|
|
|
if (_iBorder) {
|
|
// paint the borders
|
|
RECT rc;
|
|
RECT rc2;
|
|
GetClientRect(ci.hwnd, &rc);
|
|
rc2 = rc;
|
|
|
|
if( ci.style & PGS_HORZ ) {
|
|
FlipRect(&rc2);
|
|
}
|
|
rc2.right = rc2.left + _iBorder + 1;
|
|
|
|
if( ci.style & PGS_HORZ ) {
|
|
FlipRect(&rc2);
|
|
}
|
|
FillRectClr((HDC)wParam, &rc2, _clrBk);
|
|
rc2 = rc;
|
|
|
|
if( ci.style & PGS_HORZ ) {
|
|
FlipRect(&rc2);
|
|
}
|
|
rc2.left = rc2.right - _iBorder - 1;
|
|
|
|
if( ci.style & PGS_HORZ ) {
|
|
FlipRect(&rc2);
|
|
}
|
|
|
|
FillRectClr((HDC)wParam, &rc2, _clrBk);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_TIMER:
|
|
_OnTimer((UINT)wParam);
|
|
return 0;
|
|
|
|
case WM_SETTINGCHANGE:
|
|
InitGlobalMetrics(wParam);
|
|
_iButtonSize = (int) g_cxScrollbar * 3 / 4;
|
|
if (_iButtonSize < MINBUTTONSIZE) {
|
|
_iButtonSize = MINBUTTONSIZE;
|
|
}
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
if (_hDragProxy)
|
|
DestroyDragProxy(_hDragProxy);
|
|
break;
|
|
}
|
|
return CControl::v_WndProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// call with cyCh == 0 to specify auto vsizing
|
|
BOOL DrawChar(HDC hdc, LPRECT lprc, UINT wState, TCHAR ch, UINT cyCh, BOOL fAlwaysGrayed, BOOL fTopAlign, COLORREF rgbOveride)
|
|
{
|
|
COLORREF rgb;
|
|
BOOL fDrawDisabled = !fAlwaysGrayed && (wState & DCHF_INACTIVE);
|
|
BOOL fDrawPushed = wState & DCHF_PUSHED;
|
|
// Bad UI to have a pushed disabled button
|
|
ASSERT (!fDrawDisabled || !fDrawPushed);
|
|
RECT rc = *lprc;
|
|
UINT uFormat = DT_CENTER | DT_SINGLELINE;
|
|
|
|
if (fAlwaysGrayed)
|
|
rgb = g_clrBtnShadow;
|
|
else if (fDrawDisabled)
|
|
rgb = g_clrBtnHighlight;
|
|
else
|
|
rgb = g_clrBtnText;
|
|
|
|
if (rgbOveride != CLR_INVALID)
|
|
{
|
|
rgb = rgbOveride;
|
|
}
|
|
|
|
rgb = SetTextColor(hdc, rgb);
|
|
|
|
if (cyCh)
|
|
{
|
|
if (fTopAlign)
|
|
rc.bottom = rc.top + cyCh;
|
|
else
|
|
{
|
|
rc.top += ((RECTHEIGHT(rc) - cyCh) / 2);
|
|
rc.bottom = rc.top + cyCh;
|
|
}
|
|
uFormat |= DT_BOTTOM;
|
|
}
|
|
else
|
|
uFormat |= DT_VCENTER;
|
|
|
|
if (fDrawDisabled || fDrawPushed)
|
|
OffsetRect(&rc, 1, 1);
|
|
|
|
DrawText(hdc, &ch, 1, &rc, uFormat);
|
|
|
|
if (fDrawDisabled)
|
|
{
|
|
OffsetRect(&rc, -1, -1);
|
|
SetTextColor(hdc, g_clrBtnShadow);
|
|
DrawText(hdc, &ch, 1, &rc, uFormat);
|
|
}
|
|
|
|
SetTextColor(hdc, rgb);
|
|
return(TRUE);
|
|
}
|
|
|
|
void DrawBlankButton(HDC hdc, LPRECT lprc, DWORD wControlState)
|
|
{
|
|
BOOL fAdjusted;
|
|
|
|
if (wControlState & (DCHF_HOT | DCHF_PUSHED) &&
|
|
!(wControlState & DCHF_NOBORDER)) {
|
|
COLORSCHEME clrsc;
|
|
|
|
clrsc.dwSize = 1;
|
|
if (GetBkColor(hdc) == g_clrBtnShadow) {
|
|
clrsc.clrBtnHighlight = g_clrBtnHighlight;
|
|
clrsc.clrBtnShadow = g_clrBtnText;
|
|
} else
|
|
clrsc.clrBtnHighlight = clrsc.clrBtnShadow = CLR_DEFAULT;
|
|
|
|
// if button is both DCHF_HOT and DCHF_PUSHED, DCHF_HOT wins here
|
|
CCDrawEdge(hdc, lprc, (wControlState & DCHF_HOT) ? BDR_RAISEDINNER : BDR_SUNKENOUTER,
|
|
(UINT) (BF_ADJUST | BF_RECT), &clrsc);
|
|
fAdjusted = TRUE;
|
|
} else {
|
|
fAdjusted = FALSE;
|
|
}
|
|
|
|
if (!(wControlState & DCHF_TRANSPARENT))
|
|
FillRectClr(hdc, lprc, GetBkColor(hdc));
|
|
|
|
if (!fAdjusted)
|
|
InflateRect(lprc, -g_cxBorder, -g_cyBorder);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
void DrawCharButton(HDC hdc, LPRECT lprc, UINT wControlState, TCHAR ch, UINT cyCh, BOOL fAlwaysGrayed, BOOL fTopAlign, COLORREF rgbOveride)
|
|
{
|
|
RECT rc;
|
|
CopyRect(&rc, lprc);
|
|
|
|
DrawBlankButton(hdc, &rc, wControlState);
|
|
|
|
if ((RECTWIDTH(rc) <= 0) || (RECTHEIGHT(rc) <= 0))
|
|
return;
|
|
|
|
int iOldBk = SetBkMode(hdc, TRANSPARENT);
|
|
DrawChar(hdc, &rc, wControlState, ch, cyCh, fAlwaysGrayed, fTopAlign, rgbOveride);
|
|
SetBkMode(hdc, iOldBk);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
//
|
|
// DrawScrollArrow
|
|
//
|
|
// --------------------------------------------------------------------------------------
|
|
void DrawScrollArrow(HDC hdc, LPRECT lprc, UINT wControlState, COLORREF rgbOveride)
|
|
{
|
|
#define szfnMarlett TEXT("MARLETT")
|
|
TCHAR ch = (wControlState & DCHF_HORIZONTAL) ? TEXT('3') : TEXT('5');
|
|
|
|
//
|
|
// Flip the direction arrow in case of a RTL mirrored DC,
|
|
// since it won't be flipped automatically (textout!)
|
|
//
|
|
if (IS_DC_RTL_MIRRORED(hdc) && (wControlState & DCHF_HORIZONTAL))
|
|
wControlState ^= DCHF_FLIPPED;
|
|
|
|
LONG lMin = min(RECTWIDTH(*lprc), RECTHEIGHT(*lprc)) - (2 * g_cxBorder); // g_cxBorder fudge notches font size down
|
|
|
|
HFONT hFont = CreateFont(lMin, 0, 0, 0, FW_NORMAL, 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0, szfnMarlett);
|
|
|
|
HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);
|
|
|
|
if (wControlState & DCHF_FLIPPED)
|
|
ch++;
|
|
|
|
DrawCharButton(hdc, lprc, wControlState, ch, 0, FALSE, FALSE, rgbOveride);
|
|
SelectObject(hdc, hOldFont);
|
|
DeleteObject(hFont);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
|
|
#define CX_INCREMENT 1
|
|
#define CX_DECREMENT (-CX_INCREMENT)
|
|
|
|
#define MIDPOINT(x1, x2) ((x1 + x2) / 2)
|
|
#define CHEVRON_WIDTH(dSeg) (4 * dSeg)
|
|
|
|
// DrawChevron is theme-aware
|
|
void DrawChevron(HTHEME hTheme, int iPartId, HDC hdc, LPRECT lprc, DWORD dwFlags)
|
|
{
|
|
RECT rc;
|
|
CopyRect(&rc, lprc);
|
|
|
|
if (hTheme)
|
|
{
|
|
// Get state id from dwFlags
|
|
int iStateId = CHEVS_NORMAL;
|
|
|
|
if (dwFlags & DCHF_HOT)
|
|
iStateId = CHEVS_HOT;
|
|
|
|
if (dwFlags & DCHF_PUSHED)
|
|
iStateId = CHEVS_PRESSED;
|
|
|
|
DrawThemeBackground(hTheme, hdc, iPartId, iStateId, &rc, 0);
|
|
}
|
|
else
|
|
{
|
|
// draw the border and background
|
|
DrawBlankButton(hdc, &rc, dwFlags);
|
|
|
|
// offset the arrow if pushed
|
|
if (dwFlags & DCHF_PUSHED)
|
|
OffsetRect(&rc, CX_INCREMENT, CX_INCREMENT);
|
|
|
|
// draw the arrow
|
|
HBRUSH hbrSave = SelectBrush(hdc, GetSysColorBrush(COLOR_BTNTEXT));
|
|
|
|
int dSeg = (g_cxVScroll / 7);
|
|
dSeg = max(2, dSeg);
|
|
|
|
if (dwFlags & DCHF_HORIZONTAL)
|
|
{
|
|
// horizontal arrow
|
|
int x = MIDPOINT(rc.left, rc.right - CHEVRON_WIDTH(dSeg));
|
|
|
|
int yBase;
|
|
if (dwFlags & DCHF_TOPALIGN)
|
|
yBase = rc.top + (3 * dSeg);
|
|
else
|
|
yBase = MIDPOINT(rc.top, rc.bottom);
|
|
|
|
|
|
for (int y = -dSeg; y <= dSeg; y++)
|
|
{
|
|
PatBlt(hdc, x, yBase + y, dSeg, CX_INCREMENT, PATCOPY);
|
|
PatBlt(hdc, x + (dSeg * 2), yBase + y, dSeg, CX_INCREMENT, PATCOPY);
|
|
|
|
x += (y < 0) ? CX_INCREMENT : CX_DECREMENT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// vertical arrow
|
|
int y = rc.top + CX_INCREMENT;
|
|
int xBase = MIDPOINT(rc.left, rc.right);
|
|
|
|
for (int x = -dSeg; x <= dSeg; x++)
|
|
{
|
|
PatBlt(hdc, xBase + x, y, CX_INCREMENT, dSeg, PATCOPY);
|
|
PatBlt(hdc, xBase + x, y + (dSeg * 2), CX_INCREMENT, dSeg, PATCOPY);
|
|
|
|
y += (x < 0) ? CX_INCREMENT : CX_DECREMENT;
|
|
}
|
|
}
|
|
|
|
// clean up
|
|
SelectBrush(hdc, hbrSave);
|
|
}
|
|
}
|