/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ // vim: expandtab #include #include #include #include #include "qwt_painter.h" #include "qwt_paint_buffer.h" #include "qwt_scale_draw.h" #include "qwt_scale_map.h" #include "qwt_slider.h" class QwtSlider::PrivateData { public: QRect sliderRect; int thumbLength; int thumbWidth; int borderWidth; int scaleDist; int xMargin; int yMargin; QwtSlider::ScalePos scalePos; QwtSlider::BGSTYLE bgStyle; /* Scale and values might have different maps. This is confusing and I can't see strong arguments for such a feature. TODO ... */ QwtScaleMap map; // linear map mutable QSize sizeHintCache; }; /*! \brief Constructor \param parent parent widget \param orientation Orientation of the slider. Can be Qt::Horizontal or Qt::Vertical. Defaults to Qt::Horizontal. \param scalePos Position of the scale. Defaults to QwtSlider::NoScale. \param bgStyle Background style. QwtSlider::BgTrough draws the slider button in a trough, QwtSlider::BgSlot draws a slot underneath the button. An or-combination of both may also be used. The default is QwtSlider::BgTrough. QwtSlider enforces valid combinations of its orientation and scale position. If the combination is invalid, the scale position will be set to NoScale. Valid combinations are: - Qt::Horizonal with NoScale, TopScale, or BottomScale; - Qt::Vertical with NoScale, LeftScale, or RightScale. */ QwtSlider::QwtSlider(QWidget *parent, Qt::Orientation orientation, ScalePos scalePos, BGSTYLE bgStyle): QwtAbstractSlider(orientation, parent) { initSlider(orientation, scalePos, bgStyle); } #if QT_VERSION < 0x040000 /*! \brief Constructor Build a horizontal slider with no scale and BgTrough as background style \param parent parent widget \param name Object name */ QwtSlider::QwtSlider(QWidget *parent, const char* name): QwtAbstractSlider(Qt::Horizontal, parent) { setName(name); initSlider(Qt::Horizontal, NoScale, BgTrough); } #endif void QwtSlider::initSlider(Qt::Orientation orientation, ScalePos scalePos, BGSTYLE bgStyle) { if (orientation == Qt::Vertical) setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); else setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); #if QT_VERSION >= 0x040000 setAttribute(Qt::WA_WState_OwnSizePolicy, false); #else clearWState( WState_OwnSizePolicy ); #endif #if QT_VERSION < 0x040000 setWFlags(Qt::WNoAutoErase); #endif d_data = new QwtSlider::PrivateData; d_data->borderWidth = 2; d_data->scaleDist = 4; d_data->scalePos = scalePos; d_data->xMargin = 0; d_data->yMargin = 0; d_data->bgStyle = bgStyle; if (bgStyle == BgSlot) { d_data->thumbLength = 16; d_data->thumbWidth = 30; } else { d_data->thumbLength = 31; d_data->thumbWidth = 16; } d_data->sliderRect.setRect(0,0,8,8); QwtScaleDraw::Alignment align; if ( orientation == Qt::Vertical ) { // enforce a valid combination of scale position and orientation if ((d_data->scalePos == BottomScale) || (d_data->scalePos == TopScale)) d_data->scalePos = NoScale; // adopt the policy of layoutSlider (NoScale lays out like Left) if (d_data->scalePos == RightScale) align = QwtScaleDraw::RightScale; else align = QwtScaleDraw::LeftScale; } else { // enforce a valid combination of scale position and orientation if ((d_data->scalePos == LeftScale) || (d_data->scalePos == RightScale)) d_data->scalePos = NoScale; // adopt the policy of layoutSlider (NoScale lays out like Bottom) if (d_data->scalePos == TopScale) align = QwtScaleDraw::TopScale; else align = QwtScaleDraw::BottomScale; } scaleDraw()->setAlignment(align); scaleDraw()->setLength(100); setRange(0.0, 100.0, 1.0); setValue(0.0); } QwtSlider::~QwtSlider() { delete d_data; } /*! \brief Set the orientation. \param o Orientation. Allowed values are Qt::Horizontal and Qt::Vertical. If the new orientation and the old scale position are an invalid combination, the scale position will be set to QwtSlider::NoScale. \sa QwtAbstractSlider::orientation() */ void QwtSlider::setOrientation(Qt::Orientation o) { if ( o == orientation() ) return; if (o == Qt::Horizontal) { if ((d_data->scalePos == LeftScale) || (d_data->scalePos == RightScale)) d_data->scalePos = NoScale; } else { // if (o == Qt::Vertical) if ((d_data->scalePos == BottomScale) || (d_data->scalePos == TopScale)) d_data->scalePos = NoScale; } #if QT_VERSION >= 0x040000 if ( !testAttribute(Qt::WA_WState_OwnSizePolicy) ) #else if ( !testWState( WState_OwnSizePolicy ) ) #endif { QSizePolicy sp = sizePolicy(); sp.transpose(); setSizePolicy(sp); #if QT_VERSION >= 0x040000 setAttribute(Qt::WA_WState_OwnSizePolicy, false); #else clearWState( WState_OwnSizePolicy ); #endif } QwtAbstractSlider::setOrientation(o); layoutSlider(); } /*! \brief Change the scale position (and slider orientation). \param s Position of the scale. A valid combination of scale position and orientation is enforced: - if the new scale position is Left or Right, the scale orientation will become Qt::Vertical; - if the new scale position is Bottom or Top the scale orientation will become Qt::Horizontal; - if the new scale position is QwtSlider::NoScale, the scale orientation will not change. */ void QwtSlider::setScalePosition(ScalePos s) { if ( d_data->scalePos == s ) return; d_data->scalePos = s; switch(d_data->scalePos) { case BottomScale: { setOrientation(Qt::Horizontal); scaleDraw()->setAlignment(QwtScaleDraw::BottomScale); break; } case TopScale: { setOrientation(Qt::Horizontal); scaleDraw()->setAlignment(QwtScaleDraw::TopScale); break; } case LeftScale: { setOrientation(Qt::Vertical); scaleDraw()->setAlignment(QwtScaleDraw::LeftScale); break; } case RightScale: { setOrientation(Qt::Vertical); scaleDraw()->setAlignment(QwtScaleDraw::RightScale); break; } default: { // nothing } } layoutSlider(); } //! Return the scale position. QwtSlider::ScalePos QwtSlider::scalePosition() const { return d_data->scalePos; } /*! \brief Change the slider's border width \param bd border width */ void QwtSlider::setBorderWidth(int bd) { if ( bd < 0 ) bd = 0; if ( bd != d_data->borderWidth ) { d_data->borderWidth = bd; layoutSlider(); } } /*! \brief Set the slider's thumb length \param thumbLength new length */ void QwtSlider::setThumbLength(int thumbLength) { if ( thumbLength < 8 ) thumbLength = 8; if ( thumbLength != d_data->thumbLength ) { d_data->thumbLength = thumbLength; layoutSlider(); } } /*! \brief Change the width of the thumb \param w new width */ void QwtSlider::setThumbWidth(int w) { if ( w < 4 ) w = 4; if ( d_data->thumbWidth != w ) { d_data->thumbWidth = w; layoutSlider(); } } /*! \brief Set a scale draw For changing the labels of the scales, it is necessary to derive from QwtScaleDraw and overload QwtScaleDraw::label(). \param scaleDraw ScaleDraw object, that has to be created with new and will be deleted in ~QwtSlider or the next call of setScaleDraw(). */ void QwtSlider::setScaleDraw(QwtScaleDraw *scaleDraw) { const QwtScaleDraw *previousScaleDraw = this->scaleDraw(); if ( scaleDraw == NULL || scaleDraw == previousScaleDraw ) return; if ( previousScaleDraw ) scaleDraw->setAlignment(previousScaleDraw->alignment()); setAbstractScaleDraw(scaleDraw); layoutSlider(); } /*! \return the scale draw of the slider \sa setScaleDraw() */ const QwtScaleDraw *QwtSlider::scaleDraw() const { return (QwtScaleDraw *)abstractScaleDraw(); } /*! \return the scale draw of the slider \sa setScaleDraw() */ QwtScaleDraw *QwtSlider::scaleDraw() { return (QwtScaleDraw *)abstractScaleDraw(); } //! Notify changed scale void QwtSlider::scaleChange() { layoutSlider(); } //! Notify change in font void QwtSlider::fontChange(const QFont &f) { QwtAbstractSlider::fontChange( f ); layoutSlider(); } //! Draw the slider into the specified rectangle. void QwtSlider::drawSlider(QPainter *p, const QRect &r) { QRect cr(r); if (d_data->bgStyle & BgTrough) { qDrawShadePanel(p, r.x(), r.y(), r.width(), r.height(), #if QT_VERSION < 0x040000 colorGroup(), #else palette(), #endif true, d_data->borderWidth,0); cr.setRect(r.x() + d_data->borderWidth, r.y() + d_data->borderWidth, r.width() - 2 * d_data->borderWidth, r.height() - 2 * d_data->borderWidth); p->fillRect(cr.x(), cr.y(), cr.width(), cr.height(), #if QT_VERSION < 0x040000 colorGroup().brush(QColorGroup::Mid) #else palette().brush(QPalette::Mid) #endif ); } if ( d_data->bgStyle & BgSlot) { int ws = 4; int ds = d_data->thumbLength / 2 - 4; if ( ds < 1 ) ds = 1; QRect rSlot; if (orientation() == Qt::Horizontal) { if ( cr.height() & 1 ) ws++; rSlot = QRect(cr.x() + ds, cr.y() + (cr.height() - ws) / 2, cr.width() - 2 * ds, ws); } else { if ( cr.width() & 1 ) ws++; rSlot = QRect(cr.x() + (cr.width() - ws) / 2, cr.y() + ds, ws, cr.height() - 2 * ds); } p->fillRect(rSlot.x(), rSlot.y(), rSlot.width(), rSlot.height(), #if QT_VERSION < 0x040000 colorGroup().brush(QColorGroup::Dark) #else palette().brush(QPalette::Dark) #endif ); qDrawShadePanel(p, rSlot.x(), rSlot.y(), rSlot.width(), rSlot.height(), #if QT_VERSION < 0x040000 colorGroup(), #else palette(), #endif true, 1 ,0); } if ( isValid() ) drawThumb(p, cr, xyPosition(value())); } //! Draw the thumb at a position void QwtSlider::drawThumb(QPainter *p, const QRect &sliderRect, int pos) { pos++; // shade line points one pixel below if (orientation() == Qt::Horizontal) { qDrawShadePanel(p, pos - d_data->thumbLength / 2, sliderRect.y(), d_data->thumbLength, sliderRect.height(), #if QT_VERSION < 0x040000 colorGroup(), #else palette(), #endif false, d_data->borderWidth, #if QT_VERSION < 0x040000 &colorGroup().brush(QColorGroup::Button) #else &palette().brush(QPalette::Button) #endif ); qDrawShadeLine(p, pos, sliderRect.y(), pos, sliderRect.y() + sliderRect.height() - 2, #if QT_VERSION < 0x040000 colorGroup(), #else palette(), #endif true, 1); } else { // Vertical qDrawShadePanel(p,sliderRect.x(), pos - d_data->thumbLength / 2, sliderRect.width(), d_data->thumbLength, #if QT_VERSION < 0x040000 colorGroup(), #else palette(), #endif false, d_data->borderWidth, #if QT_VERSION < 0x040000 &colorGroup().brush(QColorGroup::Button) #else &palette().brush(QPalette::Button) #endif ); qDrawShadeLine(p, sliderRect.x(), pos, sliderRect.x() + sliderRect.width() - 2, pos, #if QT_VERSION < 0x040000 colorGroup(), #else palette(), #endif true, 1); } } //! Find the x/y position for a given value v int QwtSlider::xyPosition(double v) const { return d_data->map.transform(v); } //! Determine the value corresponding to a specified mouse location. double QwtSlider::getValue(const QPoint &p) { return d_data->map.invTransform( orientation() == Qt::Horizontal ? p.x() : p.y()); } /*! \brief Determine scrolling mode and direction \param p point \param scrollMode Scrolling mode \param direction Direction */ void QwtSlider::getScrollMode(const QPoint &p, int &scrollMode, int &direction ) { if (!d_data->sliderRect.contains(p)) { scrollMode = ScrNone; direction = 0; return; } const int pos = ( orientation() == Qt::Horizontal ) ? p.x() : p.y(); const int markerPos = xyPosition(value()); if ((pos > markerPos - d_data->thumbLength / 2) && (pos < markerPos + d_data->thumbLength / 2)) { scrollMode = ScrMouse; direction = 0; return; } scrollMode = ScrPage; direction = (pos > markerPos) ? 1 : -1; if ( scaleDraw()->map().p1() > scaleDraw()->map().p2() ) direction = -direction; } //! Qt paint event void QwtSlider::paintEvent(QPaintEvent *e) { const QRect &ur = e->rect(); if ( ur.isValid() ) { #if QT_VERSION < 0x040000 QwtPaintBuffer paintBuffer(this, ur); draw(paintBuffer.painter(), ur); #else QPainter painter(this); draw(&painter, ur); #endif } } //! Draw the QwtSlider void QwtSlider::draw(QPainter *painter, const QRect&) { if (d_data->scalePos != NoScale) { #if QT_VERSION < 0x040000 scaleDraw()->draw(painter, colorGroup()); #else scaleDraw()->draw(painter, palette()); #endif } drawSlider(painter, d_data->sliderRect); if ( hasFocus() ) QwtPainter::drawFocusRect(painter, this, d_data->sliderRect); } //! Qt resize event void QwtSlider::resizeEvent(QResizeEvent *) { layoutSlider( false ); } /*! Recalculate the slider's geometry and layout based on the current rect and fonts. \param update_geometry notify the layout system and call update to redraw the scale */ void QwtSlider::layoutSlider( bool update_geometry ) { int sliderWidth = d_data->thumbWidth; int sld1 = d_data->thumbLength / 2 - 1; int sld2 = d_data->thumbLength / 2 + d_data->thumbLength % 2; if ( d_data->bgStyle & BgTrough ) { sliderWidth += 2 * d_data->borderWidth; sld1 += d_data->borderWidth; sld2 += d_data->borderWidth; } int scd = 0; if ( d_data->scalePos != NoScale ) { int d1, d2; scaleDraw()->getBorderDistHint(font(), d1, d2); scd = qwtMax(d1, d2); } int slo = scd - sld1; if ( slo < 0 ) slo = 0; int x, y, length; const QRect r = rect(); if (orientation() == Qt::Horizontal) { switch (d_data->scalePos) { case TopScale: { d_data->sliderRect.setRect( r.x() + d_data->xMargin + slo, r.y() + r.height() - d_data->yMargin - sliderWidth, r.width() - 2 * d_data->xMargin - 2 * slo, sliderWidth); x = d_data->sliderRect.x() + sld1; y = d_data->sliderRect.y() - d_data->scaleDist; break; } case BottomScale: { d_data->sliderRect.setRect( r.x() + d_data->xMargin + slo, r.y() + d_data->yMargin, r.width() - 2 * d_data->xMargin - 2 * slo, sliderWidth); x = d_data->sliderRect.x() + sld1; y = d_data->sliderRect.y() + d_data->sliderRect.height() + d_data->scaleDist; break; } case NoScale: // like Bottom, but no scale. See QwtSlider(). default: { // inconsistent orientation and scale position d_data->sliderRect.setRect( r.x() + d_data->xMargin + slo, r.y() + d_data->yMargin, r.width() - 2 * d_data->xMargin - 2 * slo, sliderWidth); x = d_data->sliderRect.x() + sld1; y = 0; break; } } length = d_data->sliderRect.width() - (sld1 + sld2); } else { // if (orientation() == Qt::Vertical switch (d_data->scalePos) { case RightScale: d_data->sliderRect.setRect( r.x() + d_data->xMargin, r.y() + d_data->yMargin + slo, sliderWidth, r.height() - 2 * d_data->yMargin - 2 * slo); x = d_data->sliderRect.x() + d_data->sliderRect.width() + d_data->scaleDist; y = d_data->sliderRect.y() + sld1; break; case LeftScale: d_data->sliderRect.setRect( r.x() + r.width() - sliderWidth - d_data->xMargin, r.y() + d_data->yMargin + slo, sliderWidth, r.height() - 2 * d_data->yMargin - 2 * slo); x = d_data->sliderRect.x() - d_data->scaleDist; y = d_data->sliderRect.y() + sld1; break; case NoScale: // like Left, but no scale. See QwtSlider(). default: // inconsistent orientation and scale position d_data->sliderRect.setRect( r.x() + r.width() - sliderWidth - d_data->xMargin, r.y() + d_data->yMargin + slo, sliderWidth, r.height() - 2 * d_data->yMargin - 2 * slo); x = 0; y = d_data->sliderRect.y() + sld1; break; } length = d_data->sliderRect.height() - (sld1 + sld2); } scaleDraw()->move(x, y); scaleDraw()->setLength(length); d_data->map.setPaintXInterval(scaleDraw()->map().p1(), scaleDraw()->map().p2()); if ( update_geometry ) { d_data->sizeHintCache = QSize(); // invalidate updateGeometry(); update(); } } //! Notify change of value void QwtSlider::valueChange() { QwtAbstractSlider::valueChange(); update(); } //! Notify change of range void QwtSlider::rangeChange() { d_data->map.setScaleInterval(minValue(), maxValue()); if (autoScale()) rescale(minValue(), maxValue()); QwtAbstractSlider::rangeChange(); layoutSlider(); } /*! \brief Set distances between the widget's border and internals. \param xMargin Horizontal margin \param yMargin Vertical margin */ void QwtSlider::setMargins(int xMargin, int yMargin) { if ( xMargin < 0 ) xMargin = 0; if ( yMargin < 0 ) yMargin = 0; if ( xMargin != d_data->xMargin || yMargin != d_data->yMargin ) { d_data->xMargin = xMargin; d_data->yMargin = yMargin; layoutSlider(); } } /*! Set the background style. */ void QwtSlider::setBgStyle(BGSTYLE st) { d_data->bgStyle = st; layoutSlider(); } /*! \return the background style. */ QwtSlider::BGSTYLE QwtSlider::bgStyle() const { return d_data->bgStyle; } /*! \return the thumb length. */ int QwtSlider::thumbLength() const { return d_data->thumbLength; } /*! \return the thumb width. */ int QwtSlider::thumbWidth() const { return d_data->thumbWidth; } /*! \return the border width. */ int QwtSlider::borderWidth() const { return d_data->borderWidth; } /*! \return QwtSlider::minimumSizeHint() */ QSize QwtSlider::sizeHint() const { return minimumSizeHint(); } /*! \brief Return a minimum size hint \warning The return value of QwtSlider::minimumSizeHint() depends on the font and the scale. */ QSize QwtSlider::minimumSizeHint() const { if (!d_data->sizeHintCache.isEmpty()) return d_data->sizeHintCache; int sliderWidth = d_data->thumbWidth; if (d_data->bgStyle & BgTrough) sliderWidth += 2 * d_data->borderWidth; int w = 0, h = 0; if (d_data->scalePos != NoScale) { int d1, d2; scaleDraw()->getBorderDistHint(font(), d1, d2); int msMbd = qwtMax(d1, d2); int mbd = d_data->thumbLength / 2; if (d_data->bgStyle & BgTrough) mbd += d_data->borderWidth; if ( mbd < msMbd ) mbd = msMbd; const int sdExtent = scaleDraw()->extent( QPen(), font() ); const int sdLength = scaleDraw()->minLength( QPen(), font() ); h = sliderWidth + sdExtent + d_data->scaleDist; w = sdLength - 2 * msMbd + 2 * mbd; } else { // no scale w = 200; h = sliderWidth; } if ( orientation() == Qt::Vertical ) qSwap(w, h); w += 2 * d_data->xMargin; h += 2 * d_data->yMargin; d_data->sizeHintCache = QSize(w, h); return d_data->sizeHintCache; }