Skip to content
qwt_plot_layout.cpp 42.9 KiB
Newer Older
pixhawk's avatar
pixhawk committed
/* -*- 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
 *****************************************************************************/

Bryant's avatar
Bryant committed
#include "qwt_plot_layout.h"
pixhawk's avatar
pixhawk committed
#include "qwt_text.h"
#include "qwt_text_label.h"
#include "qwt_scale_widget.h"
Bryant's avatar
Bryant committed
#include "qwt_abstract_legend.h"
#include <qscrollbar.h>
#include <qmath.h>
pixhawk's avatar
pixhawk committed

class QwtPlotLayout::LayoutData
{
public:
Bryant's avatar
Bryant committed
    void init( const QwtPlot *, const QRectF &rect );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    struct t_legendData
    {
pixhawk's avatar
pixhawk committed
        int frameWidth;
Bryant's avatar
Bryant committed
        int hScrollExtent;
        int vScrollExtent;
pixhawk's avatar
pixhawk committed
        QSize hint;
    } legend;
Bryant's avatar
Bryant committed
    struct t_titleData
    {
pixhawk's avatar
pixhawk committed
        QwtText text;
        int frameWidth;
    } title;

Bryant's avatar
Bryant committed
    struct t_footerData
    {
        QwtText text;
        int frameWidth;
    } footer;

    struct t_scaleData
    {
pixhawk's avatar
pixhawk committed
        bool isEnabled;
        const QwtScaleWidget *scaleWidget;
        QFont scaleFont;
        int start;
        int end;
        int baseLineOffset;
Bryant's avatar
Bryant committed
        double tickOffset;
pixhawk's avatar
pixhawk committed
        int dimWithoutTitle;
    } scale[QwtPlot::axisCnt];

Bryant's avatar
Bryant committed
    struct t_canvasData
    {
        int contentsMargins[ QwtPlot::axisCnt ];

pixhawk's avatar
pixhawk committed
    } canvas;
};

/*
  Extract all layout relevant data from the plot components
*/
Bryant's avatar
Bryant committed
void QwtPlotLayout::LayoutData::init( const QwtPlot *plot, const QRectF &rect )
pixhawk's avatar
pixhawk committed
{
    // legend

Bryant's avatar
Bryant committed
    if ( plot->legend() )
    {
pixhawk's avatar
pixhawk committed
        legend.frameWidth = plot->legend()->frameWidth();
Bryant's avatar
Bryant committed
        legend.hScrollExtent =
            plot->legend()->scrollExtent( Qt::Horizontal );
        legend.vScrollExtent =
            plot->legend()->scrollExtent( Qt::Vertical );
pixhawk's avatar
pixhawk committed

        const QSize hint = plot->legend()->sizeHint();

Bryant's avatar
Bryant committed
        int w = qMin( hint.width(), qFloor( rect.width() ) );
        int h = plot->legend()->heightForWidth( w );
        if ( h <= 0 )
pixhawk's avatar
pixhawk committed
            h = hint.height();

        if ( h > rect.height() )
Bryant's avatar
Bryant committed
            w += legend.hScrollExtent;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        legend.hint = QSize( w, h );
pixhawk's avatar
pixhawk committed
    }

pixhawk's avatar
pixhawk committed

    title.frameWidth = 0;
    title.text = QwtText();

Bryant's avatar
Bryant committed
    if ( plot->titleLabel() )
    {
pixhawk's avatar
pixhawk committed
        const QwtTextLabel *label = plot->titleLabel();
        title.text = label->text();
Bryant's avatar
Bryant committed
        if ( !( title.text.testPaintAttribute( QwtText::PaintUsingTextFont ) ) )
            title.text.setFont( label->font() );
pixhawk's avatar
pixhawk committed
        title.frameWidth = plot->titleLabel()->frameWidth();
    }

Bryant's avatar
Bryant committed
    // footer

    footer.frameWidth = 0;
    footer.text = QwtText();

    if ( plot->footerLabel() )
    {
        const QwtTextLabel *label = plot->footerLabel();
        footer.text = label->text();
        if ( !( footer.text.testPaintAttribute( QwtText::PaintUsingTextFont ) ) )
            footer.text.setFont( label->font() );

        footer.frameWidth = plot->footerLabel()->frameWidth();
    }

pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
    {
        if ( plot->axisEnabled( axis ) )
        {
            const QwtScaleWidget *scaleWidget = plot->axisWidget( axis );
pixhawk's avatar
pixhawk committed

            scale[axis].isEnabled = true;

            scale[axis].scaleWidget = scaleWidget;

            scale[axis].scaleFont = scaleWidget->font();

            scale[axis].start = scaleWidget->startBorderDist();
            scale[axis].end = scaleWidget->endBorderDist();

            scale[axis].baseLineOffset = scaleWidget->margin();
            scale[axis].tickOffset = scaleWidget->margin();
            if ( scaleWidget->scaleDraw()->hasComponent(
Bryant's avatar
Bryant committed
                QwtAbstractScaleDraw::Ticks ) )
            {
                scale[axis].tickOffset +=
Bryant's avatar
Bryant committed
                    scaleWidget->scaleDraw()->maxTickLength();
pixhawk's avatar
pixhawk committed
            }

            scale[axis].dimWithoutTitle = scaleWidget->dimForLength(
Bryant's avatar
Bryant committed
                QWIDGETSIZE_MAX, scale[axis].scaleFont );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
            if ( !scaleWidget->title().isEmpty() )
            {
                scale[axis].dimWithoutTitle -=
Bryant's avatar
Bryant committed
                    scaleWidget->titleHeightForWidth( QWIDGETSIZE_MAX );
pixhawk's avatar
pixhawk committed
            }
Bryant's avatar
Bryant committed
        }
        else
        {
pixhawk's avatar
pixhawk committed
            scale[axis].isEnabled = false;
            scale[axis].start = 0;
            scale[axis].end = 0;
            scale[axis].baseLineOffset = 0;
Bryant's avatar
Bryant committed
            scale[axis].tickOffset = 0.0;
pixhawk's avatar
pixhawk committed
            scale[axis].dimWithoutTitle = 0;
        }
    }

pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    plot->canvas()->getContentsMargins( 
        &canvas.contentsMargins[ QwtPlot::yLeft ], 
        &canvas.contentsMargins[ QwtPlot::xTop ],
        &canvas.contentsMargins[ QwtPlot::yRight ],
        &canvas.contentsMargins[ QwtPlot::xBottom ] );
pixhawk's avatar
pixhawk committed
}

class QwtPlotLayout::PrivateData
{
public:
    PrivateData():
Bryant's avatar
Bryant committed
        spacing( 5 )
    {
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    QRectF titleRect;
    QRectF footerRect;
    QRectF legendRect;
    QRectF scaleRect[QwtPlot::axisCnt];
    QRectF canvasRect;
pixhawk's avatar
pixhawk committed

    QwtPlotLayout::LayoutData layoutData;

    QwtPlot::LegendPosition legendPos;
    double legendRatio;
    unsigned int spacing;
    unsigned int canvasMargin[QwtPlot::axisCnt];
Bryant's avatar
Bryant committed
    bool alignCanvasToScales[QwtPlot::axisCnt];
pixhawk's avatar
pixhawk committed
};

/*!
  \brief Constructor
 */

QwtPlotLayout::QwtPlotLayout()
{
    d_data = new PrivateData;

Bryant's avatar
Bryant committed
    setLegendPosition( QwtPlot::BottomLegend );
    setCanvasMargin( 4 );
    setAlignCanvasToScales( false );
pixhawk's avatar
pixhawk committed

    invalidate();
}

//! Destructor
QwtPlotLayout::~QwtPlotLayout()
{
    delete d_data;
}

/*!
  Change a margin of the canvas. The margin is the space
  above/below the scale ticks. A negative margin will
  be set to -1, excluding the borders of the scales.
pixhawk's avatar
pixhawk committed
  \param margin New margin
  \param axis One of QwtPlot::Axis. Specifies where the position of the margin.
pixhawk's avatar
pixhawk committed
              -1 means margin at all borders.
  \sa canvasMargin()
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \warning The margin will have no effect when alignCanvasToScale() is true
pixhawk's avatar
pixhawk committed
*/

Bryant's avatar
Bryant committed
void QwtPlotLayout::setCanvasMargin( int margin, int axis )
pixhawk's avatar
pixhawk committed
{
    if ( margin < -1 )
        margin = -1;

Bryant's avatar
Bryant committed
    if ( axis == -1 )
    {
        for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
pixhawk's avatar
pixhawk committed
            d_data->canvasMargin[axis] = margin;
Bryant's avatar
Bryant committed
    }
    else if ( axis >= 0 && axis < QwtPlot::axisCnt )
pixhawk's avatar
pixhawk committed
        d_data->canvasMargin[axis] = margin;
}

/*!
Bryant's avatar
Bryant committed
    \param axisId Axis index
pixhawk's avatar
pixhawk committed
    \return Margin around the scale tick borders
    \sa setCanvasMargin()
*/
Bryant's avatar
Bryant committed
int QwtPlotLayout::canvasMargin( int axisId ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( axisId < 0 || axisId >= QwtPlot::axisCnt )
pixhawk's avatar
pixhawk committed
        return 0;

Bryant's avatar
Bryant committed
    return d_data->canvasMargin[axisId];
}

/*!
  \brief Set the align-canvas-to-axis-scales flag for all axes

  \param on True/False
  \sa setAlignCanvasToScale(), alignCanvasToScale()
*/
void QwtPlotLayout::setAlignCanvasToScales( bool on )
{
    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
        d_data->alignCanvasToScales[axis] = on;
pixhawk's avatar
pixhawk committed
}

/*!
  Change the align-canvas-to-axis-scales setting. The canvas may:
Bryant's avatar
Bryant committed

pixhawk's avatar
pixhawk committed
  - extend beyond the axis scale ends to maximize its size,
  - align with the axis scale ends to control its size.

Bryant's avatar
Bryant committed
  The axisId parameter is somehow confusing as it identifies a border
  of the plot and not the axes, that are aligned. F.e when QwtPlot::yLeft
  is set, the left end of the the x-axes ( QwtPlot::xTop, QwtPlot::xBottom )
  is aligned.

  \param axisId Axis index
  \param on New align-canvas-to-axis-scales setting
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \sa setCanvasMargin(), alignCanvasToScale(), setAlignCanvasToScales()
  \warning In case of on == true canvasMargin() will have no effect
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtPlotLayout::setAlignCanvasToScale( int axisId, bool on )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( axisId >= 0 && axisId < QwtPlot::axisCnt )
        d_data->alignCanvasToScales[axisId] = on;
pixhawk's avatar
pixhawk committed
}

/*!
  Return the align-canvas-to-axis-scales setting. The canvas may:
  - extend beyond the axis scale ends to maximize its size
  - align with the axis scale ends to control its size.

Bryant's avatar
Bryant committed
  \param axisId Axis index
pixhawk's avatar
pixhawk committed
  \return align-canvas-to-axis-scales setting
Bryant's avatar
Bryant committed
  \sa setAlignCanvasToScale(), setAlignCanvasToScale(), setCanvasMargin()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
bool QwtPlotLayout::alignCanvasToScale( int axisId ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( axisId < 0 || axisId >= QwtPlot::axisCnt )
        return false;

    return d_data->alignCanvasToScales[ axisId ];
pixhawk's avatar
pixhawk committed
}

/*!
  Change the spacing of the plot. The spacing is the distance
  between the plot components.
Bryant's avatar
Bryant committed
  \param spacing New spacing
  \sa setCanvasMargin(), spacing()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtPlotLayout::setSpacing( int spacing )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    d_data->spacing = qMax( 0, spacing );
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \return Spacing
  \sa margin(), setSpacing()
pixhawk's avatar
pixhawk committed
*/
int QwtPlotLayout::spacing() const
{
    return d_data->spacing;
}

/*!
  \brief Specify the position of the legend
  \param pos The legend's position.
Bryant's avatar
Bryant committed
  \param ratio Ratio between legend and the bounding rectangle
               of title, footer, canvas and axes. The legend will be shrunk
pixhawk's avatar
pixhawk committed
               if it would need more space than the given ratio.
               The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0
               it will be reset to the default ratio.
               The default vertical/horizontal ratio is 0.33/0.5.

pixhawk's avatar
pixhawk committed
  \sa QwtPlot::setLegendPosition()
*/

Bryant's avatar
Bryant committed
void QwtPlotLayout::setLegendPosition( QwtPlot::LegendPosition pos, double ratio )
pixhawk's avatar
pixhawk committed
{
    if ( ratio > 1.0 )
        ratio = 1.0;

Bryant's avatar
Bryant committed
    switch ( pos )
    {
        case QwtPlot::TopLegend:
        case QwtPlot::BottomLegend:
            if ( ratio <= 0.0 )
                ratio = 0.33;
            d_data->legendRatio = ratio;
            d_data->legendPos = pos;
            break;
        case QwtPlot::LeftLegend:
        case QwtPlot::RightLegend:
            if ( ratio <= 0.0 )
                ratio = 0.5;
            d_data->legendRatio = ratio;
            d_data->legendPos = pos;
            break;
        default:
            break;
pixhawk's avatar
pixhawk committed
    }
}

/*!
  \brief Specify the position of the legend
  \param pos The legend's position. Valid values are
      \c QwtPlot::LeftLegend, \c QwtPlot::RightLegend,
pixhawk's avatar
pixhawk committed
      \c QwtPlot::TopLegend, \c QwtPlot::BottomLegend.
pixhawk's avatar
pixhawk committed
  \sa QwtPlot::setLegendPosition()
*/
Bryant's avatar
Bryant committed
void QwtPlotLayout::setLegendPosition( QwtPlot::LegendPosition pos )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    setLegendPosition( pos, 0.0 );
pixhawk's avatar
pixhawk committed
}

/*!
  \return Position of the legend
  \sa setLegendPosition(), QwtPlot::setLegendPosition(),
      QwtPlot::legendPosition()
*/
QwtPlot::LegendPosition QwtPlotLayout::legendPosition() const
{
    return d_data->legendPos;
}

/*!
  Specify the relative size of the legend in the plot
Bryant's avatar
Bryant committed
  \param ratio Ratio between legend and the bounding rectangle
               of title, footer, canvas and axes. The legend will be shrunk
pixhawk's avatar
pixhawk committed
               if it would need more space than the given ratio.
               The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0
               it will be reset to the default ratio.
               The default vertical/horizontal ratio is 0.33/0.5.
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtPlotLayout::setLegendRatio( double ratio )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    setLegendPosition( legendPosition(), ratio );
pixhawk's avatar
pixhawk committed
}

/*!
  \return The relative size of the legend in the plot.
  \sa setLegendPosition()
*/
double QwtPlotLayout::legendRatio() const
{
    return d_data->legendRatio;
}

Bryant's avatar
Bryant committed
/*!
  \brief Set the geometry for the title

  This method is intended to be used from derived layouts
  overloading activate()

  \sa titleRect(), activate()
 */
void QwtPlotLayout::setTitleRect( const QRectF &rect )
{
    d_data->titleRect = rect;
}

pixhawk's avatar
pixhawk committed
/*!
  \return Geometry for the title
  \sa activate(), invalidate()
*/
Bryant's avatar
Bryant committed
QRectF QwtPlotLayout::titleRect() const
pixhawk's avatar
pixhawk committed
{
    return d_data->titleRect;
}

/*!
Bryant's avatar
Bryant committed
  \brief Set the geometry for the footer

  This method is intended to be used from derived layouts
  overloading activate()

  \sa footerRect(), activate()
 */
void QwtPlotLayout::setFooterRect( const QRectF &rect )
{
    d_data->footerRect = rect;
}

/*!
  \return Geometry for the footer
pixhawk's avatar
pixhawk committed
  \sa activate(), invalidate()
*/
Bryant's avatar
Bryant committed
QRectF QwtPlotLayout::footerRect() const
{
    return d_data->footerRect;
}

/*!
  \brief Set the geometry for the legend

  This method is intended to be used from derived layouts
  overloading activate()

  \param rect Rectangle for the legend
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \sa legendRect(), activate()
 */
void QwtPlotLayout::setLegendRect( const QRectF &rect )
{
    d_data->legendRect = rect;
}

/*!
  \return Geometry for the legend
  \sa activate(), invalidate()
*/
QRectF QwtPlotLayout::legendRect() const
pixhawk's avatar
pixhawk committed
{
    return d_data->legendRect;
}

Bryant's avatar
Bryant committed
/*!
  \brief Set the geometry for an axis

  This method is intended to be used from derived layouts
  overloading activate()

  \param axis Axis index
  \param rect Rectangle for the scale

  \sa scaleRect(), activate()
 */
void QwtPlotLayout::setScaleRect( int axis, const QRectF &rect )
{
    if ( axis >= 0 && axis < QwtPlot::axisCnt )
        d_data->scaleRect[axis] = rect;
}

pixhawk's avatar
pixhawk committed
/*!
  \param axis Axis index
  \return Geometry for the scale
  \sa activate(), invalidate()
*/
Bryant's avatar
Bryant committed
QRectF QwtPlotLayout::scaleRect( int axis ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( axis < 0 || axis >= QwtPlot::axisCnt )
    {
        static QRectF dummyRect;
pixhawk's avatar
pixhawk committed
        return dummyRect;
    }
    return d_data->scaleRect[axis];
}

Bryant's avatar
Bryant committed
/*!
  \brief Set the geometry for the canvas

  This method is intended to be used from derived layouts
  overloading activate()

  \sa canvasRect(), activate()
 */
void QwtPlotLayout::setCanvasRect( const QRectF &rect )
{
    d_data->canvasRect = rect;
}

pixhawk's avatar
pixhawk committed
/*!
  \return Geometry for the canvas
  \sa activate(), invalidate()
*/
Bryant's avatar
Bryant committed
QRectF QwtPlotLayout::canvasRect() const
pixhawk's avatar
pixhawk committed
{
    return d_data->canvasRect;
}

/*!
  Invalidate the geometry of all components.
pixhawk's avatar
pixhawk committed
  \sa activate()
*/
void QwtPlotLayout::invalidate()
{
Bryant's avatar
Bryant committed
    d_data->titleRect = d_data->footerRect
        = d_data->legendRect = d_data->canvasRect = QRect();

    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
pixhawk's avatar
pixhawk committed
        d_data->scaleRect[axis] = QRect();
}

Bryant's avatar
Bryant committed
  \return Minimum size hint
  \param plot Plot widget

pixhawk's avatar
pixhawk committed
  \sa QwtPlot::minimumSizeHint()
*/

Bryant's avatar
Bryant committed
QSize QwtPlotLayout::minimumSizeHint( const QwtPlot *plot ) const
pixhawk's avatar
pixhawk committed
{
    class ScaleData
    {
    public:
Bryant's avatar
Bryant committed
        ScaleData()
        {
pixhawk's avatar
pixhawk committed
            w = h = minLeft = minRight = tickOffset = 0;
        }

        int w;
        int h;
        int minLeft;
        int minRight;
        int tickOffset;
    } scaleData[QwtPlot::axisCnt];

    int canvasBorder[QwtPlot::axisCnt];

Bryant's avatar
Bryant committed
    int fw;
    plot->canvas()->getContentsMargins( &fw, NULL, NULL, NULL );

pixhawk's avatar
pixhawk committed
    int axis;
Bryant's avatar
Bryant committed
    for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
    {
        if ( plot->axisEnabled( axis ) )
        {
            const QwtScaleWidget *scl = plot->axisWidget( axis );
pixhawk's avatar
pixhawk committed
            ScaleData &sd = scaleData[axis];

            const QSize hint = scl->minimumSizeHint();
            sd.w = hint.width();
            sd.h = hint.height();
Bryant's avatar
Bryant committed
            scl->getBorderDistHint( sd.minLeft, sd.minRight );
pixhawk's avatar
pixhawk committed
            sd.tickOffset = scl->margin();
Bryant's avatar
Bryant committed
            if ( scl->scaleDraw()->hasComponent( QwtAbstractScaleDraw::Ticks ) )
                sd.tickOffset += qCeil( scl->scaleDraw()->maxTickLength() );
pixhawk's avatar
pixhawk committed
        }

Bryant's avatar
Bryant committed
        canvasBorder[axis] = fw + d_data->canvasMargin[axis] + 1;
pixhawk's avatar
pixhawk committed
    }


Bryant's avatar
Bryant committed
    for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
    {
pixhawk's avatar
pixhawk committed
        ScaleData &sd = scaleData[axis];
Bryant's avatar
Bryant committed
        if ( sd.w && ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) )
        {
            if ( ( sd.minLeft > canvasBorder[QwtPlot::yLeft] )
                && scaleData[QwtPlot::yLeft].w )
            {
pixhawk's avatar
pixhawk committed
                int shiftLeft = sd.minLeft - canvasBorder[QwtPlot::yLeft];
                if ( shiftLeft > scaleData[QwtPlot::yLeft].w )
                    shiftLeft = scaleData[QwtPlot::yLeft].w;

                sd.w -= shiftLeft;
            }
Bryant's avatar
Bryant committed
            if ( ( sd.minRight > canvasBorder[QwtPlot::yRight] )
                && scaleData[QwtPlot::yRight].w )
            {
pixhawk's avatar
pixhawk committed
                int shiftRight = sd.minRight - canvasBorder[QwtPlot::yRight];
                if ( shiftRight > scaleData[QwtPlot::yRight].w )
                    shiftRight = scaleData[QwtPlot::yRight].w;

                sd.w -= shiftRight;
            }
        }

Bryant's avatar
Bryant committed
        if ( sd.h && ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight ) )
        {
            if ( ( sd.minLeft > canvasBorder[QwtPlot::xBottom] ) &&
                scaleData[QwtPlot::xBottom].h )
            {
pixhawk's avatar
pixhawk committed
                int shiftBottom = sd.minLeft - canvasBorder[QwtPlot::xBottom];
                if ( shiftBottom > scaleData[QwtPlot::xBottom].tickOffset )
                    shiftBottom = scaleData[QwtPlot::xBottom].tickOffset;

                sd.h -= shiftBottom;
            }
Bryant's avatar
Bryant committed
            if ( ( sd.minLeft > canvasBorder[QwtPlot::xTop] ) &&
                scaleData[QwtPlot::xTop].h )
            {
pixhawk's avatar
pixhawk committed
                int shiftTop = sd.minRight - canvasBorder[QwtPlot::xTop];
                if ( shiftTop > scaleData[QwtPlot::xTop].tickOffset )
                    shiftTop = scaleData[QwtPlot::xTop].tickOffset;

                sd.h -= shiftTop;
            }
        }
    }

Bryant's avatar
Bryant committed
    const QWidget *canvas = plot->canvas();

    int left, top, right, bottom;
    canvas->getContentsMargins( &left, &top, &right, &bottom );

pixhawk's avatar
pixhawk committed
    const QSize minCanvasSize = canvas->minimumSize();

    int w = scaleData[QwtPlot::yLeft].w + scaleData[QwtPlot::yRight].w;
Bryant's avatar
Bryant committed
    int cw = qMax( scaleData[QwtPlot::xBottom].w, scaleData[QwtPlot::xTop].w )
        + left + 1 + right + 1;
    w += qMax( cw, minCanvasSize.width() );
pixhawk's avatar
pixhawk committed

    int h = scaleData[QwtPlot::xBottom].h + scaleData[QwtPlot::xTop].h;
Bryant's avatar
Bryant committed
    int ch = qMax( scaleData[QwtPlot::yLeft].h, scaleData[QwtPlot::yRight].h )
        + top + 1 + bottom + 1;
    h += qMax( ch, minCanvasSize.height() );

    const QwtTextLabel *labels[2];
    labels[0] = plot->titleLabel();
    labels[1] = plot->footerLabel();
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    for ( int i = 0; i < 2; i++ )
    {
        const QwtTextLabel *label   = labels[i];
        if ( label && !label->text().isEmpty() )
        {
            // If only QwtPlot::yLeft or QwtPlot::yRight is showing,
            // we center on the plot canvas.
            const bool centerOnCanvas = !( plot->axisEnabled( QwtPlot::yLeft )
                && plot->axisEnabled( QwtPlot::yRight ) );

            int labelW = w;
            if ( centerOnCanvas )
            {
                labelW -= scaleData[QwtPlot::yLeft].w
                    + scaleData[QwtPlot::yRight].w;
pixhawk's avatar
pixhawk committed
            }

Bryant's avatar
Bryant committed
            int labelH = label->heightForWidth( labelW );
            if ( labelH > labelW ) // Compensate for a long title
            {
                w = labelW = labelH;
                if ( centerOnCanvas )
                {
                    w += scaleData[QwtPlot::yLeft].w
                        + scaleData[QwtPlot::yRight].w;
                }

                labelH = label->heightForWidth( labelW );
            }
            h += labelH + d_data->spacing;
pixhawk's avatar
pixhawk committed
        }
    }

    // Compute the legend contribution

Bryant's avatar
Bryant committed
    const QwtAbstractLegend *legend = plot->legend();
    if ( legend && !legend->isEmpty() )
    {
        if ( d_data->legendPos == QwtPlot::LeftLegend
Bryant's avatar
Bryant committed
            || d_data->legendPos == QwtPlot::RightLegend )
        {
pixhawk's avatar
pixhawk committed
            int legendW = legend->sizeHint().width();
Bryant's avatar
Bryant committed
            int legendH = legend->heightForWidth( legendW );
pixhawk's avatar
pixhawk committed

            if ( legend->frameWidth() > 0 )
                w += d_data->spacing;

            if ( legendH > h )
Bryant's avatar
Bryant committed
                legendW += legend->scrollExtent( Qt::Horizontal );
pixhawk's avatar
pixhawk committed

            if ( d_data->legendRatio < 1.0 )
Bryant's avatar
Bryant committed
                legendW = qMin( legendW, int( w / ( 1.0 - d_data->legendRatio ) ) );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
            w += legendW + d_data->spacing;
        }
        else // QwtPlot::Top, QwtPlot::Bottom
        {
            int legendW = qMin( legend->sizeHint().width(), w );
            int legendH = legend->heightForWidth( legendW );
pixhawk's avatar
pixhawk committed

            if ( legend->frameWidth() > 0 )
                h += d_data->spacing;

            if ( d_data->legendRatio < 1.0 )
Bryant's avatar
Bryant committed
                legendH = qMin( legendH, int( h / ( 1.0 - d_data->legendRatio ) ) );
Bryant's avatar
Bryant committed
            h += legendH + d_data->spacing;
pixhawk's avatar
pixhawk committed
        }
    }

    return QSize( w, h );
}

/*!
  Find the geometry for the legend
Bryant's avatar
Bryant committed

pixhawk's avatar
pixhawk committed
  \param options Options how to layout the legend
  \param rect Rectangle where to place the legend
Bryant's avatar
Bryant committed

pixhawk's avatar
pixhawk committed
  \return Geometry for the legend
Bryant's avatar
Bryant committed
  \sa Options
pixhawk's avatar
pixhawk committed
*/

Bryant's avatar
Bryant committed
QRectF QwtPlotLayout::layoutLegend( Options options,
    const QRectF &rect ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    const QSize hint( d_data->layoutData.legend.hint );
pixhawk's avatar
pixhawk committed

    int dim;
    if ( d_data->legendPos == QwtPlot::LeftLegend
Bryant's avatar
Bryant committed
        || d_data->legendPos == QwtPlot::RightLegend )
    {
pixhawk's avatar
pixhawk committed
        // We don't allow vertical legends to take more than
        // half of the available space.

Bryant's avatar
Bryant committed
        dim = qMin( hint.width(), int( rect.width() * d_data->legendRatio ) );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        if ( !( options & IgnoreScrollbars ) )
        {
            if ( hint.height() > rect.height() )
            {
pixhawk's avatar
pixhawk committed
                // The legend will need additional
                // space for the vertical scrollbar.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
                dim += d_data->layoutData.legend.hScrollExtent;
pixhawk's avatar
pixhawk committed
            }
        }
Bryant's avatar
Bryant committed
    }
    else
    {
        dim = qMin( hint.height(), int( rect.height() * d_data->legendRatio ) );
        dim = qMax( dim, d_data->layoutData.legend.vScrollExtent );
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    QRectF legendRect = rect;
    switch ( d_data->legendPos )
    {
        case QwtPlot::LeftLegend:
            legendRect.setWidth( dim );
            break;
        case QwtPlot::RightLegend:
            legendRect.setX( rect.right() - dim );
            legendRect.setWidth( dim );
            break;
        case QwtPlot::TopLegend:
            legendRect.setHeight( dim );
            break;
        case QwtPlot::BottomLegend:
            legendRect.setY( rect.bottom() - dim );
            legendRect.setHeight( dim );
            break;
pixhawk's avatar
pixhawk committed
    }

    return legendRect;
}

/*!
  Align the legend to the canvas
Bryant's avatar
Bryant committed

pixhawk's avatar
pixhawk committed
  \param canvasRect Geometry of the canvas
  \param legendRect Maximum geometry for the legend
Bryant's avatar
Bryant committed

pixhawk's avatar
pixhawk committed
  \return Geometry for the aligned legend
*/
Bryant's avatar
Bryant committed
QRectF QwtPlotLayout::alignLegend( const QRectF &canvasRect,
    const QRectF &legendRect ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    QRectF alignedRect = legendRect;
pixhawk's avatar
pixhawk committed

    if ( d_data->legendPos == QwtPlot::BottomLegend
Bryant's avatar
Bryant committed
        || d_data->legendPos == QwtPlot::TopLegend )
    {
        if ( d_data->layoutData.legend.hint.width() < canvasRect.width() )
        {
            alignedRect.setX( canvasRect.x() );
            alignedRect.setWidth( canvasRect.width() );
pixhawk's avatar
pixhawk committed
        }
Bryant's avatar
Bryant committed
    }
    else
    {
        if ( d_data->layoutData.legend.hint.height() < canvasRect.height() )
        {
            alignedRect.setY( canvasRect.y() );
            alignedRect.setHeight( canvasRect.height() );
pixhawk's avatar
pixhawk committed
        }
    }

    return alignedRect;
}

/*!
  Expand all line breaks in text labels, and calculate the height
  of their widgets in orientation of the text.

  \param options Options how to layout the legend
Bryant's avatar
Bryant committed
  \param rect Bounding rectangle for title, footer, axes and canvas.
pixhawk's avatar
pixhawk committed
  \param dimTitle Expanded height of the title widget
Bryant's avatar
Bryant committed
  \param dimFooter Expanded height of the footer widget
pixhawk's avatar
pixhawk committed
  \param dimAxis Expanded heights of the axis in axis orientation.
Bryant's avatar
Bryant committed

  \sa Options
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtPlotLayout::expandLineBreaks( Options options, const QRectF &rect,
    int &dimTitle, int &dimFooter, int dimAxis[QwtPlot::axisCnt] ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    dimTitle = dimFooter = 0;
pixhawk's avatar
pixhawk committed
    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
        dimAxis[axis] = 0;

    int backboneOffset[QwtPlot::axisCnt];
Bryant's avatar
Bryant committed
    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
    {
pixhawk's avatar
pixhawk committed
        backboneOffset[axis] = 0;
Bryant's avatar
Bryant committed
        if ( !( options & IgnoreFrames ) )
            backboneOffset[axis] += d_data->layoutData.canvas.contentsMargins[ axis ];

        if ( !d_data->alignCanvasToScales[axis] )
pixhawk's avatar
pixhawk committed
            backboneOffset[axis] += d_data->canvasMargin[axis];
    }

    bool done = false;
Bryant's avatar
Bryant committed
    while ( !done )
    {
pixhawk's avatar
pixhawk committed
        done = true;

        // the size for the 4 axis depend on each other. Expanding
        // the height of a horizontal axis will shrink the height
        // for the vertical axis, shrinking the height of a vertical
        // axis will result in a line break what will expand the
        // width and results in shrinking the width of a horizontal
        // axis what might result in a line break of a horizontal
        // axis ... . So we loop as long until no size changes.

Bryant's avatar
Bryant committed
        if ( !( ( options & IgnoreTitle ) ||
            d_data->layoutData.title.text.isEmpty() ) )
        {
            double w = rect.width();
pixhawk's avatar
pixhawk committed

            if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled
Bryant's avatar
Bryant committed
                != d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
            {
pixhawk's avatar
pixhawk committed
                // center to the canvas
                w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight];
pixhawk's avatar
pixhawk committed
            }

Bryant's avatar
Bryant committed
            int d = qCeil( d_data->layoutData.title.text.heightForWidth( w ) );
            if ( !( options & IgnoreFrames ) )
pixhawk's avatar
pixhawk committed
                d += 2 * d_data->layoutData.title.frameWidth;

Bryant's avatar
Bryant committed
            if ( d > dimTitle )
            {
pixhawk's avatar
pixhawk committed
                dimTitle = d;
                done = false;
            }
        }

Bryant's avatar
Bryant committed
        if ( !( ( options & IgnoreFooter ) ||
            d_data->layoutData.footer.text.isEmpty() ) )
        {
            double w = rect.width();

            if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled
                != d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
            {
                // center to the canvas
                w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight];
            }

            int d = qCeil( d_data->layoutData.footer.text.heightForWidth( w ) );
            if ( !( options & IgnoreFrames ) )
                d += 2 * d_data->layoutData.footer.frameWidth;

            if ( d > dimFooter )
            {
                dimFooter = d;
                done = false;
            }
        }

        for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
        {
            const struct LayoutData::t_scaleData &scaleData =
Bryant's avatar
Bryant committed
                d_data->layoutData.scale[axis];
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
            if ( scaleData.isEnabled )
            {
                double length;
                if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom )
                {
                    length = rect.width() - dimAxis[QwtPlot::yLeft]
Bryant's avatar
Bryant committed
                        - dimAxis[QwtPlot::yRight];
pixhawk's avatar
pixhawk committed
                    length -= scaleData.start + scaleData.end;

                    if ( dimAxis[QwtPlot::yRight] > 0 )
                        length -= 1;

Bryant's avatar
Bryant committed
                    length += qMin( dimAxis[QwtPlot::yLeft],
                        scaleData.start - backboneOffset[QwtPlot::yLeft] );
                    length += qMin( dimAxis[QwtPlot::yRight],
                        scaleData.end - backboneOffset[QwtPlot::yRight] );
                }
                else // QwtPlot::yLeft, QwtPlot::yRight
                {
                    length = rect.height() - dimAxis[QwtPlot::xTop]
Bryant's avatar
Bryant committed
                        - dimAxis[QwtPlot::xBottom];
pixhawk's avatar
pixhawk committed
                    length -= scaleData.start + scaleData.end;
                    length -= 1;

                    if ( dimAxis[QwtPlot::xBottom] <= 0 )
                        length -= 1;
                    if ( dimAxis[QwtPlot::xTop] <= 0 )
                        length -= 1;

Bryant's avatar
Bryant committed
                    if ( dimAxis[QwtPlot::xBottom] > 0 )
                    {
                        length += qMin(
                            d_data->layoutData.scale[QwtPlot::xBottom].tickOffset,
                            double( scaleData.start - backboneOffset[QwtPlot::xBottom] ) );
pixhawk's avatar
pixhawk committed
                    }
Bryant's avatar
Bryant committed
                    if ( dimAxis[QwtPlot::xTop] > 0 )
                    {
                        length += qMin(
                            d_data->layoutData.scale[QwtPlot::xTop].tickOffset,
                            double( scaleData.end - backboneOffset[QwtPlot::xTop] ) );
pixhawk's avatar
pixhawk committed
                    }

                    if ( dimTitle > 0 )
                        length -= dimTitle + d_data->spacing;
                }

                int d = scaleData.dimWithoutTitle;
Bryant's avatar
Bryant committed
                if ( !scaleData.scaleWidget->title().isEmpty() )
                {
                    d += scaleData.scaleWidget->titleHeightForWidth( qFloor( length ) );
pixhawk's avatar
pixhawk committed
                }


Bryant's avatar
Bryant committed
                if ( d > dimAxis[axis] )
                {
pixhawk's avatar
pixhawk committed
                    dimAxis[axis] = d;
                    done = false;
                }
            }
        }
    }
}

/*!
  Align the ticks of the axis to the canvas borders using
  the empty corners.