winamp/Src/Plugins/General/gen_ml/scrollwnd.cpp
2024-09-24 14:54:57 +02:00

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