// some code taken from (freeware) Cool ScrollBar library by J Brown #include "main.h" #include #include #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