// // Copyright (C) 1996 Ben Ross // // You may distribute under the terms of the GNU General Public // License as specified in the COPYING file. // // $Id: XeScrollBar.C,v 1.2 1999/10/18 14:51:13 ben Exp $ #include #include #include #include #include #include #include #include #include #include #include // $$$ Add horizontal scrollbar support... #define MIN_SLIDER_SIZE 10 #define SLIDER_INVALID (_visRange >= (_max - _min) && _position == 0) XeScrollBar::XeScrollBar(XeObject* parent, const char* name, const char* className) : XeWidget(parent, name, className) { _borderWidth = 2; _shadowWidth = 2; _max = 1; _min = 0; _visRange = 1; _position = 0; _sliderPosition = 0; _sliderSize = _height - _borderWidth * 2; _sliderGrab = -1; _fdiff = 0; XSetWindowAttributes attr; XGCValues values; ulong valmask; valmask = CWBitGravity; attr.bit_gravity = NorthWestGravity; XChangeWindowAttributes(gDisplay, _window, valmask, &attr); valmask = GCForeground; values.foreground = lookupColour(this, "grey40"); _shadowGC = getGC(this, &values, valmask); values.foreground = lookupColour(this, "grey90"); _rshadowGC = getGC(this, &values, valmask); values.foreground = lookupColour(this, "grey50"); _hiliteGC = getGC(this, &values, valmask); values.foreground = lookupColour(this, "grey80"); _faceGC = getGC(this, &values, valmask); XSelectInput(gDisplay, _window, (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | ExposureMask)); } XeScrollBar::~XeScrollBar() { } void XeScrollBar::adjust(bool doUpdate) { if(!_willUpdate) { _oldSize = _sliderSize; _oldPos = _sliderPosition; } int ymax = _height - _borderWidth * 2; int total = _max - _min; if(_visRange < total) { _sliderSize = int((double(_visRange)/double(total)) * ymax + 0.5); if(_sliderSize < MIN_SLIDER_SIZE) _sliderSize = MIN_SLIDER_SIZE; } else { _sliderSize = ymax; } int denom = ymax - _sliderSize; if(_sliderSize && denom != 0) // watch out for divide by zero! _fdiff = double(total - _visRange) / double(denom); else _fdiff = double(_visRange) / double(ymax); assert(_fdiff); _sliderPosition = int(double(_position) / _fdiff + 0.5); if (_sliderPosition + MIN_SLIDER_SIZE > ymax) _sliderPosition = ymax - MIN_SLIDER_SIZE; if(doUpdate) update(); } void XeScrollBar::resizeEvent(uint width, uint height) { if(height > _height) { XClearArea(gDisplay, _window, _borderWidth, _height - _borderWidth, _width - _borderWidth * 2, _borderWidth, False); } else { XRectangle r = { 0, 0, width, height}; drawBorder(r, _borderWidth, _shadowGC, _rshadowGC); } _width = width; _height = height; adjust(); } void XeScrollBar::setMax(long max) { _max = max; adjust(); } void XeScrollBar::setVisibleRange(long range) { _visRange = range; adjust(); } void XeScrollBar::setPosition(long position, bool updateNow) { if(!_willUpdate) { _oldSize = _sliderSize; _oldPos = _sliderPosition; } _position = (position < 0) ? 0 : position; _position = (position > _max) ? _max : position; int ymax = _height - _borderWidth * 2; assert(_fdiff); _sliderPosition = int(double(_position) / _fdiff + 0.5); if (_sliderPosition + MIN_SLIDER_SIZE > ymax) _sliderPosition = ymax - MIN_SLIDER_SIZE; if(updateNow) moveSlider(_oldPos, _sliderPosition); else update(); } int XeScrollBar::doEvent(XEvent *theEvent) { switch(theEvent->type) { case Expose: case GraphicsExpose: doExpose((XExposeEvent *)theEvent); break; case ButtonPress: case ButtonRelease: doButton((XButtonEvent *)theEvent); break; case MotionNotify: doMotion((XMotionEvent *)theEvent); break; default: XeObject::doEvent(theEvent); } return 0; } void XeScrollBar::doUpdate(void) { if (SLIDER_INVALID) { clearSlider(_oldPos, _oldSize); return; } drawSlider(); clipBounds(_oldPos, _oldSize); } void XeScrollBar::doButton(XButtonEvent *event) { int x = event->x - _borderWidth; int y = event->y - _borderWidth; if(event->button == Button1) { if(y < _sliderPosition || y > _sliderPosition + _sliderSize || x < 0 || x > int(_width-_borderWidth*2)) { _sliderGrab = -1; return; } _sliderGrab = y - _sliderPosition; } else if(event->button == Button2) { int ymax = _height - _borderWidth*2; int sp = _sliderPosition; _sliderGrab = _sliderSize / 2; _sliderPosition = y - _sliderGrab; if(_sliderPosition < 0) _sliderPosition = 0; if(_sliderPosition + _sliderSize > ymax) _sliderPosition = ymax - _sliderSize; _position = long(_fdiff * (_sliderPosition) + 0.5); if (SLIDER_INVALID) clearSlider(0, ymax); else moveSlider(sp, _sliderPosition); callCallbacks(XE_CB_VALUE_CHANGED, (void*)&_position); } else { _sliderGrab = -1; } } void XeScrollBar::doMotion(XMotionEvent *theEvent) { if(_sliderGrab == -1) return; if (SLIDER_INVALID) return; // // Compress motion events. // XEvent current, ahead; XMotionEvent *event = theEvent; while(XEventsQueued(gDisplay, QueuedAfterReading) > 0) { XPeekEvent(gDisplay, &ahead); if(ahead.type != MotionNotify) break; if(ahead.xmotion.window != _window) break; XNextEvent(gDisplay, ¤t); event = (XMotionEvent*)¤t; } //int x = event->x - _borderWidth; int y = event->y - _borderWidth; int ymax = _height - _borderWidth*2; int sp = _sliderPosition; _sliderPosition = y - _sliderGrab; if(_sliderPosition < 0) _sliderPosition = 0; if (sp < _sliderPosition) { // The slider is being moved downwards if (sp + _sliderSize > ymax) { // slider is already below bottom of scrollbar _sliderPosition = sp; return; } else if (_sliderPosition + _sliderSize > ymax) { _sliderPosition = ymax - _sliderSize; } } _position = long(_fdiff * (_sliderPosition) + 0.5); if (SLIDER_INVALID) clearSlider(0, ymax); else moveSlider(sp, _sliderPosition); callCallbacks(XE_CB_VALUE_CHANGED, (void*)&_position); } void XeScrollBar::doExpose(XExposeEvent *) { XRectangle r = { 0, 0, _width, _height}; drawBorder(r, _borderWidth, _shadowGC, _rshadowGC); drawSlider(); } void XeScrollBar::clearSlider(int oldpos, int oldsize) { if(oldpos == 0 && oldsize == 0) return; XRectangle r; r.x = _borderWidth; r.y = _borderWidth + oldpos; r.width = _width - 2*_borderWidth; if (uint(r.y + oldsize) >= _height - _borderWidth) r.height = _height - _borderWidth - r.y; else r.height = oldsize; XClearArea(gDisplay, _window, r.x, r.y, r.width, r.height, False); } void XeScrollBar::drawSlider(void) { if (SLIDER_INVALID) return; XRectangle r1, r2; r2.x = _borderWidth; r2.y = _borderWidth + _sliderPosition; r2.width = _width - _borderWidth * 2; // Clip height of slider: // int ymax = _height - _borderWidth * 2; if (_sliderPosition + _sliderSize > ymax) r2.height = ymax - _sliderPosition; else r2.height = _sliderSize; r1.x = _borderWidth + _shadowWidth; r1.y = _sliderPosition + _borderWidth + _shadowWidth; r1.width = _width - (_borderWidth*2 + _shadowWidth*2); r1.height = r2.height - _shadowWidth * 2; XFillRectangle(gDisplay, _window, _faceGC, r1.x, r1.y, r1.width, r1.height); drawBorder(r2, _shadowWidth, _rshadowGC, _shadowGC); } void XeScrollBar::moveSlider(int from, int to) { int diff; XRectangle copyRect; diff = to - from; copyRect.x = _borderWidth; copyRect.y = _borderWidth + from; copyRect.width = _width - (_borderWidth * 2); copyRect.height = _sliderSize; if(diff) { //XCopyArea(gDisplay, _window, _window, _widgetGC, copyRect.x, // copyRect.y, copyRect.width, copyRect.height, copyRect.x, // copyRect.y + diff); drawSlider(); if(diff > 0) { XClearArea(gDisplay, _window, copyRect.x, copyRect.y, copyRect.width, diff, False); } else { uint height = -diff; uint y = copyRect.y + _sliderSize - height; if (y >= _height - _borderWidth) // don't clear bottom shadow of scrollbar return; if (y + height > _height - _borderWidth) // clip height if necessary height = _height - _borderWidth - y; XClearArea(gDisplay, _window, copyRect.x, y, copyRect.width, height, False); } } } // // This function assumes _sliderSize and _sliderPosition have been set // and are correct. // void XeScrollBar::clipBounds(int oldpos, int oldsize) { XRectangle r; int oldposend = oldpos + oldsize; int newposend = _sliderPosition + _sliderSize; if(oldpos < _sliderPosition) { r.y = oldpos + _borderWidth; r.x = _borderWidth; r.width = _width - 2*_borderWidth; r.height = _sliderPosition - oldpos; XClearArea(gDisplay, _window, r.x, r.y, r.width, r.height, False); } if(oldposend > newposend) { r.x = _borderWidth; r.width = _width - 2*_borderWidth; r.y = newposend + _borderWidth; r.height = oldposend - newposend; uint y2 = r.y + r.height; if(r.y >= int(_height - _borderWidth)) return; if(y2 >= _height - _borderWidth) r.height -= y2 - (_height - _borderWidth); XClearArea(gDisplay, _window, r.x, r.y, r.width, r.height, False); } } int XeScrollBar::commandKey(KeySym, uint) { return 0; } void XeScrollBar::setResources(void) { } // $$$ unused - probably obselete: void XeScrollBar::resizeSlider(int oldsize, int newsize) { if(!_svisible) return; XRectangle r; if(newsize == oldsize) return; if(newsize > oldsize) { drawSlider(); } else { r.x = _borderWidth; r.y = _sliderPosition + _borderWidth + newsize; r.width = _width - 2*_borderWidth; r.height = oldsize - newsize; XClearArea(gDisplay, _window, r.x, r.y, r.width, r.height, False); drawSlider(); } }