2391 lines
63 KiB
C++
2391 lines
63 KiB
C++
|
// some code taken from (freeware) Cool ScrollBar library by J Brown
|
||
|
#include "main.h"
|
||
|
#include <windowsx.h>
|
||
|
#include <tchar.h>
|
||
|
#include "scrollwnd.h"
|
||
|
#include "../winamp/wa_dlg.h"
|
||
|
|
||
|
#ifndef WM_MOUSEWHEEL
|
||
|
#define WM_MOUSEWHEEL 0x020A
|
||
|
#endif
|
||
|
|
||
|
|
||
|
extern HRESULT(WINAPI *SetWindowTheme)(HWND hwnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList); //xp theme shit
|
||
|
extern HRESULT(WINAPI *IsAppThemed)(void);
|
||
|
|
||
|
static TCHAR szPropStr[] = _T("CoolSBSubclassPtr");
|
||
|
|
||
|
//
|
||
|
// Special thumb-tracking variables
|
||
|
//
|
||
|
//
|
||
|
static UINT uCurrentScrollbar = COOLSB_NONE; //SB_HORZ / SB_VERT
|
||
|
static UINT uCurrentScrollPortion = HTSCROLL_NONE;
|
||
|
static UINT uCurrentButton = 0;
|
||
|
|
||
|
static RECT rcThumbBounds; //area that the scroll thumb can travel in
|
||
|
static int nThumbSize; //(pixels)
|
||
|
static int nThumbPos; //(pixels)
|
||
|
static int nThumbMouseOffset; //(pixels)
|
||
|
static int nLastPos = -1; //(scrollbar units)
|
||
|
static int nThumbPos0; //(pixels) initial thumb position
|
||
|
|
||
|
//
|
||
|
// Temporary state used to auto-generate timer messages
|
||
|
//
|
||
|
static UINT uMouseOverId = 0;
|
||
|
static UINT uMouseOverScrollbar = COOLSB_NONE;
|
||
|
static UINT uHitTestPortion = HTSCROLL_NONE;
|
||
|
static UINT uLastHitTestPortion = HTSCROLL_NONE;
|
||
|
static RECT MouseOverRect;
|
||
|
|
||
|
static UINT uScrollTimerMsg = 0;
|
||
|
static UINT uScrollTimerPortion = HTSCROLL_NONE;
|
||
|
static UINT_PTR uScrollTimerId = 0;
|
||
|
static HWND hwndCurCoolSB = 0;
|
||
|
|
||
|
ScrollWnd *GetScrollWndFromHwnd(HWND hwnd)
|
||
|
{
|
||
|
return (ScrollWnd *)GetProp(hwnd, szPropStr);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// swap the rectangle's x coords with its y coords
|
||
|
//
|
||
|
static void __stdcall RotateRect(RECT *rect)
|
||
|
{
|
||
|
int temp;
|
||
|
temp = rect->left;
|
||
|
rect->left = rect->top;
|
||
|
rect->top = temp;
|
||
|
|
||
|
temp = rect->right;
|
||
|
rect->right = rect->bottom;
|
||
|
rect->bottom = temp;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// swap the coords if the scrollbar is a SB_VERT
|
||
|
//
|
||
|
static void __stdcall RotateRect0(SCROLLBAR *sb, RECT *rect)
|
||
|
{
|
||
|
if (sb->nBarType == SB_VERT)
|
||
|
RotateRect(rect);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Calculate if the SCROLLINFO members produce
|
||
|
// an enabled or disabled scrollbar
|
||
|
//
|
||
|
static BOOL IsScrollInfoActive(SCROLLINFO *si)
|
||
|
{
|
||
|
if ((si->nPage > (UINT)si->nMax
|
||
|
|| si->nMax <= si->nMin || si->nMax == 0))
|
||
|
return FALSE;
|
||
|
else
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Return if the specified scrollbar is enabled or not
|
||
|
//
|
||
|
static BOOL IsScrollbarActive(SCROLLBAR *sb)
|
||
|
{
|
||
|
SCROLLINFO *si = &sb->scrollInfo;
|
||
|
if (((sb->fScrollFlags & ESB_DISABLE_BOTH) == ESB_DISABLE_BOTH) ||
|
||
|
!(sb->fScrollFlags & CSBS_THUMBALWAYS) && !IsScrollInfoActive(si))
|
||
|
return FALSE;
|
||
|
else
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL drawFrameControl(HDC hdc, LPRECT lprc, UINT uType, UINT state)
|
||
|
{
|
||
|
HDC hdcbmp;
|
||
|
HBITMAP hbmpOld, hbmp;
|
||
|
int startx, starty;
|
||
|
|
||
|
hbmp = WADlg_getBitmap();
|
||
|
if (!hbmp) return FALSE;
|
||
|
|
||
|
hdcbmp = CreateCompatibleDC(hdc);
|
||
|
if (!hdcbmp) return FALSE;
|
||
|
|
||
|
hbmpOld = (HBITMAP)SelectObject(hdcbmp, hbmp);
|
||
|
|
||
|
startx = 0;
|
||
|
starty = 31;
|
||
|
switch (state&3)
|
||
|
{
|
||
|
case DFCS_SCROLLRIGHT: startx = 14; starty = 45; break;
|
||
|
case DFCS_SCROLLLEFT: startx = 0; starty = 45; break;
|
||
|
case DFCS_SCROLLDOWN: startx = 14; starty = 31; break;
|
||
|
}
|
||
|
if (state&DFCS_PUSHED) startx += 28;
|
||
|
StretchBlt(hdc, lprc->left, lprc->top, lprc->right - lprc->left, lprc->bottom - lprc->top, hdcbmp, startx, starty, 14, 14, SRCCOPY);
|
||
|
|
||
|
SelectObject(hdcbmp, hbmpOld);
|
||
|
DeleteDC(hdcbmp);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Draw a standard scrollbar arrow
|
||
|
//
|
||
|
static int DrawScrollArrow(SCROLLBAR *sbar, HDC hdc, RECT *rect, UINT arrow, BOOL fMouseDown, BOOL fMouseOver)
|
||
|
{
|
||
|
UINT ret;
|
||
|
UINT flags = arrow;
|
||
|
|
||
|
//HACKY bit so this routine can be called by vertical and horizontal code
|
||
|
if (sbar->nBarType == SB_VERT)
|
||
|
{
|
||
|
if (flags & DFCS_SCROLLLEFT) flags = flags & ~DFCS_SCROLLLEFT | DFCS_SCROLLUP;
|
||
|
if (flags & DFCS_SCROLLRIGHT) flags = flags & ~DFCS_SCROLLRIGHT | DFCS_SCROLLDOWN;
|
||
|
}
|
||
|
|
||
|
if (fMouseDown) flags |= (DFCS_FLAT | DFCS_PUSHED);
|
||
|
|
||
|
ret = drawFrameControl(hdc, rect, DFC_SCROLL, flags);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Return the size in pixels for the specified scrollbar metric,
|
||
|
// for the specified scrollbar
|
||
|
//
|
||
|
static int GetScrollMetric(SCROLLBAR *sbar, int metric)
|
||
|
{
|
||
|
if (sbar->nBarType == SB_HORZ)
|
||
|
{
|
||
|
if (metric == SM_CXHORZSB)
|
||
|
{
|
||
|
if (sbar->nArrowLength < 0)
|
||
|
return -sbar->nArrowLength * 14; //GetSystemMetrics(SM_CXHSCROLL);
|
||
|
else
|
||
|
return sbar->nArrowLength;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (sbar->nArrowWidth < 0)
|
||
|
return -sbar->nArrowWidth * 14; //GetSystemMetrics(SM_CYHSCROLL);
|
||
|
else
|
||
|
return sbar->nArrowWidth;
|
||
|
}
|
||
|
}
|
||
|
else if (sbar->nBarType == SB_VERT)
|
||
|
{
|
||
|
if (metric == SM_CYVERTSB)
|
||
|
{
|
||
|
if (sbar->nArrowLength < 0)
|
||
|
return -sbar->nArrowLength * 14;//GetSystemMetrics(SM_CYVSCROLL);
|
||
|
else
|
||
|
return sbar->nArrowLength;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (sbar->nArrowWidth < 0)
|
||
|
return -sbar->nArrowWidth * 14;//GetSystemMetrics(SM_CXVSCROLL);
|
||
|
else
|
||
|
return sbar->nArrowWidth;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
static COLORREF GetSBForeColor(void)
|
||
|
{
|
||
|
return WADlg_getColor(WADLG_SCROLLBAR_FGCOLOR);
|
||
|
}
|
||
|
|
||
|
static COLORREF GetSBBackColor(void)
|
||
|
{
|
||
|
return WADlg_getColor(WADLG_SCROLLBAR_BGCOLOR);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Paint a checkered rectangle, with each alternate
|
||
|
// pixel being assigned a different colour
|
||
|
//
|
||
|
static void DrawCheckedRect(HDC hdc, RECT *rect, COLORREF fg, COLORREF bg)
|
||
|
{
|
||
|
static WORD wCheckPat[8] =
|
||
|
{
|
||
|
0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555
|
||
|
};
|
||
|
|
||
|
HBITMAP hbmp;
|
||
|
HBRUSH hbr, hbrold;
|
||
|
COLORREF fgold, bgold;
|
||
|
|
||
|
hbmp = CreateBitmap(8, 8, 1, 1, wCheckPat);
|
||
|
hbr = CreatePatternBrush(hbmp);
|
||
|
|
||
|
UnrealizeObject(hbr);
|
||
|
SetBrushOrgEx(hdc, rect->left, rect->top, 0);
|
||
|
|
||
|
hbrold = (HBRUSH)SelectObject(hdc, hbr);
|
||
|
|
||
|
fgold = SetTextColor(hdc, fg);
|
||
|
bgold = SetBkColor(hdc, bg);
|
||
|
|
||
|
PatBlt(hdc, rect->left, rect->top,
|
||
|
rect->right - rect->left,
|
||
|
rect->bottom - rect->top,
|
||
|
PATCOPY);
|
||
|
|
||
|
SetBkColor(hdc, bgold);
|
||
|
SetTextColor(hdc, fgold);
|
||
|
|
||
|
SelectObject(hdc, hbrold);
|
||
|
DeleteObject(hbr);
|
||
|
DeleteObject(hbmp);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Fill the specifed rectangle using a solid colour
|
||
|
//
|
||
|
static void PaintRect(HDC hdc, RECT *rect, COLORREF color)
|
||
|
{
|
||
|
COLORREF oldcol = SetBkColor(hdc, color);
|
||
|
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, rect, _T(""), 0, 0);
|
||
|
SetBkColor(hdc, oldcol);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Draw a simple blank scrollbar push-button. Can be used
|
||
|
// to draw a push button, or the scrollbar thumb
|
||
|
// drawflag - could set to BF_FLAT to make flat scrollbars
|
||
|
//
|
||
|
void DrawBlankButton(HDC hdc, const RECT *rect, UINT drawflag, int pushed, int vertical)
|
||
|
{
|
||
|
HBITMAP hbmp, hbmpOld;
|
||
|
hbmp = WADlg_getBitmap();
|
||
|
if (!hbmp) return;
|
||
|
|
||
|
HDC hdcbmp = CreateCompatibleDC(hdc);
|
||
|
if (!hdcbmp) return;
|
||
|
|
||
|
hbmpOld = (HBITMAP)SelectObject(hdcbmp, hbmp);
|
||
|
|
||
|
#define PART1SIZE 4 //copied top
|
||
|
#define PART2SIZE 5 //stretched top
|
||
|
#define PART3SIZE 10 //copied middle
|
||
|
#define PART4SIZE 5 //stretched bottom
|
||
|
#define PART5SIZE 4 //copied bottom
|
||
|
|
||
|
if (vertical)
|
||
|
{
|
||
|
int middle = (rect->bottom - rect->top) / 2;
|
||
|
int startx = pushed ? 70 : 56;
|
||
|
//top
|
||
|
StretchBlt(hdc, rect->left, rect->top, rect->right - rect->left, PART1SIZE, hdcbmp, startx, 31, 14, PART1SIZE, SRCCOPY);
|
||
|
int p = PART1SIZE;
|
||
|
//stretched top
|
||
|
int l = middle - PART1SIZE - (PART3SIZE / 2);
|
||
|
if (l > 0)
|
||
|
{
|
||
|
StretchBlt(hdc, rect->left, rect->top + p, rect->right - rect->left, l, hdcbmp, startx, 31 + PART1SIZE, 14, PART2SIZE, SRCCOPY);
|
||
|
p += middle - PART1SIZE - (PART3SIZE / 2);
|
||
|
}
|
||
|
//copied middle
|
||
|
int m = (rect->bottom - rect->top) - PART1SIZE - PART5SIZE; //space that's available for middle
|
||
|
m = min(m, PART3SIZE);
|
||
|
if (m > 0)
|
||
|
{
|
||
|
StretchBlt(hdc, rect->left, rect->top + p, rect->right - rect->left, m, hdcbmp, startx, 31 + PART1SIZE + PART2SIZE, 14, m, SRCCOPY);
|
||
|
p += m;
|
||
|
}
|
||
|
//stretched bottom
|
||
|
l = rect->bottom - rect->top - p - PART5SIZE;
|
||
|
if (l > 0) StretchBlt(hdc, rect->left, rect->top + p, rect->right - rect->left, l, hdcbmp, startx, 31 + PART1SIZE + PART2SIZE + PART3SIZE, 14, PART4SIZE, SRCCOPY);
|
||
|
//bottom
|
||
|
StretchBlt(hdc, rect->left, rect->bottom - PART5SIZE, rect->right - rect->left, PART5SIZE, hdcbmp, startx, 31 + PART1SIZE + PART2SIZE + PART3SIZE + PART4SIZE, 14, PART5SIZE, SRCCOPY);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int middle = (rect->right - rect->left) / 2;
|
||
|
int starty = pushed ? 45 : 31;
|
||
|
//top
|
||
|
StretchBlt(hdc, rect->left, rect->top, PART1SIZE, rect->bottom - rect->top, hdcbmp, 84, starty, PART1SIZE, 14, SRCCOPY);
|
||
|
int p = PART1SIZE;
|
||
|
//stretched top
|
||
|
int l = middle - PART1SIZE - (PART3SIZE / 2);
|
||
|
if (l > 0)
|
||
|
{
|
||
|
StretchBlt(hdc, rect->left + p, rect->top, l, rect->bottom - rect->top, hdcbmp, 84 + PART1SIZE, starty, PART2SIZE, 14, SRCCOPY);
|
||
|
p += middle - PART1SIZE - (PART3SIZE / 2);
|
||
|
}
|
||
|
//copied middle
|
||
|
int m = (rect->right - rect->left) - PART1SIZE - PART5SIZE; //space that's available for middle
|
||
|
m = min(m, PART3SIZE);
|
||
|
if (m > 0)
|
||
|
{
|
||
|
StretchBlt(hdc, rect->left + p, rect->top, m, rect->bottom - rect->top, hdcbmp, 84 + PART1SIZE + PART2SIZE, starty, m, 14, SRCCOPY);
|
||
|
p += m;
|
||
|
}
|
||
|
//stretched bottom
|
||
|
l = rect->right - rect->left - p - PART5SIZE;
|
||
|
if (l > 0) StretchBlt(hdc, rect->left + p, rect->top, l, rect->bottom - rect->top, hdcbmp, 84 + PART1SIZE + PART2SIZE + PART3SIZE, starty, PART4SIZE, 14, SRCCOPY);
|
||
|
//bottom
|
||
|
StretchBlt(hdc, rect->right - PART5SIZE, rect->top, PART5SIZE, rect->bottom - rect->top, hdcbmp, 84 + PART1SIZE + PART2SIZE + PART3SIZE + PART4SIZE, starty, PART5SIZE, 14, SRCCOPY);
|
||
|
}
|
||
|
|
||
|
SelectObject(hdcbmp, hbmpOld);
|
||
|
DeleteDC(hdcbmp);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Send a WM_VSCROLL or WM_HSCROLL message
|
||
|
//
|
||
|
static void SendScrollMessage(HWND hwnd, UINT scrMsg, UINT scrId, UINT pos)
|
||
|
{
|
||
|
SendMessage(hwnd, scrMsg, MAKEWPARAM(scrId, pos), 0);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Calculate the screen coordinates of the area taken by
|
||
|
// the horizontal scrollbar. Take into account the size
|
||
|
// of the window borders
|
||
|
//
|
||
|
static BOOL GetHScrollRect(ScrollWnd *sw, HWND hwnd, RECT *rect)
|
||
|
{
|
||
|
GetWindowRect(hwnd, rect);
|
||
|
|
||
|
if (sw->fLeftScrollbar)
|
||
|
{
|
||
|
rect->left += sw->cxLeftEdge + (sw->sbarVert.fScrollVisible ?
|
||
|
GetScrollMetric(&sw->sbarVert, SM_CXVERTSB) : 0);
|
||
|
rect->right -= sw->cxRightEdge;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rect->left += sw->cxLeftEdge; //left window edge
|
||
|
|
||
|
rect->right -= sw->cxRightEdge + //right window edge
|
||
|
(sw->sbarVert.fScrollVisible ?
|
||
|
GetScrollMetric(&sw->sbarVert, SM_CXVERTSB) : 0);
|
||
|
}
|
||
|
|
||
|
rect->bottom -= sw->cyBottomEdge; //bottom window edge
|
||
|
|
||
|
rect->top = rect->bottom -
|
||
|
(sw->sbarHorz.fScrollVisible ?
|
||
|
GetScrollMetric(&sw->sbarHorz, SM_CYHORZSB) : 0);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Calculate the screen coordinates of the area taken by the
|
||
|
// vertical scrollbar
|
||
|
//
|
||
|
static BOOL GetVScrollRect(ScrollWnd *sw, HWND hwnd, RECT *rect)
|
||
|
{
|
||
|
GetWindowRect(hwnd, rect);
|
||
|
rect->top += sw->cyTopEdge; //top window edge
|
||
|
|
||
|
rect->bottom -= sw->cyBottomEdge +
|
||
|
(sw->sbarHorz.fScrollVisible ? //bottom window edge
|
||
|
GetScrollMetric(&sw->sbarHorz, SM_CYHORZSB) : 0);
|
||
|
|
||
|
if (sw->fLeftScrollbar)
|
||
|
{
|
||
|
rect->left += sw->cxLeftEdge;
|
||
|
rect->right = rect->left + (sw->sbarVert.fScrollVisible ?
|
||
|
GetScrollMetric(&sw->sbarVert, SM_CXVERTSB) : 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rect->right -= sw->cxRightEdge;
|
||
|
rect->left = rect->right - (sw->sbarVert.fScrollVisible ?
|
||
|
GetScrollMetric(&sw->sbarVert, SM_CXVERTSB) : 0);
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// Depending on what type of scrollbar nBar refers to, call the
|
||
|
// appropriate Get?ScrollRect function
|
||
|
//
|
||
|
BOOL GetScrollRect(ScrollWnd *sw, UINT nBar, HWND hwnd, RECT *rect)
|
||
|
{
|
||
|
if (nBar == SB_HORZ)
|
||
|
return GetHScrollRect(sw, hwnd, rect);
|
||
|
else if (nBar == SB_VERT)
|
||
|
return GetVScrollRect(sw, hwnd, rect);
|
||
|
else
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Work out the scrollbar width/height for either type of scrollbar (SB_HORZ/SB_VERT)
|
||
|
// rect - coords of the scrollbar.
|
||
|
// store results into *thumbsize and *thumbpos
|
||
|
//
|
||
|
static int CalcThumbSize(SCROLLBAR *sbar, const RECT *rect, int *pthumbsize, int *pthumbpos)
|
||
|
{
|
||
|
SCROLLINFO *si;
|
||
|
int scrollsize; //total size of the scrollbar including arrow buttons
|
||
|
int workingsize; //working area (where the thumb can slide)
|
||
|
int siMaxMin;
|
||
|
int butsize;
|
||
|
int startcoord;
|
||
|
int thumbpos = 0, thumbsize = 0;
|
||
|
|
||
|
//work out the width (for a horizontal) or the height (for a vertical)
|
||
|
//of a standard scrollbar button
|
||
|
butsize = GetScrollMetric(sbar, SM_SCROLL_LENGTH);
|
||
|
|
||
|
if (1) //sbar->nBarType == SB_HORZ)
|
||
|
{
|
||
|
scrollsize = rect->right - rect->left;
|
||
|
startcoord = rect->left;
|
||
|
}
|
||
|
/*else if(sbar->nBarType == SB_VERT)
|
||
|
{
|
||
|
scrollsize = rect->bottom - rect->top;
|
||
|
startcoord = rect->top;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return 0;
|
||
|
}*/
|
||
|
|
||
|
si = &sbar->scrollInfo;
|
||
|
siMaxMin = si->nMax - si->nMin + 1;
|
||
|
workingsize = scrollsize - butsize * 2;
|
||
|
|
||
|
//
|
||
|
// Work out the scrollbar thumb SIZE
|
||
|
//
|
||
|
if (si->nPage == 0)
|
||
|
{
|
||
|
thumbsize = butsize;
|
||
|
}
|
||
|
else if (siMaxMin > 0)
|
||
|
{
|
||
|
thumbsize = MulDiv(si->nPage, workingsize, siMaxMin);
|
||
|
|
||
|
if (thumbsize < sbar->nMinThumbSize)
|
||
|
thumbsize = sbar->nMinThumbSize;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Work out the scrollbar thumb position
|
||
|
//
|
||
|
if (siMaxMin > 0)
|
||
|
{
|
||
|
int pagesize = max(1, si->nPage);
|
||
|
thumbpos = MulDiv(si->nPos - si->nMin, workingsize - thumbsize, siMaxMin - pagesize);
|
||
|
|
||
|
if (thumbpos < 0)
|
||
|
thumbpos = 0;
|
||
|
|
||
|
if (thumbpos >= workingsize - thumbsize)
|
||
|
thumbpos = workingsize - thumbsize;
|
||
|
}
|
||
|
|
||
|
thumbpos += startcoord + butsize;
|
||
|
|
||
|
*pthumbpos = thumbpos;
|
||
|
*pthumbsize = thumbsize;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// return a hit-test value for whatever part of the scrollbar x,y is located in
|
||
|
// rect, x, y: SCREEN coordinates
|
||
|
// the rectangle must not include space for any inserted buttons
|
||
|
// (i.e, JUST the scrollbar area)
|
||
|
//
|
||
|
static UINT GetHorzScrollPortion(SCROLLBAR *sbar, HWND hwnd, const RECT *rect, int x, int y)
|
||
|
{
|
||
|
int thumbwidth, thumbpos;
|
||
|
int butwidth = GetScrollMetric(sbar, SM_SCROLL_LENGTH);
|
||
|
int scrollwidth = rect->right - rect->left;
|
||
|
int workingwidth = scrollwidth - butwidth * 2;
|
||
|
|
||
|
if (y < rect->top || y >= rect->bottom)
|
||
|
return HTSCROLL_NONE;
|
||
|
|
||
|
CalcThumbSize(sbar, rect, &thumbwidth, &thumbpos);
|
||
|
|
||
|
//if we have had to scale the buttons to fit in the rect,
|
||
|
//then adjust the button width accordingly
|
||
|
if (scrollwidth <= butwidth * 2)
|
||
|
{
|
||
|
butwidth = scrollwidth / 2;
|
||
|
}
|
||
|
|
||
|
//check for left button click
|
||
|
if (x >= rect->left && x < rect->left + butwidth)
|
||
|
{
|
||
|
return HTSCROLL_LEFT;
|
||
|
}
|
||
|
//check for right button click
|
||
|
else if (x >= rect->right - butwidth && x < rect->right)
|
||
|
{
|
||
|
return HTSCROLL_RIGHT;
|
||
|
}
|
||
|
|
||
|
//if the thumb is too big to fit (i.e. it isn't visible)
|
||
|
//then return a NULL scrollbar area
|
||
|
if (thumbwidth >= workingwidth)
|
||
|
return HTSCROLL_NONE;
|
||
|
|
||
|
//check for point in the thumbbar
|
||
|
if (x >= thumbpos && x < thumbpos + thumbwidth)
|
||
|
{
|
||
|
return HTSCROLL_THUMB;
|
||
|
}
|
||
|
//check for left margin
|
||
|
else if (x >= rect->left + butwidth && x < thumbpos)
|
||
|
{
|
||
|
return HTSCROLL_PAGELEFT;
|
||
|
}
|
||
|
else if (x >= thumbpos + thumbwidth && x < rect->right - butwidth)
|
||
|
{
|
||
|
return HTSCROLL_PAGERIGHT;
|
||
|
}
|
||
|
|
||
|
return HTSCROLL_NONE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// For vertical scrollbars, rotate all coordinates by -90 degrees
|
||
|
// so that we can use the horizontal version of this function
|
||
|
//
|
||
|
static UINT GetVertScrollPortion(SCROLLBAR *sb, HWND hwnd, RECT *rect, int x, int y)
|
||
|
{
|
||
|
UINT r;
|
||
|
|
||
|
RotateRect(rect);
|
||
|
r = GetHorzScrollPortion(sb, hwnd, rect, y, x);
|
||
|
RotateRect(rect);
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// CUSTOM DRAW support
|
||
|
//
|
||
|
static LRESULT PostCustomPrePostPaint0(HWND hwnd, HDC hdc, SCROLLBAR *sb, UINT dwStage)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static LRESULT PostCustomDrawNotify0(HWND hwnd, HDC hdc, UINT nBar, RECT *prect, UINT nItem, BOOL fMouseDown, BOOL fMouseOver, BOOL fInactive)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Depending on if we are supporting custom draw, either define
|
||
|
// a macro to the function name, or to nothing at all. If custom draw
|
||
|
// is turned off, then we can save ALOT of code space by binning all
|
||
|
// calls to the custom draw support.
|
||
|
//
|
||
|
#define PostCustomDrawNotify 1 ? (void)0 : PostCustomDrawNotify0
|
||
|
#define PostCustomPrePostPaint 1 ? (void)0 : PostCustomPrePostPaint0
|
||
|
|
||
|
static LRESULT PostMouseNotify0(HWND hwnd, UINT msg, UINT nBar, RECT *prect, UINT nCmdId, POINT pt)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#ifdef NOTIFY_MOUSE
|
||
|
#define PostMouseNotify PostMouseNotify0
|
||
|
#else
|
||
|
#define PostMouseNotify 1 ? (void)0 : PostMouseNotify0
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Draw a complete HORIZONTAL scrollbar in the given rectangle
|
||
|
// Don't draw any inserted buttons in this procedure
|
||
|
//
|
||
|
// uDrawFlags - hittest code, to say if to draw the
|
||
|
// specified portion in an active state or not.
|
||
|
//
|
||
|
//
|
||
|
static LRESULT NCDrawHScrollbar(SCROLLBAR *sb, HWND hwnd, HDC hdc, const RECT *rect, UINT uDrawFlags)
|
||
|
{
|
||
|
SCROLLINFO *si;
|
||
|
RECT ctrl, thumb;
|
||
|
RECT sbm;
|
||
|
int butwidth = GetScrollMetric(sb, SM_SCROLL_LENGTH);
|
||
|
int scrollwidth = rect->right - rect->left;
|
||
|
int workingwidth = scrollwidth - butwidth * 2;
|
||
|
int thumbwidth = 0, thumbpos = 0;
|
||
|
BOOL fCustomDraw = 0;
|
||
|
|
||
|
BOOL fMouseDownL = 0, fMouseOverL = 0;
|
||
|
BOOL fMouseDownR = 0, fMouseOverR = 0;
|
||
|
|
||
|
COLORREF crCheck1 = GetSBForeColor();
|
||
|
COLORREF crCheck2 = GetSBBackColor();
|
||
|
COLORREF crInverse1 = WADlg_getColor(WADLG_SCROLLBAR_INV_FGCOLOR);
|
||
|
COLORREF crInverse2 = WADlg_getColor(WADLG_SCROLLBAR_INV_BGCOLOR);
|
||
|
|
||
|
UINT uDEFlat = sb->fFlatScrollbar ? BF_FLAT : 0;
|
||
|
|
||
|
//drawing flags to modify the appearance of the scrollbar buttons
|
||
|
UINT uLeftButFlags = DFCS_SCROLLLEFT;
|
||
|
UINT uRightButFlags = DFCS_SCROLLRIGHT;
|
||
|
|
||
|
if (scrollwidth <= 0)
|
||
|
return 0;
|
||
|
|
||
|
si = &sb->scrollInfo;
|
||
|
|
||
|
if (hwnd != hwndCurCoolSB)
|
||
|
uDrawFlags = HTSCROLL_NONE;
|
||
|
//
|
||
|
// work out the thumb size and position
|
||
|
//
|
||
|
CalcThumbSize(sb, rect, &thumbwidth, &thumbpos);
|
||
|
|
||
|
if (sb->fScrollFlags & ESB_DISABLE_LEFT) uLeftButFlags |= DFCS_INACTIVE;
|
||
|
if (sb->fScrollFlags & ESB_DISABLE_RIGHT) uRightButFlags |= DFCS_INACTIVE;
|
||
|
|
||
|
//if we need to grey the arrows because there is no data to scroll
|
||
|
if (!IsScrollInfoActive(si) && !(sb->fScrollFlags & CSBS_THUMBALWAYS))
|
||
|
{
|
||
|
uLeftButFlags |= DFCS_INACTIVE;
|
||
|
uRightButFlags |= DFCS_INACTIVE;
|
||
|
}
|
||
|
|
||
|
if (hwnd == hwndCurCoolSB)
|
||
|
{
|
||
|
fMouseDownL = (uDrawFlags == HTSCROLL_LEFT);
|
||
|
fMouseDownR = (uDrawFlags == HTSCROLL_RIGHT);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Draw the scrollbar now
|
||
|
//
|
||
|
if (scrollwidth > butwidth*2)
|
||
|
{
|
||
|
//LEFT ARROW
|
||
|
SetRect(&ctrl, rect->left, rect->top, rect->left + butwidth, rect->bottom);
|
||
|
|
||
|
RotateRect0(sb, &ctrl);
|
||
|
|
||
|
if (fCustomDraw)
|
||
|
PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &ctrl, SB_LINELEFT, fMouseDownL, fMouseOverL, uLeftButFlags & DFCS_INACTIVE);
|
||
|
else
|
||
|
DrawScrollArrow(sb, hdc, &ctrl, uLeftButFlags, fMouseDownL, fMouseOverL);
|
||
|
|
||
|
RotateRect0(sb, &ctrl);
|
||
|
|
||
|
//MIDDLE PORTION
|
||
|
//if we can fit the thumbbar in, then draw it
|
||
|
if (thumbwidth > 0 && thumbwidth <= workingwidth
|
||
|
&& IsScrollInfoActive(si) && ((sb->fScrollFlags & ESB_DISABLE_BOTH) != ESB_DISABLE_BOTH))
|
||
|
{
|
||
|
//Draw the scrollbar margin above the thumb
|
||
|
SetRect(&sbm, rect->left + butwidth, rect->top, thumbpos, rect->bottom);
|
||
|
|
||
|
RotateRect0(sb, &sbm);
|
||
|
|
||
|
if (fCustomDraw)
|
||
|
{
|
||
|
PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &sbm, SB_PAGELEFT, uDrawFlags == HTSCROLL_PAGELEFT, FALSE, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (uDrawFlags == HTSCROLL_PAGELEFT)
|
||
|
DrawCheckedRect(hdc, &sbm, crInverse1, crInverse2);
|
||
|
else
|
||
|
DrawCheckedRect(hdc, &sbm, crCheck1, crCheck2);
|
||
|
|
||
|
}
|
||
|
|
||
|
RotateRect0(sb, &sbm);
|
||
|
|
||
|
//Draw the margin below the thumb
|
||
|
sbm.left = thumbpos + thumbwidth;
|
||
|
sbm.right = rect->right - butwidth;
|
||
|
|
||
|
RotateRect0(sb, &sbm);
|
||
|
if (fCustomDraw)
|
||
|
{
|
||
|
PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &sbm, SB_PAGERIGHT, uDrawFlags == HTSCROLL_PAGERIGHT, 0, 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (uDrawFlags == HTSCROLL_PAGERIGHT)
|
||
|
DrawCheckedRect(hdc, &sbm, crInverse1, crInverse2);
|
||
|
else
|
||
|
DrawCheckedRect(hdc, &sbm, crCheck1, crCheck2);
|
||
|
|
||
|
}
|
||
|
RotateRect0(sb, &sbm);
|
||
|
|
||
|
//Draw the THUMB finally
|
||
|
SetRect(&thumb, thumbpos, rect->top, thumbpos + thumbwidth, rect->bottom);
|
||
|
|
||
|
RotateRect0(sb, &thumb);
|
||
|
|
||
|
if (fCustomDraw)
|
||
|
{
|
||
|
PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &thumb, SB_THUMBTRACK, uDrawFlags == HTSCROLL_THUMB, uHitTestPortion == HTSCROLL_THUMB, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int track = 0;
|
||
|
if (uCurrentScrollbar == (UINT)sb->nBarType) track = GetScrollWndFromHwnd(hwnd)->fThumbTracking;
|
||
|
DrawBlankButton(hdc, &thumb, uDEFlat, track, sb->nBarType == SB_VERT);
|
||
|
}
|
||
|
RotateRect0(sb, &thumb);
|
||
|
|
||
|
}
|
||
|
//otherwise, just leave that whole area blank
|
||
|
else
|
||
|
{
|
||
|
OffsetRect(&ctrl, butwidth, 0);
|
||
|
ctrl.right = rect->right - butwidth;
|
||
|
|
||
|
//if we always show the thumb covering the whole scrollbar,
|
||
|
//then draw it that way
|
||
|
if (!IsScrollInfoActive(si) && (sb->fScrollFlags & CSBS_THUMBALWAYS)
|
||
|
&& ctrl.right - ctrl.left > sb->nMinThumbSize)
|
||
|
{
|
||
|
//leave a 1-pixel gap between the thumb + right button
|
||
|
ctrl.right --;
|
||
|
RotateRect0(sb, &ctrl);
|
||
|
|
||
|
if (fCustomDraw)
|
||
|
PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &ctrl, SB_THUMBTRACK, fMouseDownL, FALSE, FALSE);
|
||
|
else
|
||
|
{
|
||
|
DrawBlankButton(hdc, &ctrl, uDEFlat, 0, sb->nBarType == SB_VERT);
|
||
|
|
||
|
}
|
||
|
RotateRect0(sb, &ctrl);
|
||
|
|
||
|
//draw the single-line gap
|
||
|
ctrl.left = ctrl.right;
|
||
|
ctrl.right += 1;
|
||
|
|
||
|
RotateRect0(sb, &ctrl);
|
||
|
|
||
|
if (fCustomDraw)
|
||
|
PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &ctrl, SB_PAGERIGHT, 0, 0, 0);
|
||
|
else
|
||
|
PaintRect(hdc, &ctrl, GetSysColor(COLOR_SCROLLBAR));
|
||
|
|
||
|
RotateRect0(sb, &ctrl);
|
||
|
}
|
||
|
//otherwise, paint a blank if the thumb doesn't fit in
|
||
|
else
|
||
|
{
|
||
|
RotateRect0(sb, &ctrl);
|
||
|
|
||
|
if (fCustomDraw)
|
||
|
PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &ctrl, SB_PAGERIGHT, 0, 0, 0);
|
||
|
else
|
||
|
DrawCheckedRect(hdc, &ctrl, crCheck1, crCheck2);
|
||
|
|
||
|
RotateRect0(sb, &ctrl);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//RIGHT ARROW
|
||
|
SetRect(&ctrl, rect->right - butwidth, rect->top, rect->right, rect->bottom);
|
||
|
|
||
|
RotateRect0(sb, &ctrl);
|
||
|
|
||
|
if (fCustomDraw)
|
||
|
PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &ctrl, SB_LINERIGHT, fMouseDownR, fMouseOverR, uRightButFlags & DFCS_INACTIVE);
|
||
|
else
|
||
|
DrawScrollArrow(sb, hdc, &ctrl, uRightButFlags, fMouseDownR, fMouseOverR);
|
||
|
|
||
|
RotateRect0(sb, &ctrl);
|
||
|
}
|
||
|
//not enough room for the scrollbar, so just draw the buttons (scaled in size to fit)
|
||
|
else
|
||
|
{
|
||
|
butwidth = scrollwidth / 2;
|
||
|
|
||
|
//LEFT ARROW
|
||
|
SetRect(&ctrl, rect->left, rect->top, rect->left + butwidth, rect->bottom);
|
||
|
|
||
|
RotateRect0(sb, &ctrl);
|
||
|
if (fCustomDraw)
|
||
|
PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &ctrl, SB_LINELEFT, fMouseDownL, fMouseOverL, uLeftButFlags & DFCS_INACTIVE);
|
||
|
else
|
||
|
DrawScrollArrow(sb, hdc, &ctrl, uLeftButFlags, fMouseDownL, fMouseOverL);
|
||
|
RotateRect0(sb, &ctrl);
|
||
|
|
||
|
//RIGHT ARROW
|
||
|
OffsetRect(&ctrl, scrollwidth - butwidth, 0);
|
||
|
|
||
|
RotateRect0(sb, &ctrl);
|
||
|
if (fCustomDraw)
|
||
|
PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &ctrl, SB_LINERIGHT, fMouseDownR, fMouseOverR, uRightButFlags & DFCS_INACTIVE);
|
||
|
else
|
||
|
DrawScrollArrow(sb, hdc, &ctrl, uRightButFlags, fMouseDownR, fMouseOverR);
|
||
|
RotateRect0(sb, &ctrl);
|
||
|
|
||
|
//if there is a gap between the buttons, fill it with a solid color
|
||
|
//if(butwidth & 0x0001)
|
||
|
if (ctrl.left != rect->left + butwidth)
|
||
|
{
|
||
|
ctrl.left --;
|
||
|
ctrl.right -= butwidth;
|
||
|
RotateRect0(sb, &ctrl);
|
||
|
|
||
|
if (fCustomDraw)
|
||
|
PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &ctrl, SB_PAGERIGHT, 0, 0, 0);
|
||
|
else
|
||
|
DrawCheckedRect(hdc, &ctrl, crCheck1, crCheck2);
|
||
|
|
||
|
RotateRect0(sb, &ctrl);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return fCustomDraw;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Draw a vertical scrollbar using the horizontal draw routine, but
|
||
|
// with the coordinates adjusted accordingly
|
||
|
//
|
||
|
static LRESULT NCDrawVScrollbar(SCROLLBAR *sb, HWND hwnd, HDC hdc, const RECT *rect, UINT uDrawFlags)
|
||
|
{
|
||
|
LRESULT ret;
|
||
|
RECT rc;
|
||
|
|
||
|
rc = *rect;
|
||
|
RotateRect(&rc);
|
||
|
ret = NCDrawHScrollbar(sb, hwnd, hdc, &rc, uDrawFlags);
|
||
|
RotateRect(&rc);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Generic wrapper function for the scrollbar drawing
|
||
|
//
|
||
|
static LRESULT NCDrawScrollbar(SCROLLBAR *sb, HWND hwnd, HDC hdc, const RECT *rect, UINT uDrawFlags)
|
||
|
{
|
||
|
if (sb->nBarType == SB_HORZ)
|
||
|
return NCDrawHScrollbar(sb, hwnd, hdc, rect, uDrawFlags);
|
||
|
else
|
||
|
return NCDrawVScrollbar(sb, hwnd, hdc, rect, uDrawFlags);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Define these two for proper processing of NCPAINT
|
||
|
// NOT needed if we don't bother to mask the scrollbars we draw
|
||
|
// to prevent the old window procedure from accidently drawing over them
|
||
|
//
|
||
|
HDC CoolSB_GetDC(HWND hwnd, WPARAM wParam)
|
||
|
{
|
||
|
// I just can't figure out GetDCEx, so I'll just use this:
|
||
|
return GetWindowDC(hwnd);
|
||
|
}
|
||
|
|
||
|
static LRESULT NCPaint(ScrollWnd *sw, HWND hwnd, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
SCROLLBAR *sb;
|
||
|
HDC hdc;
|
||
|
HRGN hrgn;
|
||
|
RECT winrect, rect;
|
||
|
HRGN clip;
|
||
|
BOOL fUpdateAll = ((LONG)wParam == 1);
|
||
|
BOOL fCustomDraw = FALSE;
|
||
|
LRESULT ret;
|
||
|
DWORD dwStyle;
|
||
|
|
||
|
GetWindowRect(hwnd, &winrect);
|
||
|
|
||
|
//if entire region needs painting, then make a region to cover the entire window
|
||
|
hrgn = (HRGN)wParam;
|
||
|
|
||
|
//hdc = GetWindowDC(hwnd);
|
||
|
hdc = CoolSB_GetDC(hwnd, wParam);
|
||
|
|
||
|
//
|
||
|
// Only draw the horizontal scrollbar if the window is tall enough
|
||
|
//
|
||
|
sb = &sw->sbarHorz;
|
||
|
if (sb->fScrollVisible)
|
||
|
{
|
||
|
//get the screen coordinates of the whole horizontal scrollbar area
|
||
|
GetHScrollRect(sw, hwnd, &rect);
|
||
|
|
||
|
//make the coordinates relative to the window for drawing
|
||
|
OffsetRect(&rect, -winrect.left, -winrect.top);
|
||
|
|
||
|
if (uCurrentScrollbar == SB_HORZ)
|
||
|
fCustomDraw |= NCDrawHScrollbar(sb, hwnd, hdc, &rect, uScrollTimerPortion);
|
||
|
else
|
||
|
fCustomDraw |= NCDrawHScrollbar(sb, hwnd, hdc, &rect, HTSCROLL_NONE);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Only draw the vertical scrollbar if the window is wide enough to accomodate it
|
||
|
//
|
||
|
sb = &sw->sbarVert;
|
||
|
if (sb->fScrollVisible)
|
||
|
{
|
||
|
//get the screen cooridinates of the whole horizontal scrollbar area
|
||
|
GetVScrollRect(sw, hwnd, &rect);
|
||
|
|
||
|
//make the coordinates relative to the window for drawing
|
||
|
OffsetRect(&rect, -winrect.left, -winrect.top);
|
||
|
|
||
|
if (uCurrentScrollbar == SB_VERT)
|
||
|
fCustomDraw |= NCDrawVScrollbar(sb, hwnd, hdc, &rect, uScrollTimerPortion);
|
||
|
else
|
||
|
fCustomDraw |= NCDrawVScrollbar(sb, hwnd, hdc, &rect, HTSCROLL_NONE);
|
||
|
}
|
||
|
|
||
|
//Call the default window procedure for WM_NCPAINT, with the
|
||
|
//new window region. ** region must be in SCREEN coordinates **
|
||
|
dwStyle = GetWindowLongPtr(hwnd, GWL_STYLE);
|
||
|
|
||
|
// If the window has WS_(H-V)SCROLL bits set, we should reset them
|
||
|
// to avoid windows taking the scrollbars into account.
|
||
|
// We temporarily set a flag preventing the subsecuent
|
||
|
// WM_STYLECHANGING/WM_STYLECHANGED to be forwarded to
|
||
|
// the original window procedure
|
||
|
if (dwStyle & (WS_VSCROLL | WS_HSCROLL))
|
||
|
{
|
||
|
sw->bPreventStyleChange = TRUE;
|
||
|
SetWindowLongPtr(hwnd, GWL_STYLE, dwStyle & ~(WS_VSCROLL | WS_HSCROLL));
|
||
|
}
|
||
|
|
||
|
ret = (sw->fWndUnicode) ? CallWindowProcW(sw->oldproc, hwnd, WM_NCPAINT, (WPARAM)hrgn, lParam) :
|
||
|
CallWindowProcA(sw->oldproc, hwnd, WM_NCPAINT, (WPARAM)hrgn, lParam);
|
||
|
if (dwStyle & (WS_VSCROLL | WS_HSCROLL))
|
||
|
{
|
||
|
SetWindowLong(hwnd, GWL_STYLE, dwStyle);
|
||
|
sw->bPreventStyleChange = FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
// DRAW THE DEAD AREA
|
||
|
// only do this if the horizontal and vertical bars are visible
|
||
|
if (sw->sbarHorz.fScrollVisible && sw->sbarVert.fScrollVisible)
|
||
|
{
|
||
|
GetWindowRect(hwnd, &rect);
|
||
|
OffsetRect(&rect, -winrect.left, -winrect.top);
|
||
|
|
||
|
rect.bottom -= sw->cyBottomEdge;
|
||
|
rect.top = rect.bottom - GetScrollMetric(&sw->sbarHorz, SM_CYHORZSB);
|
||
|
|
||
|
if (sw->fLeftScrollbar)
|
||
|
{
|
||
|
rect.left += sw->cxLeftEdge;
|
||
|
rect.right = rect.left + GetScrollMetric(&sw->sbarVert, SM_CXVERTSB);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rect.right -= sw->cxRightEdge;
|
||
|
rect.left = rect.right - GetScrollMetric(&sw->sbarVert, SM_CXVERTSB);
|
||
|
}
|
||
|
|
||
|
if (fCustomDraw)
|
||
|
PostCustomDrawNotify(hwnd, hdc, SB_BOTH, &rect, 32, 0, 0, 0);
|
||
|
else
|
||
|
{
|
||
|
//calculate the position of THIS window's dead area
|
||
|
//with the position of the PARENT window's client rectangle.
|
||
|
//if THIS window has been positioned such that its bottom-right
|
||
|
//corner sits in the parent's bottom-right corner, then we should
|
||
|
//show the sizing-grip.
|
||
|
//Otherwise, assume this window is not in the right place, and
|
||
|
//just draw a blank rectangle
|
||
|
RECT parent;
|
||
|
RECT rect2;
|
||
|
HWND hwndParent = GetParent(hwnd);
|
||
|
|
||
|
GetClientRect(hwndParent, &parent);
|
||
|
MapWindowPoints(hwndParent, 0, (POINT *)&parent, 2);
|
||
|
|
||
|
CopyRect(&rect2, &rect);
|
||
|
OffsetRect(&rect2, winrect.left, winrect.top);
|
||
|
|
||
|
if (!sw->fLeftScrollbar && parent.right == rect2.right + sw->cxRightEdge && parent.bottom == rect2.bottom + sw->cyBottomEdge
|
||
|
|| sw->fLeftScrollbar && parent.left == rect2.left - sw->cxLeftEdge && parent.bottom == rect2.bottom + sw->cyBottomEdge)
|
||
|
drawFrameControl(hdc, &rect, DFC_SCROLL, sw->fLeftScrollbar ? DFCS_SCROLLSIZEGRIPRIGHT : DFCS_SCROLLSIZEGRIP);
|
||
|
else
|
||
|
PaintRect(hdc, &rect, WADlg_getColor(WADLG_SCROLLBAR_DEADAREA_COLOR));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
UNREFERENCED_PARAMETER(clip);
|
||
|
|
||
|
ReleaseDC(hwnd, hdc);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Need to detect if we have clicked in the scrollbar region or not
|
||
|
//
|
||
|
static LRESULT NCHitTest(ScrollWnd *sw, HWND hwnd, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
RECT hrect;
|
||
|
RECT vrect;
|
||
|
POINT pt;
|
||
|
|
||
|
pt.x = GET_X_LPARAM(lParam);
|
||
|
pt.y = GET_Y_LPARAM(lParam);
|
||
|
|
||
|
//work out exactly where the Horizontal and Vertical scrollbars are
|
||
|
GetHScrollRect(sw, hwnd, &hrect);
|
||
|
GetVScrollRect(sw, hwnd, &vrect);
|
||
|
|
||
|
//Clicked in the horizontal scrollbar area
|
||
|
if (sw->sbarHorz.fScrollVisible && PtInRect(&hrect, pt))
|
||
|
{
|
||
|
return HTHSCROLL;
|
||
|
}
|
||
|
//Clicked in the vertical scrollbar area
|
||
|
else if (sw->sbarVert.fScrollVisible && PtInRect(&vrect, pt))
|
||
|
{
|
||
|
return HTVSCROLL;
|
||
|
}
|
||
|
//clicked somewhere else
|
||
|
else
|
||
|
{
|
||
|
return (sw->fWndUnicode) ? CallWindowProcW(sw->oldproc, hwnd, WM_NCHITTEST, wParam, lParam) :
|
||
|
CallWindowProcA(sw->oldproc, hwnd, WM_NCHITTEST, wParam, lParam);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Return a HT* value indicating what part of the scrollbar was clicked
|
||
|
// Rectangle is not adjusted
|
||
|
//
|
||
|
static UINT GetHorzPortion(SCROLLBAR *sb, HWND hwnd, RECT *rect, int x, int y)
|
||
|
{
|
||
|
RECT rc = *rect;
|
||
|
|
||
|
if (y < rc.top || y >= rc.bottom) return HTSCROLL_NONE;
|
||
|
|
||
|
//Now we have the rectangle for the scrollbar itself, so work out
|
||
|
//what part we clicked on.
|
||
|
return GetHorzScrollPortion(sb, hwnd, &rc, x, y);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Just call the horizontal version, with adjusted coordinates
|
||
|
//
|
||
|
static UINT GetVertPortion(SCROLLBAR *sb, HWND hwnd, RECT *rect, int x, int y)
|
||
|
{
|
||
|
UINT ret;
|
||
|
RotateRect(rect);
|
||
|
ret = GetHorzPortion(sb, hwnd, rect, y, x);
|
||
|
RotateRect(rect);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Wrapper function for GetHorzPortion and GetVertPortion
|
||
|
//
|
||
|
static UINT GetPortion(SCROLLBAR *sb, HWND hwnd, RECT *rect, int x, int y)
|
||
|
{
|
||
|
if (sb->nBarType == SB_HORZ)
|
||
|
return GetHorzPortion(sb, hwnd, rect, x, y);
|
||
|
else if (sb->nBarType == SB_VERT)
|
||
|
return GetVertPortion(sb, hwnd, rect, x, y);
|
||
|
else
|
||
|
return HTSCROLL_NONE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Input: rectangle of the total scrollbar area
|
||
|
// Output: adjusted to take the inserted buttons into account
|
||
|
//
|
||
|
static void GetRealHorzScrollRect(SCROLLBAR *sb, RECT *rect)
|
||
|
{
|
||
|
if (sb->fButVisibleBefore) rect->left += sb->nButSizeBefore;
|
||
|
if (sb->fButVisibleAfter) rect->right -= sb->nButSizeAfter;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Input: rectangle of the total scrollbar area
|
||
|
// Output: adjusted to take the inserted buttons into account
|
||
|
//
|
||
|
static void GetRealVertScrollRect(SCROLLBAR *sb, RECT *rect)
|
||
|
{
|
||
|
if (sb->fButVisibleBefore) rect->top += sb->nButSizeBefore;
|
||
|
if (sb->fButVisibleAfter) rect->bottom -= sb->nButSizeAfter;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Decide which type of scrollbar we have before calling
|
||
|
// the real function to do the job
|
||
|
//
|
||
|
static void GetRealScrollRect(SCROLLBAR *sb, RECT *rect)
|
||
|
{
|
||
|
if (sb->nBarType == SB_HORZ)
|
||
|
{
|
||
|
GetRealHorzScrollRect(sb, rect);
|
||
|
}
|
||
|
else if (sb->nBarType == SB_VERT)
|
||
|
{
|
||
|
GetRealVertScrollRect(sb, rect);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Left button click in the non-client area
|
||
|
//
|
||
|
static LRESULT NCLButtonDown(ScrollWnd *sw, HWND hwnd, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
RECT rect, winrect;
|
||
|
HDC hdc;
|
||
|
SCROLLBAR *sb;
|
||
|
POINT pt;
|
||
|
|
||
|
pt.x = GET_X_LPARAM(lParam);
|
||
|
pt.y = GET_Y_LPARAM(lParam);
|
||
|
|
||
|
hwndCurCoolSB = hwnd;
|
||
|
|
||
|
//
|
||
|
// HORIZONTAL SCROLLBAR PROCESSING
|
||
|
//
|
||
|
if (wParam == HTHSCROLL)
|
||
|
{
|
||
|
uScrollTimerMsg = WM_HSCROLL;
|
||
|
uCurrentScrollbar = SB_HORZ;
|
||
|
sb = &sw->sbarHorz;
|
||
|
|
||
|
//get the total area of the normal Horz scrollbar area
|
||
|
GetHScrollRect(sw, hwnd, &rect);
|
||
|
uCurrentScrollPortion = GetHorzPortion(sb, hwnd, &rect, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
||
|
}
|
||
|
//
|
||
|
// VERTICAL SCROLLBAR PROCESSING
|
||
|
//
|
||
|
else if (wParam == HTVSCROLL)
|
||
|
{
|
||
|
uScrollTimerMsg = WM_VSCROLL;
|
||
|
uCurrentScrollbar = SB_VERT;
|
||
|
sb = &sw->sbarVert;
|
||
|
|
||
|
//get the total area of the normal Horz scrollbar area
|
||
|
GetVScrollRect(sw, hwnd, &rect);
|
||
|
uCurrentScrollPortion = GetVertPortion(sb, hwnd, &rect, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
||
|
}
|
||
|
//
|
||
|
// NORMAL PROCESSING
|
||
|
//
|
||
|
else
|
||
|
{
|
||
|
uCurrentScrollPortion = HTSCROLL_NONE;
|
||
|
return (sw->fWndUnicode) ? CallWindowProcW(sw->oldproc, hwnd, WM_NCLBUTTONDOWN, wParam, lParam) :
|
||
|
CallWindowProcA(sw->oldproc, hwnd, WM_NCLBUTTONDOWN, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// we can now share the same code for vertical
|
||
|
// and horizontal scrollbars
|
||
|
//
|
||
|
switch (uCurrentScrollPortion)
|
||
|
{
|
||
|
//inserted buttons to the left/right
|
||
|
case HTSCROLL_THUMB:
|
||
|
|
||
|
//if the scrollbar is disabled, then do no further processing
|
||
|
if (!IsScrollbarActive(sb))
|
||
|
return 0;
|
||
|
|
||
|
GetRealScrollRect(sb, &rect);
|
||
|
RotateRect0(sb, &rect);
|
||
|
CalcThumbSize(sb, &rect, &nThumbSize, &nThumbPos);
|
||
|
RotateRect0(sb, &rect);
|
||
|
|
||
|
//remember the bounding rectangle of the scrollbar work area
|
||
|
rcThumbBounds = rect;
|
||
|
|
||
|
sw->fThumbTracking = TRUE;
|
||
|
sb->scrollInfo.nTrackPos = sb->scrollInfo.nPos;
|
||
|
|
||
|
if (wParam == HTVSCROLL)
|
||
|
nThumbMouseOffset = pt.y - nThumbPos;
|
||
|
else
|
||
|
nThumbMouseOffset = pt.x - nThumbPos;
|
||
|
|
||
|
nLastPos = -sb->scrollInfo.nPos;
|
||
|
nThumbPos0 = nThumbPos;
|
||
|
|
||
|
//if(sb->fFlatScrollbar)
|
||
|
//{
|
||
|
GetWindowRect(hwnd, &winrect);
|
||
|
OffsetRect(&rect, -winrect.left, -winrect.top);
|
||
|
hdc = GetWindowDC(hwnd);
|
||
|
NCDrawScrollbar(sb, hwnd, hdc, &rect, HTSCROLL_THUMB);
|
||
|
ReleaseDC(hwnd, hdc);
|
||
|
//}
|
||
|
|
||
|
break;
|
||
|
|
||
|
//Any part of the scrollbar
|
||
|
case HTSCROLL_LEFT:
|
||
|
if (sb->fScrollFlags & ESB_DISABLE_LEFT) return 0;
|
||
|
else goto target1;
|
||
|
|
||
|
case HTSCROLL_RIGHT:
|
||
|
if (sb->fScrollFlags & ESB_DISABLE_RIGHT) return 0;
|
||
|
else goto target1;
|
||
|
|
||
|
goto target1;
|
||
|
|
||
|
case HTSCROLL_PAGELEFT: case HTSCROLL_PAGERIGHT:
|
||
|
|
||
|
target1:
|
||
|
|
||
|
//if the scrollbar is disabled, then do no further processing
|
||
|
if (!IsScrollbarActive(sb))
|
||
|
break;
|
||
|
|
||
|
//ajust the horizontal rectangle to NOT include
|
||
|
//any inserted buttons
|
||
|
GetRealScrollRect(sb, &rect);
|
||
|
|
||
|
SendScrollMessage(hwnd, uScrollTimerMsg, uCurrentScrollPortion, 0);
|
||
|
|
||
|
// Check what area the mouse is now over :
|
||
|
// If the scroll thumb has moved under the mouse in response to
|
||
|
// a call to SetScrollPos etc, then we don't hilight the scrollbar margin
|
||
|
if (uCurrentScrollbar == SB_HORZ)
|
||
|
uScrollTimerPortion = GetHorzScrollPortion(sb, hwnd, &rect, pt.x, pt.y);
|
||
|
else
|
||
|
uScrollTimerPortion = GetVertScrollPortion(sb, hwnd, &rect, pt.x, pt.y);
|
||
|
|
||
|
GetWindowRect(hwnd, &winrect);
|
||
|
OffsetRect(&rect, -winrect.left, -winrect.top);
|
||
|
hdc = GetWindowDC(hwnd);
|
||
|
|
||
|
//if we aren't hot-tracking, then don't highlight
|
||
|
//the scrollbar thumb unless we click on it
|
||
|
if (uScrollTimerPortion == HTSCROLL_THUMB)
|
||
|
uScrollTimerPortion = HTSCROLL_NONE;
|
||
|
NCDrawScrollbar(sb, hwnd, hdc, &rect, uScrollTimerPortion);
|
||
|
ReleaseDC(hwnd, hdc);
|
||
|
|
||
|
//Post the scroll message!!!!
|
||
|
uScrollTimerPortion = uCurrentScrollPortion;
|
||
|
|
||
|
//set a timer going on the first click.
|
||
|
//if this one expires, then we can start off a more regular timer
|
||
|
//to generate the auto-scroll behaviour
|
||
|
uScrollTimerId = SetTimer(hwnd, COOLSB_TIMERID1, COOLSB_TIMERINTERVAL1, 0);
|
||
|
sw->update();
|
||
|
break;
|
||
|
default:
|
||
|
return (sw->fWndUnicode) ? CallWindowProcW(sw->oldproc, hwnd, WM_NCLBUTTONDOWN, wParam, lParam) :
|
||
|
CallWindowProcA(sw->oldproc, hwnd, WM_NCLBUTTONDOWN, wParam, lParam);
|
||
|
//return 0;
|
||
|
}
|
||
|
|
||
|
SetCapture(hwnd);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Left button released
|
||
|
//
|
||
|
static LRESULT LButtonUp(ScrollWnd *sw, HWND hwnd, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
RECT rect;
|
||
|
POINT pt;
|
||
|
RECT winrect;
|
||
|
|
||
|
//current scrollportion is the button that we clicked down on
|
||
|
if (uCurrentScrollPortion != HTSCROLL_NONE)
|
||
|
{
|
||
|
SCROLLBAR *sb = &sw->sbarHorz;
|
||
|
lParam = GetMessagePos();
|
||
|
ReleaseCapture();
|
||
|
|
||
|
GetWindowRect(hwnd, &winrect);
|
||
|
pt.x = GET_X_LPARAM(lParam);
|
||
|
pt.y = GET_Y_LPARAM(lParam);
|
||
|
|
||
|
//emulate the mouse input on a scrollbar here...
|
||
|
if (uCurrentScrollbar == SB_HORZ)
|
||
|
{
|
||
|
//get the total area of the normal Horz scrollbar area
|
||
|
sb = &sw->sbarHorz;
|
||
|
GetHScrollRect(sw, hwnd, &rect);
|
||
|
}
|
||
|
else if (uCurrentScrollbar == SB_VERT)
|
||
|
{
|
||
|
//get the total area of the normal Horz scrollbar area
|
||
|
sb = &sw->sbarVert;
|
||
|
GetVScrollRect(sw, hwnd, &rect);
|
||
|
}
|
||
|
|
||
|
//we need to do different things depending on if the
|
||
|
//user is activating the scrollbar itself, or one of
|
||
|
//the inserted buttons
|
||
|
switch (uCurrentScrollPortion)
|
||
|
{
|
||
|
//The scrollbar is active
|
||
|
case HTSCROLL_LEFT: case HTSCROLL_RIGHT:
|
||
|
case HTSCROLL_PAGELEFT: case HTSCROLL_PAGERIGHT:
|
||
|
case HTSCROLL_NONE:
|
||
|
|
||
|
KillTimer(hwnd, uScrollTimerId);
|
||
|
|
||
|
case HTSCROLL_THUMB:
|
||
|
|
||
|
sw->update();
|
||
|
|
||
|
//In case we were thumb tracking, make sure we stop NOW
|
||
|
if (sw->fThumbTracking == TRUE)
|
||
|
{
|
||
|
SendScrollMessage(hwnd, uScrollTimerMsg, SB_THUMBPOSITION, nLastPos);
|
||
|
sw->fThumbTracking = FALSE;
|
||
|
}
|
||
|
|
||
|
//send the SB_ENDSCROLL message now that scrolling has finished
|
||
|
SendScrollMessage(hwnd, uScrollTimerMsg, SB_ENDSCROLL, 0);
|
||
|
|
||
|
//adjust the total scroll area to become where the scrollbar
|
||
|
//really is (take into account the inserted buttons)
|
||
|
GetRealScrollRect(sb, &rect);
|
||
|
OffsetRect(&rect, -winrect.left, -winrect.top);
|
||
|
HDC hdc = GetWindowDC(hwnd);
|
||
|
|
||
|
//draw whichever scrollbar sb is
|
||
|
NCDrawScrollbar(sb, hwnd, hdc, &rect, HTSCROLL_NORMAL);
|
||
|
|
||
|
ReleaseDC(hwnd, hdc);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//reset our state to default
|
||
|
uCurrentScrollPortion = HTSCROLL_NONE;
|
||
|
uScrollTimerPortion = HTSCROLL_NONE;
|
||
|
uScrollTimerId = 0;
|
||
|
|
||
|
uScrollTimerMsg = 0;
|
||
|
uCurrentScrollbar = COOLSB_NONE;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/*
|
||
|
// Can't remember why I did this!
|
||
|
if(GetCapture() == hwnd)
|
||
|
{
|
||
|
ReleaseCapture();
|
||
|
}*/
|
||
|
}
|
||
|
|
||
|
//sw->update();
|
||
|
|
||
|
return (sw->fWndUnicode) ? CallWindowProcW(sw->oldproc, hwnd, WM_LBUTTONUP, wParam, lParam) :
|
||
|
CallWindowProcA(sw->oldproc, hwnd, WM_LBUTTONUP, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// This function is called whenever the mouse is moved and
|
||
|
// we are dragging the scrollbar thumb about.
|
||
|
//
|
||
|
static LRESULT ThumbTrackHorz(SCROLLBAR *sbar, HWND hwnd, int x, int y)
|
||
|
{
|
||
|
POINT pt;
|
||
|
RECT rc, winrect, rc2;
|
||
|
COLORREF crCheck1 = GetSBForeColor();
|
||
|
COLORREF crCheck2 = GetSBBackColor();
|
||
|
HDC hdc;
|
||
|
int thumbpos = nThumbPos;
|
||
|
int pos;
|
||
|
int siMaxMin = 0;
|
||
|
UINT flatflag = sbar->fFlatScrollbar ? BF_FLAT : 0;
|
||
|
BOOL fCustomDraw = FALSE;
|
||
|
|
||
|
SCROLLINFO *si;
|
||
|
si = &sbar->scrollInfo;
|
||
|
|
||
|
pt.x = x;
|
||
|
pt.y = y;
|
||
|
|
||
|
//draw the thumb at whatever position
|
||
|
rc = rcThumbBounds;
|
||
|
|
||
|
SetRect(&rc2, rc.left - THUMBTRACK_SNAPDIST*2, rc.top - THUMBTRACK_SNAPDIST,
|
||
|
rc.right + THUMBTRACK_SNAPDIST*2, rc.bottom + THUMBTRACK_SNAPDIST);
|
||
|
|
||
|
rc.left += GetScrollMetric(sbar, SM_CXHORZSB);
|
||
|
rc.right -= GetScrollMetric(sbar, SM_CXHORZSB);
|
||
|
|
||
|
//if the mouse is not in a suitable distance of the scrollbar,
|
||
|
//then "snap" the thumb back to its initial position
|
||
|
#ifdef SNAP_THUMB_BACK
|
||
|
if (!PtInRect(&rc2, pt))
|
||
|
{
|
||
|
thumbpos = nThumbPos0;
|
||
|
}
|
||
|
//otherwise, move the thumb to where the mouse is
|
||
|
else
|
||
|
#endif //SNAP_THUMB_BACK
|
||
|
{
|
||
|
//keep the thumb within the scrollbar limits
|
||
|
thumbpos = pt.x - nThumbMouseOffset;
|
||
|
if (thumbpos < rc.left) thumbpos = rc.left;
|
||
|
if (thumbpos > rc.right - nThumbSize) thumbpos = rc.right - nThumbSize;
|
||
|
}
|
||
|
|
||
|
GetWindowRect(hwnd, &winrect);
|
||
|
|
||
|
if (sbar->nBarType == SB_VERT)
|
||
|
RotateRect(&winrect);
|
||
|
|
||
|
hdc = GetWindowDC(hwnd);
|
||
|
|
||
|
OffsetRect(&rc, -winrect.left, -winrect.top);
|
||
|
thumbpos -= winrect.left;
|
||
|
|
||
|
//draw the margin before the thumb
|
||
|
SetRect(&rc2, rc.left, rc.top, thumbpos, rc.bottom);
|
||
|
RotateRect0(sbar, &rc2);
|
||
|
|
||
|
if (fCustomDraw)
|
||
|
PostCustomDrawNotify(hwnd, hdc, sbar->nBarType, &rc2, SB_PAGELEFT, 0, 0, 0);
|
||
|
else
|
||
|
DrawCheckedRect(hdc, &rc2, crCheck1, crCheck2);
|
||
|
|
||
|
RotateRect0(sbar, &rc2);
|
||
|
|
||
|
//draw the margin after the thumb
|
||
|
SetRect(&rc2, thumbpos + nThumbSize, rc.top, rc.right, rc.bottom);
|
||
|
|
||
|
RotateRect0(sbar, &rc2);
|
||
|
|
||
|
if (fCustomDraw)
|
||
|
PostCustomDrawNotify(hwnd, hdc, sbar->nBarType, &rc2, SB_PAGERIGHT, 0, 0, 0);
|
||
|
else
|
||
|
DrawCheckedRect(hdc, &rc2, crCheck1, crCheck2);
|
||
|
|
||
|
RotateRect0(sbar, &rc2);
|
||
|
|
||
|
//finally draw the thumb itelf. This is how it looks on win2000, anyway
|
||
|
SetRect(&rc2, thumbpos, rc.top, thumbpos + nThumbSize, rc.bottom);
|
||
|
|
||
|
RotateRect0(sbar, &rc2);
|
||
|
|
||
|
if (fCustomDraw)
|
||
|
PostCustomDrawNotify(hwnd, hdc, sbar->nBarType, &rc2, SB_THUMBTRACK, TRUE, TRUE, FALSE);
|
||
|
else
|
||
|
{
|
||
|
DrawBlankButton(hdc, &rc2, flatflag, 1, sbar->nBarType == SB_VERT);
|
||
|
}
|
||
|
|
||
|
RotateRect0(sbar, &rc2);
|
||
|
ReleaseDC(hwnd, hdc);
|
||
|
|
||
|
//post a SB_TRACKPOS message!!!
|
||
|
siMaxMin = si->nMax - si->nMin;
|
||
|
|
||
|
if (siMaxMin > 0)
|
||
|
pos = MulDiv(thumbpos - rc.left, siMaxMin - si->nPage + 1, rc.right - rc.left - nThumbSize);
|
||
|
else
|
||
|
pos = thumbpos - rc.left;
|
||
|
|
||
|
if (pos != nLastPos)
|
||
|
{
|
||
|
|
||
|
if (sbar->flags & SCROLLBAR_LISTVIEW)
|
||
|
{
|
||
|
// only for listviews
|
||
|
if (sbar->nBarType == SB_HORZ)
|
||
|
{
|
||
|
SCROLLINFO info;
|
||
|
info.cbSize = sizeof(SCROLLINFO);
|
||
|
info.fMask = SIF_TRACKPOS;
|
||
|
if (GetScrollInfo(hwnd, SB_HORZ, &info))
|
||
|
{
|
||
|
int nPos = info.nTrackPos;
|
||
|
SendMessage(hwnd, LVM_SCROLL, pos - nPos, 0);
|
||
|
SetScrollInfo(hwnd, sbar->nBarType, &info, FALSE);
|
||
|
}
|
||
|
}
|
||
|
else if (sbar->nBarType == SB_VERT)
|
||
|
{
|
||
|
SCROLLINFO info;
|
||
|
info.cbSize = sizeof(SCROLLINFO);
|
||
|
info.fMask = SIF_TRACKPOS;
|
||
|
if (GetScrollInfo(hwnd, SB_VERT, &info))
|
||
|
{
|
||
|
int nPos = info.nTrackPos;
|
||
|
SendMessage(hwnd, LVM_SCROLL, 0, (pos - nPos)*14); //BIG FUCKO: get the text height size
|
||
|
SetScrollInfo(hwnd, sbar->nBarType, &info, FALSE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
si->nTrackPos = pos;
|
||
|
SCROLLINFO info;
|
||
|
info.cbSize = sizeof(SCROLLINFO);
|
||
|
info.fMask = SIF_TRACKPOS;
|
||
|
info.nTrackPos = pos;
|
||
|
|
||
|
SetScrollInfo(hwnd, sbar->nBarType, &info, FALSE);
|
||
|
SendScrollMessage(hwnd, uScrollTimerMsg, SB_THUMBTRACK, pos);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
nLastPos = pos;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// remember to rotate the thumb bounds rectangle!!
|
||
|
//
|
||
|
static LRESULT ThumbTrackVert(SCROLLBAR *sb, HWND hwnd, int x, int y)
|
||
|
{
|
||
|
//sw->swapcoords = TRUE;
|
||
|
RotateRect(&rcThumbBounds);
|
||
|
ThumbTrackHorz(sb, hwnd, y, x);
|
||
|
RotateRect(&rcThumbBounds);
|
||
|
//sw->swapcoords = FALSE;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Called when we have set the capture from the NCLButtonDown(...)
|
||
|
//
|
||
|
static LRESULT MouseMove(ScrollWnd *sw, HWND hwnd, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
RECT rect;
|
||
|
POINT pt;
|
||
|
RECT winrect;
|
||
|
|
||
|
if (sw->fThumbTracking == TRUE)
|
||
|
{
|
||
|
int x, y;
|
||
|
lParam = GetMessagePos();
|
||
|
x = GET_X_LPARAM(lParam);
|
||
|
y = GET_Y_LPARAM(lParam);
|
||
|
|
||
|
if (uCurrentScrollbar == SB_HORZ)
|
||
|
return ThumbTrackHorz(&sw->sbarHorz, hwnd, x, y);
|
||
|
|
||
|
|
||
|
else if (uCurrentScrollbar == SB_VERT)
|
||
|
return ThumbTrackVert(&sw->sbarVert, hwnd, x, y);
|
||
|
}
|
||
|
|
||
|
if (uCurrentScrollPortion == HTSCROLL_NONE)
|
||
|
{
|
||
|
|
||
|
return (sw->fWndUnicode) ? CallWindowProcW(sw->oldproc, hwnd, WM_MOUSEMOVE, wParam, lParam) :
|
||
|
CallWindowProcA(sw->oldproc, hwnd, WM_MOUSEMOVE, wParam, lParam);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LPARAM nlParam;
|
||
|
SCROLLBAR *sb = &sw->sbarHorz;
|
||
|
|
||
|
nlParam = GetMessagePos();
|
||
|
|
||
|
GetWindowRect(hwnd, &winrect);
|
||
|
|
||
|
pt.x = GET_X_LPARAM(nlParam);
|
||
|
pt.y = GET_Y_LPARAM(nlParam);
|
||
|
|
||
|
//emulate the mouse input on a scrollbar here...
|
||
|
if (uCurrentScrollbar == SB_HORZ)
|
||
|
{
|
||
|
sb = &sw->sbarHorz;
|
||
|
}
|
||
|
else if (uCurrentScrollbar == SB_VERT)
|
||
|
{
|
||
|
sb = &sw->sbarVert;
|
||
|
}
|
||
|
|
||
|
//get the total area of the normal scrollbar area
|
||
|
GetScrollRect(sw, sb->nBarType, hwnd, &rect);
|
||
|
|
||
|
//see if we clicked in the inserted buttons / normal scrollbar
|
||
|
//thisportion = GetPortion(sb, hwnd, &rect, LOWORD(lParam), HIWORD(lParam));
|
||
|
UINT thisportion = GetPortion(sb, hwnd, &rect, pt.x, pt.y);
|
||
|
|
||
|
//we need to do different things depending on if the
|
||
|
//user is activating the scrollbar itself, or one of
|
||
|
//the inserted buttons
|
||
|
static UINT lastportion = 0;
|
||
|
switch (uCurrentScrollPortion)
|
||
|
{
|
||
|
//The scrollbar is active
|
||
|
case HTSCROLL_LEFT: case HTSCROLL_RIGHT: case HTSCROLL_THUMB:
|
||
|
case HTSCROLL_PAGELEFT: case HTSCROLL_PAGERIGHT:
|
||
|
case HTSCROLL_NONE:
|
||
|
|
||
|
//adjust the total scroll area to become where the scrollbar
|
||
|
//really is (take into account the inserted buttons)
|
||
|
GetRealScrollRect(sb, &rect);
|
||
|
|
||
|
OffsetRect(&rect, -winrect.left, -winrect.top);
|
||
|
HDC hdc = GetWindowDC(hwnd);
|
||
|
|
||
|
if (thisportion != uCurrentScrollPortion)
|
||
|
{
|
||
|
uScrollTimerPortion = HTSCROLL_NONE;
|
||
|
|
||
|
if (lastportion != thisportion)
|
||
|
NCDrawScrollbar(sb, hwnd, hdc, &rect, HTSCROLL_NORMAL);
|
||
|
}
|
||
|
//otherwise, draw the button in its depressed / clicked state
|
||
|
else
|
||
|
{
|
||
|
uScrollTimerPortion = uCurrentScrollPortion;
|
||
|
|
||
|
if (lastportion != thisportion)
|
||
|
NCDrawScrollbar(sb, hwnd, hdc, &rect, thisportion);
|
||
|
}
|
||
|
|
||
|
ReleaseDC(hwnd, hdc);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
lastportion = thisportion;
|
||
|
|
||
|
//must return zero here, because we might get cursor anomilies
|
||
|
//CallWindowProc(sw->oldproc, hwnd, WM_MOUSEMOVE, wParam, lParam);
|
||
|
return 0;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We must allocate from in the non-client area for our scrollbars
|
||
|
// Call the default window procedure first, to get the borders (if any)
|
||
|
// allocated some space, then allocate the space for the scrollbars
|
||
|
// if they fit
|
||
|
//
|
||
|
static LRESULT NCCalcSize(ScrollWnd *sw, HWND hwnd, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
NCCALCSIZE_PARAMS *nccsp;
|
||
|
RECT *rect;
|
||
|
RECT oldrect;
|
||
|
//BOOL fCalcValidRects = (wParam == TRUE);
|
||
|
SCROLLBAR *sb;
|
||
|
LRESULT ret;
|
||
|
DWORD dwStyle;
|
||
|
|
||
|
//Regardless of the value of fCalcValidRects, the first rectangle
|
||
|
//in the array specified by the rgrc structure member of the
|
||
|
//NCCALCSIZE_PARAMS structure contains the coordinates of the window,
|
||
|
//so we can use the exact same code to modify this rectangle, when
|
||
|
//wParam is TRUE and when it is FALSE.
|
||
|
nccsp = (NCCALCSIZE_PARAMS *)lParam;
|
||
|
rect = &nccsp->rgrc[0];
|
||
|
oldrect = *rect;
|
||
|
|
||
|
dwStyle = GetWindowLong(hwnd, GWL_STYLE);
|
||
|
|
||
|
// TURN OFF SCROLL-STYLES.
|
||
|
if (dwStyle & (WS_VSCROLL | WS_HSCROLL))
|
||
|
{
|
||
|
sw->bPreventStyleChange = TRUE;
|
||
|
SetWindowLong(hwnd, GWL_STYLE, dwStyle & ~(WS_VSCROLL | WS_HSCROLL));
|
||
|
}
|
||
|
|
||
|
//call the default procedure to get the borders allocated
|
||
|
|
||
|
ret = (sw->fWndUnicode) ? CallWindowProcW(sw->oldproc, hwnd, WM_NCCALCSIZE, wParam, lParam) :
|
||
|
CallWindowProcA(sw->oldproc, hwnd, WM_NCCALCSIZE, wParam, lParam);
|
||
|
|
||
|
// RESTORE PREVIOUS STYLES (if present at all)
|
||
|
if (dwStyle & (WS_VSCROLL | WS_HSCROLL))
|
||
|
{
|
||
|
SetWindowLong(hwnd, GWL_STYLE, dwStyle);
|
||
|
sw->bPreventStyleChange = FALSE;
|
||
|
}
|
||
|
|
||
|
// calculate what the size of each window border is,
|
||
|
sw->cxLeftEdge = rect->left - oldrect.left;
|
||
|
sw->cxRightEdge = oldrect.right - rect->right;
|
||
|
sw->cyTopEdge = rect->top - oldrect.top;
|
||
|
sw->cyBottomEdge = oldrect.bottom - rect->bottom;
|
||
|
|
||
|
sb = &sw->sbarHorz;
|
||
|
|
||
|
//if there is room, allocate some space for the horizontal scrollbar
|
||
|
//NOTE: Change the ">" to a ">=" to make the horz bar totally fill the
|
||
|
//window before disappearing
|
||
|
if ((sb->fScrollFlags & CSBS_VISIBLE) &&
|
||
|
#ifdef COOLSB_FILLWINDOW
|
||
|
rect->bottom - rect->top >= GetScrollMetric(sb, SM_CYHORZSB))
|
||
|
#else
|
||
|
rect->bottom - rect->top > GetScrollMetric(sb, SM_CYHORZSB))
|
||
|
#endif
|
||
|
{
|
||
|
rect->bottom -= GetScrollMetric(sb, SM_CYHORZSB);
|
||
|
sb->fScrollVisible = TRUE;
|
||
|
}
|
||
|
else
|
||
|
sb->fScrollVisible = FALSE;
|
||
|
|
||
|
sb = &sw->sbarVert;
|
||
|
|
||
|
//if there is room, allocate some space for the vertical scrollbar
|
||
|
if ((sb->fScrollFlags & CSBS_VISIBLE) &&
|
||
|
rect->right - rect->left >= GetScrollMetric(sb, SM_CXVERTSB))
|
||
|
{
|
||
|
if (sw->fLeftScrollbar)
|
||
|
rect->left += GetScrollMetric(sb, SM_CXVERTSB);
|
||
|
else
|
||
|
rect->right -= GetScrollMetric(sb, SM_CXVERTSB);
|
||
|
|
||
|
sb->fScrollVisible = TRUE;
|
||
|
}
|
||
|
else
|
||
|
sb->fScrollVisible = FALSE;
|
||
|
|
||
|
//don't return a value unless we actually modify the other rectangles
|
||
|
//in the NCCALCSIZE_PARAMS structure. In this case, we return 0
|
||
|
//no matter what the value of fCalcValidRects is
|
||
|
return ret;//FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// used for hot-tracking over the scroll buttons
|
||
|
//
|
||
|
static LRESULT NCMouseMove(ScrollWnd *sw, HWND hwnd, WPARAM wHitTest, LPARAM lParam)
|
||
|
{
|
||
|
//install a timer for the mouse-over events, if the mouse moves
|
||
|
//over one of the scrollbars
|
||
|
return (sw->fWndUnicode) ? CallWindowProcW(sw->oldproc, hwnd, WM_NCMOUSEMOVE, wHitTest, lParam) :
|
||
|
CallWindowProcA(sw->oldproc, hwnd, WM_NCMOUSEMOVE, wHitTest, lParam);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Timer routine to generate scrollbar messages
|
||
|
//
|
||
|
static LRESULT CoolSB_Timer(ScrollWnd *swnd, HWND hwnd, WPARAM wTimerId, LPARAM lParam)
|
||
|
{
|
||
|
//let all timer messages go past if we don't have a timer installed ourselves
|
||
|
if (uScrollTimerId == 0 && uMouseOverId == 0)
|
||
|
{
|
||
|
return (swnd->fWndUnicode) ? CallWindowProcW(swnd->oldproc, hwnd, WM_TIMER, wTimerId, lParam) :
|
||
|
CallWindowProcA(swnd->oldproc, hwnd, WM_TIMER, wTimerId, lParam);
|
||
|
|
||
|
}
|
||
|
|
||
|
//if the first timer goes off, then we can start a more
|
||
|
//regular timer interval to auto-generate scroll messages
|
||
|
//this gives a slight pause between first pressing the scroll arrow, and the
|
||
|
//actual scroll starting
|
||
|
if (wTimerId == COOLSB_TIMERID1)
|
||
|
{
|
||
|
KillTimer(hwnd, uScrollTimerId);
|
||
|
uScrollTimerId = SetTimer(hwnd, COOLSB_TIMERID2, COOLSB_TIMERINTERVAL2, 0);
|
||
|
return 0;
|
||
|
}
|
||
|
//send the scrollbar message repeatedly
|
||
|
else if (wTimerId == COOLSB_TIMERID2)
|
||
|
{
|
||
|
//need to process a spoof WM_MOUSEMOVE, so that
|
||
|
//we know where the mouse is each time the scroll timer goes off.
|
||
|
//This is so we can stop sending scroll messages if the thumb moves
|
||
|
//under the mouse.
|
||
|
POINT pt;
|
||
|
GetCursorPos(&pt);
|
||
|
ScreenToClient(hwnd, &pt);
|
||
|
|
||
|
MouseMove(swnd, hwnd, MK_LBUTTON, MAKELPARAM(pt.x, pt.y));
|
||
|
|
||
|
if (uScrollTimerPortion != HTSCROLL_NONE)
|
||
|
SendScrollMessage(hwnd, uScrollTimerMsg, uScrollTimerPortion, 0);
|
||
|
|
||
|
swnd->update();
|
||
|
return 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return (swnd->fWndUnicode) ? CallWindowProcW(swnd->oldproc, hwnd, WM_TIMER, wTimerId, lParam) :
|
||
|
CallWindowProcA(swnd->oldproc, hwnd, WM_TIMER, wTimerId, lParam);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We must intercept any calls to SetWindowLong, to check if
|
||
|
// left-scrollbars are taking effect or not
|
||
|
//
|
||
|
static LRESULT CoolSB_StyleChange(ScrollWnd *swnd, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
STYLESTRUCT *ss = (STYLESTRUCT *)lParam;
|
||
|
|
||
|
if (wParam == GWL_EXSTYLE)
|
||
|
{
|
||
|
if (ss->styleNew & WS_EX_LEFTSCROLLBAR)
|
||
|
swnd->fLeftScrollbar = TRUE;
|
||
|
else
|
||
|
swnd->fLeftScrollbar = FALSE;
|
||
|
}
|
||
|
|
||
|
return (swnd->fWndUnicode) ? CallWindowProcW(swnd->oldproc, hwnd, msg, wParam, lParam) :
|
||
|
CallWindowProcA(swnd->oldproc, hwnd, msg, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
static UINT curTool = -1;
|
||
|
static LRESULT CoolSB_Notify(ScrollWnd *swnd, HWND hwnd, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
return (swnd->fWndUnicode) ? CallWindowProcW(swnd->oldproc, hwnd, WM_NOTIFY, wParam, lParam) :
|
||
|
CallWindowProcA(swnd->oldproc, hwnd, WM_NOTIFY, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
static LRESULT SendToolTipMessage0(HWND hwndTT, UINT message, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
return SendMessage(hwndTT, message, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
#ifdef COOLSB_TOOLTIPS
|
||
|
#define SendToolTipMessage SendToolTipMessage0
|
||
|
#else
|
||
|
#define SendToolTipMessage 1 ? (void)0 : SendToolTipMessage0
|
||
|
#endif
|
||
|
|
||
|
|
||
|
//
|
||
|
// We must intercept any calls to SetWindowLong, to make sure that
|
||
|
// the user does not set the WS_VSCROLL or WS_HSCROLL styles
|
||
|
//
|
||
|
static LRESULT CoolSB_SetCursor(ScrollWnd *swnd, HWND hwnd, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
return (swnd->fWndUnicode) ? CallWindowProcW(swnd->oldproc, hwnd, WM_SETCURSOR, wParam, lParam) :
|
||
|
CallWindowProcA(swnd->oldproc, hwnd, WM_SETCURSOR, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// CoolScrollbar subclass procedure.
|
||
|
// Handle all messages needed to mimick normal windows scrollbars
|
||
|
//
|
||
|
LRESULT CALLBACK CoolSBWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
WNDPROC oldproc;
|
||
|
|
||
|
ScrollWnd *swnd = GetScrollWndFromHwnd(hwnd);
|
||
|
|
||
|
if (!swnd || !swnd->oldproc)
|
||
|
{
|
||
|
return (IsWindowUnicode(hwnd)) ? DefWindowProcW(hwnd, message, wParam, lParam) :
|
||
|
DefWindowProcA(hwnd, message, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
switch (message)
|
||
|
{
|
||
|
case WM_NCDESTROY:
|
||
|
//this should NEVER be called, because the user
|
||
|
//should have called Uninitialize() themselves.
|
||
|
|
||
|
//However, if the user tries to call Uninitialize()..
|
||
|
//after this window is destroyed, this window's entry in the lookup
|
||
|
//table will not be there, and the call will fail
|
||
|
oldproc = swnd->oldproc;
|
||
|
delete(swnd);
|
||
|
|
||
|
//we must call the original window procedure, otherwise it
|
||
|
//will never get the WM_NCDESTROY message, and it wouldn't
|
||
|
//be able to clean up etc.
|
||
|
return (IsWindowUnicode(hwnd)) ? CallWindowProcW(oldproc, hwnd, message, wParam, lParam) : CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
|
||
|
|
||
|
case WM_NCCALCSIZE:
|
||
|
return NCCalcSize(swnd, hwnd, wParam, lParam);
|
||
|
|
||
|
case WM_NCPAINT:
|
||
|
return NCPaint(swnd, hwnd, wParam, lParam);
|
||
|
|
||
|
case WM_NCHITTEST:
|
||
|
return NCHitTest(swnd, hwnd, wParam, lParam);
|
||
|
|
||
|
case WM_NCRBUTTONDOWN: case WM_NCRBUTTONUP:
|
||
|
case WM_NCMBUTTONDOWN: case WM_NCMBUTTONUP:
|
||
|
if (wParam == HTHSCROLL || wParam == HTVSCROLL)
|
||
|
return 0;
|
||
|
else
|
||
|
break;
|
||
|
|
||
|
case WM_NCLBUTTONDBLCLK:
|
||
|
//TRACE("WM_NCLBUTTONDBLCLK %d\n", count++);
|
||
|
if (wParam == HTHSCROLL || wParam == HTVSCROLL)
|
||
|
return NCLButtonDown(swnd, hwnd, wParam, lParam);
|
||
|
else
|
||
|
break;
|
||
|
|
||
|
case WM_NCLBUTTONDOWN:
|
||
|
//TRACE("WM_NCLBUTTONDOWN%d\n", count++);
|
||
|
return NCLButtonDown(swnd, hwnd, wParam, lParam);
|
||
|
|
||
|
|
||
|
case WM_LBUTTONUP:
|
||
|
//TRACE("WM_LBUTTONUP %d\n", count++);
|
||
|
return LButtonUp(swnd, hwnd, wParam, lParam);
|
||
|
|
||
|
case WM_NOTIFY:
|
||
|
return CoolSB_Notify(swnd, hwnd, wParam, lParam);
|
||
|
|
||
|
//Mouse moves are received when we set the mouse capture,
|
||
|
//even when the mouse moves over the non-client area
|
||
|
case WM_MOUSEMOVE:
|
||
|
//TRACE("WM_MOUSEMOVE %d\n", count++);
|
||
|
return MouseMove(swnd, hwnd, wParam, lParam);
|
||
|
|
||
|
case WM_TIMER:
|
||
|
return CoolSB_Timer(swnd, hwnd, wParam, lParam);
|
||
|
|
||
|
//case WM_STYLECHANGING:
|
||
|
// return CoolSB_StyleChange(swnd, hwnd, WM_STYLECHANGING, wParam, lParam);
|
||
|
case WM_STYLECHANGED:
|
||
|
if (swnd->bPreventStyleChange)
|
||
|
{
|
||
|
// the NCPAINT handler has told us to eat this message!
|
||
|
return 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (message == WM_STYLECHANGED)
|
||
|
return CoolSB_StyleChange(swnd, hwnd, WM_STYLECHANGED, wParam, lParam);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case WM_NCMOUSEMOVE:
|
||
|
{
|
||
|
static LPARAM lastpos = -1;
|
||
|
|
||
|
//TRACE("WM_NCMOUSEMOVE %d\n", count++);
|
||
|
|
||
|
//The problem with NCMOUSEMOVE is that it is sent continuously
|
||
|
//even when the mouse is stationary (under win2000 / win98)
|
||
|
//
|
||
|
//Tooltips don't like being sent a continous stream of mouse-moves
|
||
|
//if the cursor isn't moving, because they will think that the mouse
|
||
|
//is moving position, and the internal timer will never expire
|
||
|
//
|
||
|
if (lastpos != lParam)
|
||
|
{
|
||
|
lastpos = lParam;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NCMouseMove(swnd, hwnd, wParam, lParam);
|
||
|
|
||
|
|
||
|
case WM_SETCURSOR:
|
||
|
return CoolSB_SetCursor(swnd, hwnd, wParam, lParam);
|
||
|
|
||
|
case WM_CAPTURECHANGED:
|
||
|
break;
|
||
|
|
||
|
case WM_ERASEBKGND:
|
||
|
if (swnd && !swnd->fThumbTracking && uCurrentScrollPortion == HTSCROLL_NONE)
|
||
|
{
|
||
|
//disable windows scrollbar painting (fixes gfx repainting weirdness)
|
||
|
int style = GetWindowLong(hwnd, GWL_STYLE);
|
||
|
if (style&(WS_HSCROLL | WS_VSCROLL))
|
||
|
{
|
||
|
SetWindowLong(hwnd, GWL_STYLE, style&~(WS_HSCROLL | WS_VSCROLL));
|
||
|
}
|
||
|
|
||
|
LRESULT ret = (swnd->fWndUnicode) ? CallWindowProcW(swnd->oldproc, hwnd, message, wParam, lParam) :
|
||
|
CallWindowProcA(swnd->oldproc, hwnd, message, wParam, lParam);
|
||
|
swnd->update();
|
||
|
return ret;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
//needed if we want mousewheel to work properly because we disable the styles in WM_ERASEBKGND...
|
||
|
case WM_MOUSEWHEEL:
|
||
|
{
|
||
|
int style = GetWindowLong(hwnd, GWL_STYLE);
|
||
|
swnd->bPreventStyleChange = TRUE;
|
||
|
SetWindowLong(hwnd, GWL_STYLE, style | (swnd->sbarHorz.fScrollVisible ? WS_HSCROLL : 0) | (swnd->sbarVert.fScrollVisible ? WS_VSCROLL : 0));
|
||
|
LRESULT ret = (swnd->fWndUnicode) ? CallWindowProcW(swnd->oldproc, hwnd, message, wParam, lParam) :
|
||
|
CallWindowProcA(swnd->oldproc, hwnd, message, wParam, lParam);
|
||
|
SetWindowLongPtr(hwnd, GWL_STYLE, style);
|
||
|
swnd->bPreventStyleChange = FALSE;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
case WM_USER + 0x3443: //manually sent by other windows (like columns header for ex.)
|
||
|
if (swnd) swnd->update();
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return (swnd->fWndUnicode) ? CallWindowProcW(swnd->oldproc, hwnd, message, wParam, lParam) :
|
||
|
CallWindowProcA(swnd->oldproc, hwnd, message, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
SCROLLBAR *GetScrollBarFromHwnd(HWND hwnd, UINT nBar)
|
||
|
{
|
||
|
ScrollWnd *sw = GetScrollWndFromHwnd(hwnd);
|
||
|
|
||
|
if (!sw) return 0;
|
||
|
|
||
|
if (nBar == SB_HORZ)
|
||
|
return &sw->sbarHorz;
|
||
|
else if (nBar == SB_VERT)
|
||
|
return &sw->sbarVert;
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// return the default minimum size of a scrollbar thumb
|
||
|
//
|
||
|
int WINAPI CoolSB_GetDefaultMinThumbSize(void)
|
||
|
{
|
||
|
DWORD dwVersion = GetVersion();
|
||
|
|
||
|
// set the minimum thumb size for a scrollbar. This
|
||
|
// differs between NT4 and 2000, so need to check to see
|
||
|
// which platform we are running under
|
||
|
if (dwVersion < 0x80000000) // Windows NT/2000
|
||
|
{
|
||
|
if (LOBYTE(LOWORD(dwVersion)) >= 5)
|
||
|
return MINTHUMBSIZE_2000;
|
||
|
else
|
||
|
return MINTHUMBSIZE_NT4;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return MINTHUMBSIZE_NT4;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Set the minimum size, in pixels, that the thumb box will shrink to.
|
||
|
//
|
||
|
BOOL WINAPI CoolSB_SetMinThumbSize(HWND hwnd, UINT wBar, UINT size)
|
||
|
{
|
||
|
SCROLLBAR *sbar;
|
||
|
|
||
|
if (!GetScrollWndFromHwnd(hwnd))
|
||
|
return FALSE;
|
||
|
|
||
|
if (size == -1)
|
||
|
size = CoolSB_GetDefaultMinThumbSize();
|
||
|
|
||
|
if ((wBar == SB_HORZ || wBar == SB_BOTH) &&
|
||
|
(sbar = GetScrollBarFromHwnd(hwnd, SB_HORZ)))
|
||
|
{
|
||
|
sbar->nMinThumbSize = size;
|
||
|
}
|
||
|
|
||
|
if ((wBar == SB_VERT || wBar == SB_BOTH) &&
|
||
|
(sbar = GetScrollBarFromHwnd(hwnd, SB_VERT)))
|
||
|
{
|
||
|
sbar->nMinThumbSize = size;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static void RedrawNonClient(HWND hwnd, BOOL fFrameChanged)
|
||
|
{
|
||
|
if (fFrameChanged == FALSE)
|
||
|
{
|
||
|
SendMessage(hwnd, WM_NCPAINT, (WPARAM)1, 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE
|
||
|
| SWP_FRAMECHANGED | SWP_DRAWFRAME);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ScrollWnd::ScrollWnd(HWND hwnd, int flags)
|
||
|
{
|
||
|
SCROLLINFO *si;
|
||
|
RECT rect;
|
||
|
DWORD dwCurStyle;
|
||
|
|
||
|
bars = 0;
|
||
|
oldproc = NULL;
|
||
|
memset(&sbarHorz, 0, sizeof(sbarHorz));
|
||
|
memset(&sbarVert, 0, sizeof(sbarVert));
|
||
|
sbarHorz.flags = flags;
|
||
|
sbarVert.flags = flags;
|
||
|
fThumbTracking = 0;
|
||
|
fLeftScrollbar = 0;
|
||
|
cxLeftEdge = cxRightEdge = cyTopEdge = cyBottomEdge = 0;
|
||
|
bPreventStyleChange = 0;
|
||
|
m_disable_hscroll = 0;
|
||
|
m_xp_theme_disabled = 0;
|
||
|
|
||
|
m_hwnd = hwnd;
|
||
|
|
||
|
GetClientRect(hwnd, &rect);
|
||
|
|
||
|
si = &sbarHorz.scrollInfo;
|
||
|
si->cbSize = sizeof(SCROLLINFO);
|
||
|
si->fMask = SIF_ALL;
|
||
|
GetScrollInfo(hwnd, SB_HORZ, si);
|
||
|
|
||
|
si = &sbarVert.scrollInfo;
|
||
|
si->cbSize = sizeof(SCROLLINFO);
|
||
|
si->fMask = SIF_ALL;
|
||
|
GetScrollInfo(hwnd, SB_VERT, si);
|
||
|
|
||
|
//check to see if the window has left-aligned scrollbars
|
||
|
if (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LEFTSCROLLBAR)
|
||
|
fLeftScrollbar = TRUE;
|
||
|
else
|
||
|
fLeftScrollbar = FALSE;
|
||
|
|
||
|
dwCurStyle = GetWindowLong(hwnd, GWL_STYLE);
|
||
|
|
||
|
SetProp(hwnd, szPropStr, (HANDLE)this);
|
||
|
|
||
|
//scrollbars will automatically get enabled, even if
|
||
|
//they aren't to start with....sorry, but there isn't an
|
||
|
//easy alternative.
|
||
|
if (dwCurStyle & WS_HSCROLL)
|
||
|
sbarHorz.fScrollFlags = CSBS_VISIBLE;
|
||
|
|
||
|
if (dwCurStyle & WS_VSCROLL)
|
||
|
sbarVert.fScrollFlags = CSBS_VISIBLE;
|
||
|
|
||
|
//need to be able to distinguish between horizontal and vertical
|
||
|
//scrollbars in some instances
|
||
|
sbarHorz.nBarType = SB_HORZ;
|
||
|
sbarVert.nBarType = SB_VERT;
|
||
|
|
||
|
sbarHorz.fFlatScrollbar = CSBS_NORMAL;
|
||
|
sbarVert.fFlatScrollbar = CSBS_NORMAL;
|
||
|
|
||
|
//set the default arrow sizes for the scrollbars
|
||
|
sbarHorz.nArrowLength = SYSTEM_METRIC;
|
||
|
sbarHorz.nArrowWidth = SYSTEM_METRIC;
|
||
|
sbarVert.nArrowLength = SYSTEM_METRIC;
|
||
|
sbarVert.nArrowWidth = SYSTEM_METRIC;
|
||
|
|
||
|
bPreventStyleChange = FALSE;
|
||
|
|
||
|
fWndUnicode = IsWindowUnicode(hwnd);
|
||
|
oldproc = (WNDPROC)(LONG_PTR) ((fWndUnicode) ? SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)CoolSBWndProc) :
|
||
|
SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)CoolSBWndProc));
|
||
|
|
||
|
CoolSB_SetMinThumbSize(hwnd, SB_BOTH, CoolSB_GetDefaultMinThumbSize());
|
||
|
|
||
|
//send the window a frame changed message to update the scrollbars
|
||
|
RedrawNonClient(hwnd, TRUE);
|
||
|
|
||
|
//disable XP styles
|
||
|
if (SetWindowTheme && !m_xp_theme_disabled)
|
||
|
{
|
||
|
SetWindowTheme(m_hwnd, L" ", L" ");
|
||
|
m_xp_theme_disabled = 1;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
BOOL WINAPI CoolSB_ShowScrollBar(HWND hwnd, int wBar, BOOL fShow)
|
||
|
{
|
||
|
SCROLLBAR *sbar;
|
||
|
BOOL bFailed = FALSE;
|
||
|
DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
|
||
|
|
||
|
if ((wBar == SB_HORZ || wBar == SB_BOTH) &&
|
||
|
(sbar = GetScrollBarFromHwnd(hwnd, SB_HORZ)))
|
||
|
{
|
||
|
sbar->fScrollFlags = sbar->fScrollFlags & ~CSBS_VISIBLE;
|
||
|
sbar->fScrollFlags |= (fShow == TRUE ? CSBS_VISIBLE : 0);
|
||
|
//bFailed = TRUE;
|
||
|
|
||
|
if (fShow) SetWindowLong(hwnd, GWL_STYLE, dwStyle | WS_HSCROLL);
|
||
|
else SetWindowLong(hwnd, GWL_STYLE, dwStyle & ~WS_HSCROLL);
|
||
|
}
|
||
|
|
||
|
if ((wBar == SB_VERT || wBar == SB_BOTH) &&
|
||
|
(sbar = GetScrollBarFromHwnd(hwnd, SB_VERT)))
|
||
|
{
|
||
|
sbar->fScrollFlags = sbar->fScrollFlags & ~CSBS_VISIBLE;
|
||
|
sbar->fScrollFlags |= (fShow == TRUE ? CSBS_VISIBLE : 0);
|
||
|
//bFailed = TRUE;
|
||
|
|
||
|
if (fShow) SetWindowLong(hwnd, GWL_STYLE, dwStyle | WS_VSCROLL);
|
||
|
else SetWindowLong(hwnd, GWL_STYLE, dwStyle & ~WS_VSCROLL);
|
||
|
}
|
||
|
|
||
|
if (bFailed)
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetWindowPos(hwnd, 0, 0, 0, 0, 0,
|
||
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
|
||
|
SWP_NOACTIVATE | SWP_FRAMECHANGED);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ScrollWnd::~ScrollWnd()
|
||
|
{
|
||
|
if (oldproc)
|
||
|
{
|
||
|
((fWndUnicode) ? SetWindowLongPtrW(m_hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)oldproc) :
|
||
|
SetWindowLongPtrA(m_hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)oldproc));
|
||
|
}
|
||
|
RemoveProp(m_hwnd, szPropStr);
|
||
|
RedrawNonClient(m_hwnd, TRUE);
|
||
|
}
|
||
|
|
||
|
void ScrollWnd::update()
|
||
|
{
|
||
|
int dohorz = 0, dovert = 0;
|
||
|
|
||
|
SCROLLINFO tsi = {sizeof(SCROLLINFO), SIF_ALL, };
|
||
|
|
||
|
if (!m_disable_hscroll)
|
||
|
{
|
||
|
GetScrollInfo(m_hwnd, SB_HORZ, &tsi);
|
||
|
if (memcmp(&tsi, &sbarHorz.scrollInfo, sizeof(SCROLLINFO)))
|
||
|
{
|
||
|
memcpy(&sbarHorz.scrollInfo, &tsi, sizeof(SCROLLINFO));
|
||
|
dohorz = 1;
|
||
|
}
|
||
|
}
|
||
|
GetScrollInfo(m_hwnd, SB_VERT, &tsi);
|
||
|
if (memcmp(&tsi, &sbarVert.scrollInfo, sizeof(SCROLLINFO)))
|
||
|
{
|
||
|
memcpy(&sbarVert.scrollInfo, &tsi, sizeof(SCROLLINFO));
|
||
|
dovert = 1;
|
||
|
}
|
||
|
|
||
|
BOOL fRecalcFrame = FALSE;
|
||
|
if (dohorz) updatesb(SB_HORZ, &fRecalcFrame);
|
||
|
if (dovert) updatesb(SB_VERT, &fRecalcFrame);
|
||
|
|
||
|
if (dohorz || dovert) RedrawNonClient(m_hwnd, fRecalcFrame);
|
||
|
}
|
||
|
|
||
|
void ScrollWnd::updatesb(int fnBar, BOOL *fRecalcFrame)
|
||
|
{
|
||
|
SCROLLBAR *sbar = (fnBar == SB_HORZ ? &sbarHorz : &sbarVert);
|
||
|
SCROLLINFO *mysi = &sbar->scrollInfo;
|
||
|
|
||
|
if (mysi->nPage > (UINT)mysi->nMax
|
||
|
|| (mysi->nPage == (UINT)mysi->nMax && mysi->nMax == 0)
|
||
|
|| mysi->nMax <= mysi->nMin)
|
||
|
{
|
||
|
if (sbar->fScrollVisible)
|
||
|
{
|
||
|
CoolSB_ShowScrollBar(m_hwnd, fnBar, FALSE);
|
||
|
*fRecalcFrame = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!sbar->fScrollVisible && mysi->nPage > 0)
|
||
|
{
|
||
|
CoolSB_ShowScrollBar(m_hwnd, fnBar, TRUE);
|
||
|
*fRecalcFrame = TRUE;
|
||
|
}
|
||
|
else if (sbar->fScrollVisible && mysi->nPage == 0)
|
||
|
{
|
||
|
CoolSB_ShowScrollBar(m_hwnd, fnBar, FALSE);
|
||
|
*fRecalcFrame = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ScrollWnd::disableHorzScroll()
|
||
|
{
|
||
|
m_disable_hscroll = 1;
|
||
|
sbarHorz.fScrollFlags = 0;
|
||
|
}
|
||
|
|
||
|
#if 0 // unused
|
||
|
int ScrollWnd::setScrollInfo(int fnBar, LPSCROLLINFO lpsi, BOOL fRedraw)
|
||
|
{
|
||
|
SCROLLINFO *mysi;
|
||
|
SCROLLBAR *sbar;
|
||
|
BOOL fRecalcFrame = FALSE;
|
||
|
|
||
|
if (!lpsi)
|
||
|
return FALSE;
|
||
|
|
||
|
if (fnBar == SB_HORZ) mysi = &sbarHorz.scrollInfo;
|
||
|
else mysi = &sbarVert.scrollInfo;
|
||
|
|
||
|
if (lpsi->fMask & SIF_RANGE)
|
||
|
{
|
||
|
mysi->nMin = lpsi->nMin;
|
||
|
mysi->nMax = lpsi->nMax;
|
||
|
}
|
||
|
|
||
|
//The nPage member must specify a value from 0 to nMax - nMin +1.
|
||
|
if (lpsi->fMask & SIF_PAGE)
|
||
|
{
|
||
|
UINT t = (UINT)(mysi->nMax - mysi->nMin + 1);
|
||
|
mysi->nPage = min(max(0, lpsi->nPage), t);
|
||
|
}
|
||
|
|
||
|
//The nPos member must specify a value between nMin and nMax - max(nPage - 1, 0).
|
||
|
if (lpsi->fMask & SIF_POS)
|
||
|
{
|
||
|
mysi->nPos = max(lpsi->nPos, mysi->nMin);
|
||
|
mysi->nPos = min((UINT)mysi->nPos, mysi->nMax - max(mysi->nPage - 1, 0));
|
||
|
}
|
||
|
|
||
|
sbar = GetScrollBarFromHwnd(m_hwnd, fnBar);
|
||
|
|
||
|
if ((lpsi->fMask & SIF_DISABLENOSCROLL) || (sbar->fScrollFlags & CSBS_THUMBALWAYS))
|
||
|
{
|
||
|
if (!sbar->fScrollVisible)
|
||
|
{
|
||
|
CoolSB_ShowScrollBar(m_hwnd, fnBar, TRUE);
|
||
|
fRecalcFrame = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (mysi->nPage > (UINT)mysi->nMax
|
||
|
|| mysi->nPage == (UINT)mysi->nMax && mysi->nMax == 0
|
||
|
|| mysi->nMax <= mysi->nMin)
|
||
|
{
|
||
|
if (sbar->fScrollVisible)
|
||
|
{
|
||
|
CoolSB_ShowScrollBar(m_hwnd, fnBar, FALSE);
|
||
|
fRecalcFrame = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!sbar->fScrollVisible)
|
||
|
{
|
||
|
CoolSB_ShowScrollBar(m_hwnd, fnBar, TRUE);
|
||
|
fRecalcFrame = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (fRedraw && !fThumbTracking)
|
||
|
RedrawNonClient(m_hwnd, fRecalcFrame);
|
||
|
|
||
|
return mysi->nPos;
|
||
|
}
|
||
|
#endif
|