#include #include #include "scrollbar.h" #include #include #include #define TIMER_ID 9871 #define TIMER_ID2 9872 #define FIRST_DELAY 350 #define NEXT_DELAY 75 ScrollBar::ScrollBar() { leftrgn = NULL; rightrgn = NULL; buttonrgn = NULL; position = 0; moving = 0; lefting = 0; righting = 0; clicked = 0; height = DEFAULT_HEIGHT; buttonx = 0; shiftleft = 0; shiftright = 0; curmouseposition = POS_NONE; clickmouseposition = POS_NONE; pageing = 0; timer = timer2 = 0; npages = 100; pageway = PAGE_NONE; updown = 256; insetpos = 0; clickbuttonx = 0; vertical = 0; firstdelay = 0; lastx = lasty = 0; } ScrollBar::~ScrollBar() { deleteResources(); } void ScrollBar::deleteResources() { delete leftrgn; leftrgn = NULL; delete buttonrgn; buttonrgn = NULL; delete rightrgn; rightrgn = NULL; } // this one is inherited void ScrollBar::freeResources() { SCROLLBAR_PARENT::freeResources(); deleteResources(); } void ScrollBar::reloadResources() { SCROLLBAR_PARENT::reloadResources(); loadBmps(); } int ScrollBar::onMouseMove (int x, int y) { SCROLLBAR_PARENT::onMouseMove(x, y); lastx = x; lasty = y; if (clicked && clickmouseposition == POS_BUTTON) { POINT pt={x,y}; int x; if (!vertical) x = pt.x - clickpos.x; else x = pt.y - clickpos.y; RECT r; getClientRect(&r); int maxwidth; if (!vertical) maxwidth = (r.right-r.left)-(shiftright+shiftleft+bmpbutton.getWidth())+1; else maxwidth = (r.bottom-r.top)-(shiftright+shiftleft+bmpbutton.getHeight())+1; buttonx = MIN(MAX(clickbuttonx + x, 0), maxwidth); calcPosition(); invalidate(); } else { int oldposition = curmouseposition; curmouseposition = getMousePosition(); if (oldposition != curmouseposition) invalidate(); if (curmouseposition != POS_NONE && !getCapture()) beginCapture(); if (curmouseposition == POS_NONE && getCapture() && !clicked && !pageing) endCapture(); } return 1; } int ScrollBar::getWidth() { if (!bmpbutton) return 0; if (!vertical) return bmpbutton.getHeight(); else return bmpbutton.getWidth(); return 0; } int ScrollBar::getMousePosition() { int v = POS_NONE; POINT pt={lastx, lasty}; RECT c; getClientRect(&c); pt.x -= c.left; pt.y -= c.top; api_region *l, *b, *r; l = leftrgn->clone(); b = buttonrgn->clone(); if (!vertical) b->offset(buttonx+shiftleft, 0); else b->offset(0, buttonx+shiftleft); r = rightrgn->clone(); if (!vertical) r->offset(c.right-c.left-bmpleft.getWidth(), 0); else r->offset(0, c.bottom-c.top-bmpleft.getHeight()); if (b->ptInRegion(&pt)) v = POS_BUTTON; if (l->ptInRegion(&pt)) v = POS_LEFT; if (r->ptInRegion(&pt)) v = POS_RIGHT; leftrgn->disposeClone(l); buttonrgn->disposeClone(b); rightrgn->disposeClone(r); return v; } int ScrollBar::onLeftButtonDown(int x, int y) { clickmouseposition = getMousePosition(); if (!pageing && clickmouseposition != POS_NONE) { clicked = 1; if (clickmouseposition == POS_LEFT || clickmouseposition == POS_RIGHT) handleUpDown(); if (clickmouseposition) { clickpos.x = lastx; clickpos.y = lasty; clickbuttonx = buttonx; } } else { clicked = 0; pageing = 1; handlePageUpDown(); } invalidate(); return 1; } void ScrollBar::handleUpDown() { setTimer(TIMER_ID2, FIRST_DELAY); timer2 = 1; firstdelay = 1; checkUpDown(); } int ScrollBar::checkUpDown() { if (!clicked) { if (timer2) { killTimer(TIMER_ID2); timer2 = 0; return 1; } } if (getMousePosition() == clickmouseposition) upDown(clickmouseposition); return 1; } void ScrollBar::handlePageUpDown() { setTimer(TIMER_ID, FIRST_DELAY); timer = 1; firstdelay = 1; checkPageUpDown(); } int ScrollBar::checkPageUpDown() { if (!pageing) { if (timer) { killTimer(TIMER_ID); timer = 0; pageway = PAGE_NONE; return 1; } } POINT pt={lastx,lasty}; RECT c; getClientRect(&c); pt.x -= c.left; pt.y -= c.top; if (!vertical) { int middlebutton = shiftleft + buttonx + bmpbutton.getWidth()/2; api_region *r = buttonrgn->clone(); r->offset(buttonx+shiftleft, 0); if (pt.x > middlebutton && !r->ptInRegion(&pt) && pageway != PAGE_DOWN) pageUp(); if (pt.x < middlebutton && !r->ptInRegion(&pt) && pageway != PAGE_UP) pageDown(); buttonrgn->disposeClone(r); } else { int middlebutton = shiftleft + buttonx + bmpbutton.getHeight()/2; api_region *r = buttonrgn->clone(); r->offset(0, buttonx+shiftleft); if (pt.y > middlebutton && !r->ptInRegion(&pt) && pageway != PAGE_DOWN) pageUp(); if (pt.y < middlebutton && !r->ptInRegion(&pt) && pageway != PAGE_UP) pageDown(); buttonrgn->disposeClone(r); } return 1; } int ScrollBar::onLeftButtonUp(int x, int y) { clicked = 0; clickmouseposition = POS_NONE; curmouseposition = POS_NONE; onMouseMove(x,y); if (pageing) { pageing = 0; checkPageUpDown(); } onSetFinalPosition(); invalidate(); return 1; } int ScrollBar::onRightButtonDown(int x, int y) { return 1; } int ScrollBar::onRightButtonUp(int x, int y) { return 1; } int ScrollBar::onMouseWheelUp(int clicked, int lines) { return 1; } int ScrollBar::onMouseWheelDown(int clicked, int lines) { return 1; } int ScrollBar::onPaint(Canvas *canvas) { AutoSkinBitmap &thisleft = curmouseposition == POS_LEFT ? (clicked ? bmplpressed : bmplhilite) : bmpleft; AutoSkinBitmap &thisbutton = curmouseposition == POS_BUTTON ? (clicked ? bmpbpressed : bmpbhilite) : bmpbutton; AutoSkinBitmap &thisright = curmouseposition == POS_RIGHT ? (clicked ? bmprpressed : bmprhilite) : bmpright; if (curmouseposition != clickmouseposition && clicked) { thisleft = bmpleft; thisbutton = bmpbutton; thisright = bmpright; } RECT r; PaintBltCanvas paintcanvas; if (canvas == NULL) { if (!paintcanvas.beginPaint(this)) return 0; canvas = &paintcanvas; } SCROLLBAR_PARENT::onPaint(canvas); getClientRect(&r); renderBaseTexture(canvas, r); if (!vertical) { RECT c; c.left = r.left; c.right = r.left; c.top = r.top; c.bottom = r.bottom; if (bmpbackgroundleft.getBitmap()) { c.right = c.left + bmpbackgroundleft.getWidth(); bmpbackgroundleft.getBitmap()->stretchToRectAlpha(canvas, &c); } int l = c.right; c.left = r.right; c.right = r.right; if (bmpbackgroundright.getBitmap()) { c.left = r.right - bmpbackgroundright.getWidth(); bmpbackgroundright.getBitmap()->stretchToRectAlpha(canvas, &c); } c.right = c.left; c.left = l; if (bmpbackgroundmiddle.getBitmap()) { bmpbackgroundmiddle.getBitmap()->stretchToRectAlpha(canvas, &c); } c.left = r.left + buttonx+shiftleft; c.top = r.top + 0; c.right = r.left + buttonx+thisbutton.getWidth()+shiftleft; c.bottom = r.top + getWidth(); thisbutton.stretchToRectAlpha(canvas, &c); c.left = r.left; c.top = r.top; c.right = r.left + thisleft.getWidth(); c.bottom = r.top + getWidth(); thisleft.stretchToRectAlpha(canvas, &c); c.left = r.right-thisright.getWidth(); c.top = r.top; c.right = r.right; c.bottom = r.top+getWidth(); thisright.stretchToRectAlpha(canvas, &c); } else { RECT c; c.top = r.top; c.bottom = r.top; c.left = r.left; c.right = r.right; if (bmpbackgroundleft.getBitmap()) { c.bottom = c.top + bmpbackgroundleft.getHeight(); bmpbackgroundleft.getBitmap()->stretchToRectAlpha(canvas, &c); } int l = c.bottom; c.top = r.bottom; c.bottom = r.bottom; if (bmpbackgroundright.getBitmap()) { c.top = r.bottom - bmpbackgroundright.getHeight(); bmpbackgroundright.getBitmap()->stretchToRectAlpha(canvas, &c); } c.bottom = c.top; c.top = l; if (bmpbackgroundmiddle.getBitmap()) { bmpbackgroundmiddle.getBitmap()->stretchToRectAlpha(canvas, &c); } c.left = r.right - thisleft.getWidth(); c.top = r.top+buttonx + shiftleft; c.right = r.right; c.bottom = r.top+buttonx+thisbutton.getHeight() + shiftleft; thisbutton.stretchToRectAlpha(canvas, &c); c.left = r.right - thisleft.getWidth(); c.top = r.top; c.right = r.right; c.bottom = r.top+thisleft.getHeight(); thisleft.stretchToRectAlpha(canvas, &c); c.left = r.right-thisright.getWidth(); c.top = r.bottom-thisright.getHeight(); c.right = r.right; c.bottom = r.bottom; thisright.stretchToRectAlpha(canvas, &c); } return 1; } int ScrollBar::getHeight() { return height; } void ScrollBar::setHeight(int newheight) { height = newheight; } int ScrollBar::onResize() { calcXPosition(); invalidate(); return 1; } int ScrollBar::onInit() { SCROLLBAR_PARENT::onInit(); return 1; } void ScrollBar::setBitmaps(wchar_t *left, wchar_t *lpressed, wchar_t *lhilite, wchar_t *right, wchar_t *rpressed, wchar_t *rhilite, wchar_t *button, wchar_t *bpressed, wchar_t *bhilite) { deleteResources(); bmpleft = left; bmplpressed = lpressed; bmplhilite = lhilite; bmpright = right; bmprpressed = rpressed; bmprhilite = rhilite; bmpbutton = button; bmpbpressed = bpressed; bmpbhilite = bhilite; loadBmps(); } void ScrollBar::setBackgroundBitmaps(const wchar_t *left, const wchar_t *middle, const wchar_t *right) { bmpbackgroundleft = left; bmpbackgroundmiddle = middle; bmpbackgroundright = right; } void ScrollBar::loadBmps() { if (bmpleft.getBitmap()) leftrgn = new RegionI(bmpleft); if (bmpbutton.getBitmap()) buttonrgn = new RegionI(bmpbutton); if (bmpright.getBitmap()) rightrgn = new RegionI(bmpright); calcOverlapping(); calcXPosition(); } void ScrollBar::setPosition(int pos) { setPrivatePosition(pos, FALSE); } void ScrollBar::setPrivatePosition(int pos, bool signal, bool smooth) { if (insetpos) return; // helps stupid people (like me) insetpos = 1; position = MIN(SCROLLBAR_FULL, pos); position = MAX(0, position); calcXPosition(); if (signal) onSetPosition(smooth); if (isInited() && isVisible()) invalidate(); insetpos = 0; } int ScrollBar::getPosition() { return position; } int ScrollBar::onSetPosition(bool smooth) { notifyParent(ChildNotify::SCROLLBAR_SETPOSITION, smooth); return 1; } int ScrollBar::onSetFinalPosition() { notifyParent(ChildNotify::SCROLLBAR_SETFINALPOSITION); return 1; } void ScrollBar::calcOverlapping() { if (!vertical) { shiftleft = bmpleft.getWidth(); if (leftrgn && buttonrgn) { int i; for (i=shiftleft;i>=0;i--) { api_region *reg = buttonrgn->clone(); reg->offset(i, 0); if (leftrgn->doesIntersectRgn(reg)) { i++; buttonrgn->disposeClone(reg); break; } buttonrgn->disposeClone(reg); } if (i >= 0) shiftleft = i; } shiftright = bmpright.getWidth(); if (rightrgn && buttonrgn) { int i; for (i=0;i>=-shiftright;i--) { api_region *reg = rightrgn->clone(); reg->offset(i+bmpbutton.getWidth(), 0); if (reg->doesIntersectRgn(buttonrgn)) { i++; rightrgn->disposeClone(reg); break; } rightrgn->disposeClone(reg); } if (i >= -shiftright) shiftright += i; } } else { shiftleft = bmpleft.getHeight(); if (leftrgn && buttonrgn) { int i; for (i=shiftleft;i>=0;i--) { api_region *reg = buttonrgn->clone(); reg->offset(0, i); if (leftrgn->doesIntersectRgn(reg)) { i++; buttonrgn->disposeClone(reg); break; } buttonrgn->disposeClone(reg); } if (i >= 0) shiftleft = i; } shiftright = bmpright.getHeight(); if (rightrgn && buttonrgn) { int i; for (i=0;i>=-shiftright;i--) { api_region *reg = rightrgn->clone(); reg->offset(0, i+bmpbutton.getHeight()); if (reg->doesIntersectRgn(buttonrgn)) { i++; rightrgn->disposeClone(reg); break; } rightrgn->disposeClone(reg); } if (i >= -shiftright) shiftright += i; } } } void ScrollBar::calcXPosition() { if (!isInited()) return; RECT r; getClientRect(&r); int maxwidth; if (!vertical) maxwidth = (r.right-r.left)-(bmpbutton.getWidth()+shiftleft+shiftright)+1; else maxwidth = (r.bottom-r.top)-(bmpbutton.getHeight()+shiftleft+shiftright)+1; int oldx = buttonx; buttonx = (int)(((float)getPosition() / SCROLLBAR_FULL) * maxwidth); if (buttonx != oldx) invalidate(); } void ScrollBar::calcPosition() { if (!isInited()) return; RECT r; getClientRect(&r); int maxwidth; if (!vertical) maxwidth = r.right-r.left-(bmpbutton.getWidth()+shiftleft+shiftright)+1; else maxwidth = r.bottom-r.top-(bmpbutton.getHeight()+shiftleft+shiftright)+1; setPrivatePosition((int)((float)buttonx / maxwidth * SCROLLBAR_FULL)); //invalidate(); } void ScrollBar::timerCallback(int id) { switch (id) { case TIMER_ID: if (firstdelay) { killTimer(TIMER_ID); setTimer(TIMER_ID, NEXT_DELAY); timer = 1; firstdelay = 0; } checkPageUpDown(); break; case TIMER_ID2: if (firstdelay) { killTimer(TIMER_ID2); setTimer(TIMER_ID2, NEXT_DELAY); timer2 = 1; firstdelay = 0; } checkUpDown(); break; default: SCROLLBAR_PARENT::timerCallback(id); } } // FG> smooth scrolling forced on, sorry, microsoft does it too so the user perceives IE scrolling as faster than it actually is // eventho they tell you "The smooth-scrolling effect for list boxes should be disabled when this setting is FALSE. Your application must do this if it creates customized list boxes", they // break their own rule so people don't bitch too much. ergo there is no reason we should not do that too. int ScrollBar::pageUp() { pageway = PAGE_UP; setPrivatePosition((int)MAX(0.f, (float)getPosition() + (float)SCROLLBAR_FULL / (npages-1)), TRUE, 1/*Std::osparam_getSmoothScroll()*/); return 1; }; int ScrollBar::pageDown() { pageway = PAGE_DOWN; setPrivatePosition((int)MIN((float)SCROLLBAR_FULL, (float)getPosition() - (float)SCROLLBAR_FULL / (npages-1)), TRUE, 1/*Std::osparam_getSmoothScroll()*/); return 1; }; void ScrollBar::setNPages(int n) { //ASSERT(n >= 2); if (n < 2) n = 2; npages = n; } void ScrollBar::gotoPage(int page) { page = MIN(page, npages-1); page = MAX(page, 0); setPrivatePosition((int)((float)SCROLLBAR_FULL / (npages-1) * page), TRUE, FALSE); } void ScrollBar::setUpDownValue(int newupdown) { updown = newupdown; } int ScrollBar::upDown(int which) { switch (which) { case POS_LEFT: setPrivatePosition(getPosition()-updown); break; case POS_RIGHT: setPrivatePosition(getPosition()+updown); break; } return 1; } void ScrollBar::setVertical(bool isvertical) { vertical = isvertical; calcOverlapping(); if (isInited()) invalidate(); }