Skip to content
ScrollZoomer.cc 12 KiB
Newer Older
pixhawk's avatar
pixhawk committed
#include <qevent.h>
#include <qwt_plot_canvas.h>
#include <qwt_plot_layout.h>
#include <qwt_scale_engine.h>
#include <qwt_scale_widget.h>
#include <Scrollbar.h>
#include <ScrollZoomer.h>

class ScrollData
{
pixhawk's avatar
pixhawk committed
public:
    ScrollData():
        scrollBar(NULL),
        position(ScrollZoomer::OppositeToScale),
#if QT_VERSION < 0x040000
        mode(QScrollView::Auto)
#else
        mode(Qt::ScrollBarAsNeeded)
#endif
    {
    }

    ~ScrollData() {
pixhawk's avatar
pixhawk committed
        delete scrollBar;
    }

    ScrollBar *scrollBar;
    ScrollZoomer::ScrollBarPosition position;
#if QT_VERSION < 0x040000
    QScrollView::ScrollBarMode mode;
#else
    Qt::ScrollBarPolicy mode;
#endif
};

ScrollZoomer::ScrollZoomer(QwtPlotCanvas *canvas):
    QwtPlotZoomer(canvas),
    d_cornerWidget(NULL),
    d_hScrollData(NULL),
    d_vScrollData(NULL),
    d_inZoom(false)
{
    if ( !canvas )
        return;

    d_hScrollData = new ScrollData;
    d_vScrollData = new ScrollData;
}

ScrollZoomer::~ScrollZoomer()
{
    delete d_cornerWidget;
    delete d_vScrollData;
    delete d_hScrollData;
}

void ScrollZoomer::rescale()
{
    QwtScaleWidget *xScale = plot()->axisWidget(xAxis());
    QwtScaleWidget *yScale = plot()->axisWidget(yAxis());

    if ( zoomRectIndex() <= 0 ) {
        if ( d_inZoom ) {
pixhawk's avatar
pixhawk committed
            xScale->setMinBorderDist(0, 0);
            yScale->setMinBorderDist(0, 0);
            d_inZoom = false;
        }
    } else {
        if ( !d_inZoom ) {
pixhawk's avatar
pixhawk committed
            /*
             We set a minimum border distance.
             Otherwise the canvas size changes when scrolling,
             between situations where the major ticks are at
             the canvas borders (requiring extra space for the label)
             and situations where all labels can be painted below/top
             or left/right of the canvas.
             */
            int start, end;

            xScale->getBorderDistHint(start, end);
            xScale->setMinBorderDist(start, end);

            yScale->getBorderDistHint(start, end);
            yScale->setMinBorderDist(start, end);

            d_inZoom = false;
        }
    }

    QwtPlotZoomer::rescale();
    updateScrollBars();
}

ScrollBar *ScrollZoomer::scrollBar(Qt::Orientation o)
{
    ScrollBar *&sb = (o == Qt::Vertical)
                     ? d_vScrollData->scrollBar : d_hScrollData->scrollBar;
pixhawk's avatar
pixhawk committed

    if ( sb == NULL ) {
pixhawk's avatar
pixhawk committed
        sb = new ScrollBar(o, canvas());
        sb->hide();
        connect(sb,
                SIGNAL(valueChanged(Qt::Orientation, double, double)),
                SLOT(scrollBarMoved(Qt::Orientation, double, double)));
pixhawk's avatar
pixhawk committed
    }
    return sb;
}

ScrollBar *ScrollZoomer::horizontalScrollBar() const
{
    return d_hScrollData->scrollBar;
}

ScrollBar *ScrollZoomer::verticalScrollBar() const
{
    return d_vScrollData->scrollBar;
}

#if QT_VERSION < 0x040000
void ScrollZoomer::setHScrollBarMode(QScrollView::ScrollBarMode mode)
#else
void ScrollZoomer::setHScrollBarMode(Qt::ScrollBarPolicy mode)
#endif
{
    if ( hScrollBarMode() != mode ) {
pixhawk's avatar
pixhawk committed
        d_hScrollData->mode = mode;
        updateScrollBars();
    }
}

#if QT_VERSION < 0x040000
void ScrollZoomer::setVScrollBarMode(QScrollView::ScrollBarMode mode)
#else
void ScrollZoomer::setVScrollBarMode(Qt::ScrollBarPolicy mode)
#endif
{
    if ( vScrollBarMode() != mode ) {
pixhawk's avatar
pixhawk committed
        d_vScrollData->mode = mode;
        updateScrollBars();
    }
}

#if QT_VERSION < 0x040000
QScrollView::ScrollBarMode ScrollZoomer::hScrollBarMode() const
#else
Qt::ScrollBarPolicy ScrollZoomer::hScrollBarMode() const
#endif
{
    return d_hScrollData->mode;
}

#if QT_VERSION < 0x040000
QScrollView::ScrollBarMode ScrollZoomer::vScrollBarMode() const
#else
Qt::ScrollBarPolicy ScrollZoomer::vScrollBarMode() const
#endif
{
    return d_vScrollData->mode;
}

void ScrollZoomer::setHScrollBarPosition(ScrollBarPosition pos)
{
    if ( d_hScrollData->position != pos ) {
pixhawk's avatar
pixhawk committed
        d_hScrollData->position = pos;
        updateScrollBars();
    }
}

void ScrollZoomer::setVScrollBarPosition(ScrollBarPosition pos)
{
    if ( d_vScrollData->position != pos ) {
pixhawk's avatar
pixhawk committed
        d_vScrollData->position = pos;
        updateScrollBars();
    }
}

ScrollZoomer::ScrollBarPosition ScrollZoomer::hScrollBarPosition() const
{
    return d_hScrollData->position;
}

ScrollZoomer::ScrollBarPosition ScrollZoomer::vScrollBarPosition() const
{
    return d_vScrollData->position;
}

void ScrollZoomer::setCornerWidget(QWidget *w)
{
    if ( w != d_cornerWidget ) {
        if ( canvas() ) {
pixhawk's avatar
pixhawk committed
            delete d_cornerWidget;
            d_cornerWidget = w;
            if ( d_cornerWidget->parent() != canvas() ) {
pixhawk's avatar
pixhawk committed
#if QT_VERSION < 0x040000
                d_cornerWidget->reparent(canvas(), QPoint(0, 0));
#else
                d_cornerWidget->setParent(canvas());
#endif
            }

            updateScrollBars();
        }
    }
}

QWidget *ScrollZoomer::cornerWidget() const
{
    return d_cornerWidget;
}

bool ScrollZoomer::eventFilter(QObject *o, QEvent *e)
{
    if (  o == canvas() ) {
        switch(e->type()) {
        case QEvent::Resize: {
            const int fw = ((QwtPlotCanvas *)canvas())->frameWidth();

            QRect rect;
            rect.setSize(((QResizeEvent *)e)->size());
            rect.setRect(rect.x() + fw, rect.y() + fw,
                         rect.width() - 2 * fw, rect.height() - 2 * fw);

            layoutScrollBars(rect);
            break;
        }
        case QEvent::ChildRemoved: {
            const QObject *child = ((QChildEvent *)e)->child();
            if ( child == d_cornerWidget )
                d_cornerWidget = NULL;
            else if ( child == d_hScrollData->scrollBar )
                d_hScrollData->scrollBar = NULL;
            else if ( child == d_vScrollData->scrollBar )
                d_vScrollData->scrollBar = NULL;
            break;
        }
        default:
            break;
pixhawk's avatar
pixhawk committed
        }
    }
    return QwtPlotZoomer::eventFilter(o, e);
}

bool ScrollZoomer::needScrollBar(Qt::Orientation o) const
{
#if QT_VERSION < 0x040000
    QScrollView::ScrollBarMode mode;
#else
    Qt::ScrollBarPolicy mode;
#endif
    double zoomMin, zoomMax, baseMin, baseMax;

    if ( o == Qt::Horizontal ) {
pixhawk's avatar
pixhawk committed
        mode = d_hScrollData->mode;
        baseMin = zoomBase().left();
        baseMax = zoomBase().right();
        zoomMin = zoomRect().left();
        zoomMax = zoomRect().right();
pixhawk's avatar
pixhawk committed
        mode = d_vScrollData->mode;
        baseMin = zoomBase().top();
        baseMax = zoomBase().bottom();
        zoomMin = zoomRect().top();
        zoomMax = zoomRect().bottom();
    }

    bool needed = false;
    switch(mode) {
pixhawk's avatar
pixhawk committed
#if QT_VERSION < 0x040000
    case QScrollView::AlwaysOn:
pixhawk's avatar
pixhawk committed
#else
    case Qt::ScrollBarAlwaysOn:
pixhawk's avatar
pixhawk committed
#endif
        needed = true;
        break;
pixhawk's avatar
pixhawk committed
#if QT_VERSION < 0x040000
    case QScrollView::AlwaysOff:
pixhawk's avatar
pixhawk committed
#else
    case Qt::ScrollBarAlwaysOff:
pixhawk's avatar
pixhawk committed
#endif
        needed = false;
        break;
    default: {
        if ( baseMin < zoomMin || baseMax > zoomMax )
            needed = true;
        break;
    }
pixhawk's avatar
pixhawk committed
    }
    return needed;
}

void ScrollZoomer::updateScrollBars()
{
    if ( !canvas() )
        return;

    const int xAxis = QwtPlotZoomer::xAxis();
    const int yAxis = QwtPlotZoomer::yAxis();

    int xScrollBarAxis = xAxis;
    if ( hScrollBarPosition() == OppositeToScale )
        xScrollBarAxis = oppositeAxis(xScrollBarAxis);

    int yScrollBarAxis = yAxis;
    if ( vScrollBarPosition() == OppositeToScale )
        yScrollBarAxis = oppositeAxis(yScrollBarAxis);


    QwtPlotLayout *layout = plot()->plotLayout();

    bool showHScrollBar = needScrollBar(Qt::Horizontal);
    if ( showHScrollBar ) {
pixhawk's avatar
pixhawk committed
        ScrollBar *sb = scrollBar(Qt::Horizontal);

        sb->setPalette(plot()->palette());

        const QwtScaleEngine *se = plot()->axisScaleEngine(xAxis);
        sb->setInverted(se->testAttribute(QwtScaleEngine::Inverted));

        sb->setBase(zoomBase().left(), zoomBase().right());
        sb->moveSlider(zoomRect().left(), zoomRect().right());

        if ( !sb->isVisibleTo(canvas()) ) {
pixhawk's avatar
pixhawk committed
            sb->show();
            layout->setCanvasMargin(layout->canvasMargin(xScrollBarAxis)
                                    + sb->extent(), xScrollBarAxis);
pixhawk's avatar
pixhawk committed
        }
    } else {
        if ( horizontalScrollBar() ) {
pixhawk's avatar
pixhawk committed
            horizontalScrollBar()->hide();
            layout->setCanvasMargin(layout->canvasMargin(xScrollBarAxis)
                                    - horizontalScrollBar()->extent(), xScrollBarAxis);
pixhawk's avatar
pixhawk committed
        }
    }

    bool showVScrollBar = needScrollBar(Qt::Vertical);
    if ( showVScrollBar ) {
pixhawk's avatar
pixhawk committed
        ScrollBar *sb = scrollBar(Qt::Vertical);

        sb->setPalette(plot()->palette());

        const QwtScaleEngine *se = plot()->axisScaleEngine(xAxis);
        sb->setInverted(!(se->testAttribute(QwtScaleEngine::Inverted)));

        sb->setBase(zoomBase().top(), zoomBase().bottom());
        sb->moveSlider(zoomRect().top(), zoomRect().bottom());

        if ( !sb->isVisibleTo(canvas()) ) {
pixhawk's avatar
pixhawk committed
            sb->show();
            layout->setCanvasMargin(layout->canvasMargin(yScrollBarAxis)
                                    + sb->extent(), yScrollBarAxis);
pixhawk's avatar
pixhawk committed
        }
    } else {
        if ( verticalScrollBar() ) {
pixhawk's avatar
pixhawk committed
            verticalScrollBar()->hide();
            layout->setCanvasMargin(layout->canvasMargin(yScrollBarAxis)
                                    - verticalScrollBar()->extent(), yScrollBarAxis);
pixhawk's avatar
pixhawk committed
        }
    }

    if ( showHScrollBar && showVScrollBar ) {
        if ( d_cornerWidget == NULL ) {
pixhawk's avatar
pixhawk committed
            d_cornerWidget = new QWidget(canvas());
#if QT_VERSION >= 0x040100
            d_cornerWidget->setAutoFillBackground(true);
#endif
            d_cornerWidget->setPalette(plot()->palette());
        }
        d_cornerWidget->show();
pixhawk's avatar
pixhawk committed
        if ( d_cornerWidget )
            d_cornerWidget->hide();
    }

    layoutScrollBars(((QwtPlotCanvas *)canvas())->contentsRect());
    plot()->updateLayout();
}

void ScrollZoomer::layoutScrollBars(const QRect &rect)
{
    int hPos = xAxis();
    if ( hScrollBarPosition() == OppositeToScale )
        hPos = oppositeAxis(hPos);

    int vPos = yAxis();
    if ( vScrollBarPosition() == OppositeToScale )
        vPos = oppositeAxis(vPos);

    ScrollBar *hScrollBar = horizontalScrollBar();
    ScrollBar *vScrollBar = verticalScrollBar();

    const int hdim = hScrollBar ? hScrollBar->extent() : 0;
    const int vdim = vScrollBar ? vScrollBar->extent() : 0;

    if ( hScrollBar && hScrollBar->isVisible() ) {
pixhawk's avatar
pixhawk committed
        int x = rect.x();
        int y = (hPos == QwtPlot::xTop)
                ? rect.top() : rect.bottom() - hdim + 1;
pixhawk's avatar
pixhawk committed
        int w = rect.width();

        if ( vScrollBar && vScrollBar->isVisible() ) {
pixhawk's avatar
pixhawk committed
            if ( vPos == QwtPlot::yLeft )
                x += vdim;
            w -= vdim;
        }

        hScrollBar->setGeometry(x, y, w, hdim);
    }
    if ( vScrollBar && vScrollBar->isVisible() ) {
pixhawk's avatar
pixhawk committed
        int pos = yAxis();
        if ( vScrollBarPosition() == OppositeToScale )
            pos = oppositeAxis(pos);

        int x = (vPos == QwtPlot::yLeft)
                ? rect.left() : rect.right() - vdim + 1;
pixhawk's avatar
pixhawk committed
        int y = rect.y();

        int h = rect.height();

        if ( hScrollBar && hScrollBar->isVisible() ) {
pixhawk's avatar
pixhawk committed
            if ( hPos == QwtPlot::xTop )
                y += hdim;

            h -= hdim;
        }

        vScrollBar->setGeometry(x, y, vdim, h);
    }
    if ( hScrollBar && hScrollBar->isVisible() &&
            vScrollBar && vScrollBar->isVisible() ) {
        if ( d_cornerWidget ) {
pixhawk's avatar
pixhawk committed
            QRect cornerRect(
                vScrollBar->pos().x(), hScrollBar->pos().y(),
                vdim, hdim);
            d_cornerWidget->setGeometry(cornerRect);
        }
    }
}

void ScrollZoomer::scrollBarMoved(Qt::Orientation o, double min, double)
{
    if ( o == Qt::Horizontal )
        move(QPoint(min, zoomRect().top()));
pixhawk's avatar
pixhawk committed
    else
        move(QPoint(zoomRect().left(), min));
pixhawk's avatar
pixhawk committed

    emit zoomed(zoomRect());
}

int ScrollZoomer::oppositeAxis(int axis) const
{
    switch(axis) {
    case QwtPlot::xBottom:
        return QwtPlot::xTop;
    case QwtPlot::xTop:
        return QwtPlot::xBottom;
    case QwtPlot::yLeft:
        return QwtPlot::yRight;
    case QwtPlot::yRight:
        return QwtPlot::yLeft;
    default:
        break;
pixhawk's avatar
pixhawk committed
    }

    return axis;
}