qwt_plot_layout.cpp
Go to the documentation of this file.
00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
00002  * Qwt Widget Library
00003  * Copyright (C) 1997   Josef Wilgen
00004  * Copyright (C) 2002   Uwe Rathmann
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the Qwt License, Version 1.0
00008  *****************************************************************************/
00009 
00010 #include "qwt_plot_layout.h"
00011 #include "qwt_text.h"
00012 #include "qwt_text_label.h"
00013 #include "qwt_scale_widget.h"
00014 #include "qwt_abstract_legend.h"
00015 #include <qscrollbar.h>
00016 #include <qmath.h>
00017 
00018 class QwtPlotLayout::LayoutData
00019 {
00020 public:
00021     void init( const QwtPlot *, const QRectF &rect );
00022 
00023     struct t_legendData
00024     {
00025         int frameWidth;
00026         int hScrollExtent;
00027         int vScrollExtent;
00028         QSize hint;
00029     } legend;
00030 
00031     struct t_titleData
00032     {
00033         QwtText text;
00034         int frameWidth;
00035     } title;
00036 
00037     struct t_footerData
00038     {
00039         QwtText text;
00040         int frameWidth;
00041     } footer;
00042 
00043     struct t_scaleData
00044     {
00045         bool isEnabled;
00046         const QwtScaleWidget *scaleWidget;
00047         QFont scaleFont;
00048         int start;
00049         int end;
00050         int baseLineOffset;
00051         double tickOffset;
00052         int dimWithoutTitle;
00053     } scale[QwtPlot::axisCnt];
00054 
00055     struct t_canvasData
00056     {
00057         int contentsMargins[ QwtPlot::axisCnt ];
00058 
00059     } canvas;
00060 };
00061 
00062 /*
00063   Extract all layout relevant data from the plot components
00064 */
00065 void QwtPlotLayout::LayoutData::init( const QwtPlot *plot, const QRectF &rect )
00066 {
00067     // legend
00068 
00069     if ( plot->legend() )
00070     {
00071         legend.frameWidth = plot->legend()->frameWidth();
00072         legend.hScrollExtent =
00073             plot->legend()->scrollExtent( Qt::Horizontal );
00074         legend.vScrollExtent =
00075             plot->legend()->scrollExtent( Qt::Vertical );
00076 
00077         const QSize hint = plot->legend()->sizeHint();
00078 
00079         const int w = qMin( hint.width(), qFloor( rect.width() ) );
00080 
00081         int h = plot->legend()->heightForWidth( w );
00082         if ( h <= 0 )
00083             h = hint.height();
00084 
00085         legend.hint = QSize( w, h );
00086     }
00087 
00088     // title
00089 
00090     title.frameWidth = 0;
00091     title.text = QwtText();
00092 
00093     if ( plot->titleLabel() )
00094     {
00095         const QwtTextLabel *label = plot->titleLabel();
00096         title.text = label->text();
00097         if ( !( title.text.testPaintAttribute( QwtText::PaintUsingTextFont ) ) )
00098             title.text.setFont( label->font() );
00099 
00100         title.frameWidth = plot->titleLabel()->frameWidth();
00101     }
00102 
00103     // footer
00104 
00105     footer.frameWidth = 0;
00106     footer.text = QwtText();
00107 
00108     if ( plot->footerLabel() )
00109     {
00110         const QwtTextLabel *label = plot->footerLabel();
00111         footer.text = label->text();
00112         if ( !( footer.text.testPaintAttribute( QwtText::PaintUsingTextFont ) ) )
00113             footer.text.setFont( label->font() );
00114 
00115         footer.frameWidth = plot->footerLabel()->frameWidth();
00116     }
00117 
00118     // scales
00119 
00120     for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
00121     {
00122         if ( plot->axisEnabled( axis ) )
00123         {
00124             const QwtScaleWidget *scaleWidget = plot->axisWidget( axis );
00125 
00126             scale[axis].isEnabled = true;
00127 
00128             scale[axis].scaleWidget = scaleWidget;
00129 
00130             scale[axis].scaleFont = scaleWidget->font();
00131 
00132             scale[axis].start = scaleWidget->startBorderDist();
00133             scale[axis].end = scaleWidget->endBorderDist();
00134 
00135             scale[axis].baseLineOffset = scaleWidget->margin();
00136             scale[axis].tickOffset = scaleWidget->margin();
00137             if ( scaleWidget->scaleDraw()->hasComponent(
00138                 QwtAbstractScaleDraw::Ticks ) )
00139             {
00140                 scale[axis].tickOffset +=
00141                     scaleWidget->scaleDraw()->maxTickLength();
00142             }
00143 
00144             scale[axis].dimWithoutTitle = scaleWidget->dimForLength(
00145                 QWIDGETSIZE_MAX, scale[axis].scaleFont );
00146 
00147             if ( !scaleWidget->title().isEmpty() )
00148             {
00149                 scale[axis].dimWithoutTitle -=
00150                     scaleWidget->titleHeightForWidth( QWIDGETSIZE_MAX );
00151             }
00152         }
00153         else
00154         {
00155             scale[axis].isEnabled = false;
00156             scale[axis].start = 0;
00157             scale[axis].end = 0;
00158             scale[axis].baseLineOffset = 0;
00159             scale[axis].tickOffset = 0.0;
00160             scale[axis].dimWithoutTitle = 0;
00161         }
00162     }
00163 
00164     // canvas
00165 
00166     plot->canvas()->getContentsMargins( 
00167         &canvas.contentsMargins[ QwtPlot::yLeft ], 
00168         &canvas.contentsMargins[ QwtPlot::xTop ],
00169         &canvas.contentsMargins[ QwtPlot::yRight ],
00170         &canvas.contentsMargins[ QwtPlot::xBottom ] );
00171 }
00172 
00173 class QwtPlotLayout::PrivateData
00174 {
00175 public:
00176     PrivateData():
00177         spacing( 5 )
00178     {
00179     }
00180 
00181     QRectF titleRect;
00182     QRectF footerRect;
00183     QRectF legendRect;
00184     QRectF scaleRect[QwtPlot::axisCnt];
00185     QRectF canvasRect;
00186 
00187     QwtPlotLayout::LayoutData layoutData;
00188 
00189     QwtPlot::LegendPosition legendPos;
00190     double legendRatio;
00191     unsigned int spacing;
00192     unsigned int canvasMargin[QwtPlot::axisCnt];
00193     bool alignCanvasToScales[QwtPlot::axisCnt];
00194 };
00195 
00200 QwtPlotLayout::QwtPlotLayout()
00201 {
00202     d_data = new PrivateData;
00203 
00204     setLegendPosition( QwtPlot::BottomLegend );
00205     setCanvasMargin( 4 );
00206     setAlignCanvasToScales( false );
00207 
00208     invalidate();
00209 }
00210 
00212 QwtPlotLayout::~QwtPlotLayout()
00213 {
00214     delete d_data;
00215 }
00216 
00230 void QwtPlotLayout::setCanvasMargin( int margin, int axis )
00231 {
00232     if ( margin < -1 )
00233         margin = -1;
00234 
00235     if ( axis == -1 )
00236     {
00237         for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
00238             d_data->canvasMargin[axis] = margin;
00239     }
00240     else if ( axis >= 0 && axis < QwtPlot::axisCnt )
00241         d_data->canvasMargin[axis] = margin;
00242 }
00243 
00249 int QwtPlotLayout::canvasMargin( int axisId ) const
00250 {
00251     if ( axisId < 0 || axisId >= QwtPlot::axisCnt )
00252         return 0;
00253 
00254     return d_data->canvasMargin[axisId];
00255 }
00256 
00263 void QwtPlotLayout::setAlignCanvasToScales( bool on )
00264 {
00265     for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
00266         d_data->alignCanvasToScales[axis] = on;
00267 }
00268 
00286 void QwtPlotLayout::setAlignCanvasToScale( int axisId, bool on )
00287 {
00288     if ( axisId >= 0 && axisId < QwtPlot::axisCnt )
00289         d_data->alignCanvasToScales[axisId] = on;
00290 }
00291 
00301 bool QwtPlotLayout::alignCanvasToScale( int axisId ) const
00302 {
00303     if ( axisId < 0 || axisId >= QwtPlot::axisCnt )
00304         return false;
00305 
00306     return d_data->alignCanvasToScales[ axisId ];
00307 }
00308 
00316 void QwtPlotLayout::setSpacing( int spacing )
00317 {
00318     d_data->spacing = qMax( 0, spacing );
00319 }
00320 
00325 int QwtPlotLayout::spacing() const
00326 {
00327     return d_data->spacing;
00328 }
00329 
00343 void QwtPlotLayout::setLegendPosition( QwtPlot::LegendPosition pos, double ratio )
00344 {
00345     if ( ratio > 1.0 )
00346         ratio = 1.0;
00347 
00348     switch ( pos )
00349     {
00350         case QwtPlot::TopLegend:
00351         case QwtPlot::BottomLegend:
00352             if ( ratio <= 0.0 )
00353                 ratio = 0.33;
00354             d_data->legendRatio = ratio;
00355             d_data->legendPos = pos;
00356             break;
00357         case QwtPlot::LeftLegend:
00358         case QwtPlot::RightLegend:
00359             if ( ratio <= 0.0 )
00360                 ratio = 0.5;
00361             d_data->legendRatio = ratio;
00362             d_data->legendPos = pos;
00363             break;
00364         default:
00365             break;
00366     }
00367 }
00368 
00377 void QwtPlotLayout::setLegendPosition( QwtPlot::LegendPosition pos )
00378 {
00379     setLegendPosition( pos, 0.0 );
00380 }
00381 
00387 QwtPlot::LegendPosition QwtPlotLayout::legendPosition() const
00388 {
00389     return d_data->legendPos;
00390 }
00391 
00401 void QwtPlotLayout::setLegendRatio( double ratio )
00402 {
00403     setLegendPosition( legendPosition(), ratio );
00404 }
00405 
00410 double QwtPlotLayout::legendRatio() const
00411 {
00412     return d_data->legendRatio;
00413 }
00414 
00423 void QwtPlotLayout::setTitleRect( const QRectF &rect )
00424 {
00425     d_data->titleRect = rect;
00426 }
00427 
00432 QRectF QwtPlotLayout::titleRect() const
00433 {
00434     return d_data->titleRect;
00435 }
00436 
00445 void QwtPlotLayout::setFooterRect( const QRectF &rect )
00446 {
00447     d_data->footerRect = rect;
00448 }
00449 
00454 QRectF QwtPlotLayout::footerRect() const
00455 {
00456     return d_data->footerRect;
00457 }
00458 
00469 void QwtPlotLayout::setLegendRect( const QRectF &rect )
00470 {
00471     d_data->legendRect = rect;
00472 }
00473 
00478 QRectF QwtPlotLayout::legendRect() const
00479 {
00480     return d_data->legendRect;
00481 }
00482 
00494 void QwtPlotLayout::setScaleRect( int axis, const QRectF &rect )
00495 {
00496     if ( axis >= 0 && axis < QwtPlot::axisCnt )
00497         d_data->scaleRect[axis] = rect;
00498 }
00499 
00505 QRectF QwtPlotLayout::scaleRect( int axis ) const
00506 {
00507     if ( axis < 0 || axis >= QwtPlot::axisCnt )
00508     {
00509         static QRectF dummyRect;
00510         return dummyRect;
00511     }
00512     return d_data->scaleRect[axis];
00513 }
00514 
00523 void QwtPlotLayout::setCanvasRect( const QRectF &rect )
00524 {
00525     d_data->canvasRect = rect;
00526 }
00527 
00532 QRectF QwtPlotLayout::canvasRect() const
00533 {
00534     return d_data->canvasRect;
00535 }
00536 
00541 void QwtPlotLayout::invalidate()
00542 {
00543     d_data->titleRect = d_data->footerRect
00544         = d_data->legendRect = d_data->canvasRect = QRect();
00545 
00546     for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
00547         d_data->scaleRect[axis] = QRect();
00548 }
00549 
00557 QSize QwtPlotLayout::minimumSizeHint( const QwtPlot *plot ) const
00558 {
00559     class ScaleData
00560     {
00561     public:
00562         ScaleData()
00563         {
00564             w = h = minLeft = minRight = tickOffset = 0;
00565         }
00566 
00567         int w;
00568         int h;
00569         int minLeft;
00570         int minRight;
00571         int tickOffset;
00572     } scaleData[QwtPlot::axisCnt];
00573 
00574     int canvasBorder[QwtPlot::axisCnt];
00575 
00576     int fw;
00577     plot->canvas()->getContentsMargins( &fw, NULL, NULL, NULL );
00578 
00579     int axis;
00580     for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
00581     {
00582         if ( plot->axisEnabled( axis ) )
00583         {
00584             const QwtScaleWidget *scl = plot->axisWidget( axis );
00585             ScaleData &sd = scaleData[axis];
00586 
00587             const QSize hint = scl->minimumSizeHint();
00588             sd.w = hint.width();
00589             sd.h = hint.height();
00590             scl->getBorderDistHint( sd.minLeft, sd.minRight );
00591             sd.tickOffset = scl->margin();
00592             if ( scl->scaleDraw()->hasComponent( QwtAbstractScaleDraw::Ticks ) )
00593                 sd.tickOffset += qCeil( scl->scaleDraw()->maxTickLength() );
00594         }
00595 
00596         canvasBorder[axis] = fw + d_data->canvasMargin[axis] + 1;
00597     }
00598 
00599 
00600     for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
00601     {
00602         ScaleData &sd = scaleData[axis];
00603         if ( sd.w && ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) )
00604         {
00605             if ( ( sd.minLeft > canvasBorder[QwtPlot::yLeft] )
00606                 && scaleData[QwtPlot::yLeft].w )
00607             {
00608                 int shiftLeft = sd.minLeft - canvasBorder[QwtPlot::yLeft];
00609                 if ( shiftLeft > scaleData[QwtPlot::yLeft].w )
00610                     shiftLeft = scaleData[QwtPlot::yLeft].w;
00611 
00612                 sd.w -= shiftLeft;
00613             }
00614             if ( ( sd.minRight > canvasBorder[QwtPlot::yRight] )
00615                 && scaleData[QwtPlot::yRight].w )
00616             {
00617                 int shiftRight = sd.minRight - canvasBorder[QwtPlot::yRight];
00618                 if ( shiftRight > scaleData[QwtPlot::yRight].w )
00619                     shiftRight = scaleData[QwtPlot::yRight].w;
00620 
00621                 sd.w -= shiftRight;
00622             }
00623         }
00624 
00625         if ( sd.h && ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight ) )
00626         {
00627             if ( ( sd.minLeft > canvasBorder[QwtPlot::xBottom] ) &&
00628                 scaleData[QwtPlot::xBottom].h )
00629             {
00630                 int shiftBottom = sd.minLeft - canvasBorder[QwtPlot::xBottom];
00631                 if ( shiftBottom > scaleData[QwtPlot::xBottom].tickOffset )
00632                     shiftBottom = scaleData[QwtPlot::xBottom].tickOffset;
00633 
00634                 sd.h -= shiftBottom;
00635             }
00636             if ( ( sd.minLeft > canvasBorder[QwtPlot::xTop] ) &&
00637                 scaleData[QwtPlot::xTop].h )
00638             {
00639                 int shiftTop = sd.minRight - canvasBorder[QwtPlot::xTop];
00640                 if ( shiftTop > scaleData[QwtPlot::xTop].tickOffset )
00641                     shiftTop = scaleData[QwtPlot::xTop].tickOffset;
00642 
00643                 sd.h -= shiftTop;
00644             }
00645         }
00646     }
00647 
00648     const QWidget *canvas = plot->canvas();
00649 
00650     int left, top, right, bottom;
00651     canvas->getContentsMargins( &left, &top, &right, &bottom );
00652 
00653     const QSize minCanvasSize = canvas->minimumSize();
00654 
00655     int w = scaleData[QwtPlot::yLeft].w + scaleData[QwtPlot::yRight].w;
00656     int cw = qMax( scaleData[QwtPlot::xBottom].w, scaleData[QwtPlot::xTop].w )
00657         + left + 1 + right + 1;
00658     w += qMax( cw, minCanvasSize.width() );
00659 
00660     int h = scaleData[QwtPlot::xBottom].h + scaleData[QwtPlot::xTop].h;
00661     int ch = qMax( scaleData[QwtPlot::yLeft].h, scaleData[QwtPlot::yRight].h )
00662         + top + 1 + bottom + 1;
00663     h += qMax( ch, minCanvasSize.height() );
00664 
00665     const QwtTextLabel *labels[2];
00666     labels[0] = plot->titleLabel();
00667     labels[1] = plot->footerLabel();
00668 
00669     for ( int i = 0; i < 2; i++ )
00670     {
00671         const QwtTextLabel *label   = labels[i];
00672         if ( label && !label->text().isEmpty() )
00673         {
00674             // If only QwtPlot::yLeft or QwtPlot::yRight is showing,
00675             // we center on the plot canvas.
00676             const bool centerOnCanvas = !( plot->axisEnabled( QwtPlot::yLeft )
00677                 && plot->axisEnabled( QwtPlot::yRight ) );
00678 
00679             int labelW = w;
00680             if ( centerOnCanvas )
00681             {
00682                 labelW -= scaleData[QwtPlot::yLeft].w
00683                     + scaleData[QwtPlot::yRight].w;
00684             }
00685 
00686             int labelH = label->heightForWidth( labelW );
00687             if ( labelH > labelW ) // Compensate for a long title
00688             {
00689                 w = labelW = labelH;
00690                 if ( centerOnCanvas )
00691                 {
00692                     w += scaleData[QwtPlot::yLeft].w
00693                         + scaleData[QwtPlot::yRight].w;
00694                 }
00695 
00696                 labelH = label->heightForWidth( labelW );
00697             }
00698             h += labelH + d_data->spacing;
00699         }
00700     }
00701 
00702     // Compute the legend contribution
00703 
00704     const QwtAbstractLegend *legend = plot->legend();
00705     if ( legend && !legend->isEmpty() )
00706     {
00707         if ( d_data->legendPos == QwtPlot::LeftLegend
00708             || d_data->legendPos == QwtPlot::RightLegend )
00709         {
00710             int legendW = legend->sizeHint().width();
00711             int legendH = legend->heightForWidth( legendW );
00712 
00713             if ( legend->frameWidth() > 0 )
00714                 w += d_data->spacing;
00715 
00716             if ( legendH > h )
00717                 legendW += legend->scrollExtent( Qt::Horizontal );
00718 
00719             if ( d_data->legendRatio < 1.0 )
00720                 legendW = qMin( legendW, int( w / ( 1.0 - d_data->legendRatio ) ) );
00721 
00722             w += legendW + d_data->spacing;
00723         }
00724         else // QwtPlot::Top, QwtPlot::Bottom
00725         {
00726             int legendW = qMin( legend->sizeHint().width(), w );
00727             int legendH = legend->heightForWidth( legendW );
00728 
00729             if ( legend->frameWidth() > 0 )
00730                 h += d_data->spacing;
00731 
00732             if ( d_data->legendRatio < 1.0 )
00733                 legendH = qMin( legendH, int( h / ( 1.0 - d_data->legendRatio ) ) );
00734 
00735             h += legendH + d_data->spacing;
00736         }
00737     }
00738 
00739     return QSize( w, h );
00740 }
00741 
00752 QRectF QwtPlotLayout::layoutLegend( Options options,
00753     const QRectF &rect ) const
00754 {
00755     const QSize hint( d_data->layoutData.legend.hint );
00756 
00757     int dim;
00758     if ( d_data->legendPos == QwtPlot::LeftLegend
00759         || d_data->legendPos == QwtPlot::RightLegend )
00760     {
00761         // We don't allow vertical legends to take more than
00762         // half of the available space.
00763 
00764         dim = qMin( hint.width(), int( rect.width() * d_data->legendRatio ) );
00765 
00766         if ( !( options & IgnoreScrollbars ) )
00767         {
00768             if ( hint.height() > rect.height() )
00769             {
00770                 // The legend will need additional
00771                 // space for the vertical scrollbar.
00772 
00773                 dim += d_data->layoutData.legend.hScrollExtent;
00774             }
00775         }
00776     }
00777     else
00778     {
00779         dim = qMin( hint.height(), int( rect.height() * d_data->legendRatio ) );
00780         dim = qMax( dim, d_data->layoutData.legend.vScrollExtent );
00781     }
00782 
00783     QRectF legendRect = rect;
00784     switch ( d_data->legendPos )
00785     {
00786         case QwtPlot::LeftLegend:
00787             legendRect.setWidth( dim );
00788             break;
00789         case QwtPlot::RightLegend:
00790             legendRect.setX( rect.right() - dim );
00791             legendRect.setWidth( dim );
00792             break;
00793         case QwtPlot::TopLegend:
00794             legendRect.setHeight( dim );
00795             break;
00796         case QwtPlot::BottomLegend:
00797             legendRect.setY( rect.bottom() - dim );
00798             legendRect.setHeight( dim );
00799             break;
00800     }
00801 
00802     return legendRect;
00803 }
00804 
00813 QRectF QwtPlotLayout::alignLegend( const QRectF &canvasRect,
00814     const QRectF &legendRect ) const
00815 {
00816     QRectF alignedRect = legendRect;
00817 
00818     if ( d_data->legendPos == QwtPlot::BottomLegend
00819         || d_data->legendPos == QwtPlot::TopLegend )
00820     {
00821         if ( d_data->layoutData.legend.hint.width() < canvasRect.width() )
00822         {
00823             alignedRect.setX( canvasRect.x() );
00824             alignedRect.setWidth( canvasRect.width() );
00825         }
00826     }
00827     else
00828     {
00829         if ( d_data->layoutData.legend.hint.height() < canvasRect.height() )
00830         {
00831             alignedRect.setY( canvasRect.y() );
00832             alignedRect.setHeight( canvasRect.height() );
00833         }
00834     }
00835 
00836     return alignedRect;
00837 }
00838 
00851 void QwtPlotLayout::expandLineBreaks( Options options, const QRectF &rect,
00852     int &dimTitle, int &dimFooter, int dimAxis[QwtPlot::axisCnt] ) const
00853 {
00854     dimTitle = dimFooter = 0;
00855     for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
00856         dimAxis[axis] = 0;
00857 
00858     int backboneOffset[QwtPlot::axisCnt];
00859     for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
00860     {
00861         backboneOffset[axis] = 0;
00862         if ( !( options & IgnoreFrames ) )
00863             backboneOffset[axis] += d_data->layoutData.canvas.contentsMargins[ axis ];
00864 
00865         if ( !d_data->alignCanvasToScales[axis] )
00866             backboneOffset[axis] += d_data->canvasMargin[axis];
00867     }
00868 
00869     bool done = false;
00870     while ( !done )
00871     {
00872         done = true;
00873 
00874         // the size for the 4 axis depend on each other. Expanding
00875         // the height of a horizontal axis will shrink the height
00876         // for the vertical axis, shrinking the height of a vertical
00877         // axis will result in a line break what will expand the
00878         // width and results in shrinking the width of a horizontal
00879         // axis what might result in a line break of a horizontal
00880         // axis ... . So we loop as long until no size changes.
00881 
00882         if ( !( ( options & IgnoreTitle ) ||
00883             d_data->layoutData.title.text.isEmpty() ) )
00884         {
00885             double w = rect.width();
00886 
00887             if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled
00888                 != d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
00889             {
00890                 // center to the canvas
00891                 w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight];
00892             }
00893 
00894             int d = qCeil( d_data->layoutData.title.text.heightForWidth( w ) );
00895             if ( !( options & IgnoreFrames ) )
00896                 d += 2 * d_data->layoutData.title.frameWidth;
00897 
00898             if ( d > dimTitle )
00899             {
00900                 dimTitle = d;
00901                 done = false;
00902             }
00903         }
00904 
00905         if ( !( ( options & IgnoreFooter ) ||
00906             d_data->layoutData.footer.text.isEmpty() ) )
00907         {
00908             double w = rect.width();
00909 
00910             if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled
00911                 != d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
00912             {
00913                 // center to the canvas
00914                 w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight];
00915             }
00916 
00917             int d = qCeil( d_data->layoutData.footer.text.heightForWidth( w ) );
00918             if ( !( options & IgnoreFrames ) )
00919                 d += 2 * d_data->layoutData.footer.frameWidth;
00920 
00921             if ( d > dimFooter )
00922             {
00923                 dimFooter = d;
00924                 done = false;
00925             }
00926         }
00927 
00928         for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
00929         {
00930             const struct LayoutData::t_scaleData &scaleData =
00931                 d_data->layoutData.scale[axis];
00932 
00933             if ( scaleData.isEnabled )
00934             {
00935                 double length;
00936                 if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom )
00937                 {
00938                     length = rect.width() - dimAxis[QwtPlot::yLeft]
00939                         - dimAxis[QwtPlot::yRight];
00940                     length -= scaleData.start + scaleData.end;
00941 
00942                     if ( dimAxis[QwtPlot::yRight] > 0 )
00943                         length -= 1;
00944 
00945                     length += qMin( dimAxis[QwtPlot::yLeft],
00946                         scaleData.start - backboneOffset[QwtPlot::yLeft] );
00947                     length += qMin( dimAxis[QwtPlot::yRight],
00948                         scaleData.end - backboneOffset[QwtPlot::yRight] );
00949                 }
00950                 else // QwtPlot::yLeft, QwtPlot::yRight
00951                 {
00952                     length = rect.height() - dimAxis[QwtPlot::xTop]
00953                         - dimAxis[QwtPlot::xBottom];
00954                     length -= scaleData.start + scaleData.end;
00955                     length -= 1;
00956 
00957                     if ( dimAxis[QwtPlot::xBottom] <= 0 )
00958                         length -= 1;
00959                     if ( dimAxis[QwtPlot::xTop] <= 0 )
00960                         length -= 1;
00961 
00962                     if ( dimAxis[QwtPlot::xBottom] > 0 )
00963                     {
00964                         length += qMin(
00965                             d_data->layoutData.scale[QwtPlot::xBottom].tickOffset,
00966                             double( scaleData.start - backboneOffset[QwtPlot::xBottom] ) );
00967                     }
00968                     if ( dimAxis[QwtPlot::xTop] > 0 )
00969                     {
00970                         length += qMin(
00971                             d_data->layoutData.scale[QwtPlot::xTop].tickOffset,
00972                             double( scaleData.end - backboneOffset[QwtPlot::xTop] ) );
00973                     }
00974 
00975                     if ( dimTitle > 0 )
00976                         length -= dimTitle + d_data->spacing;
00977                 }
00978 
00979                 int d = scaleData.dimWithoutTitle;
00980                 if ( !scaleData.scaleWidget->title().isEmpty() )
00981                 {
00982                     d += scaleData.scaleWidget->titleHeightForWidth( qFloor( length ) );
00983                 }
00984 
00985 
00986                 if ( d > dimAxis[axis] )
00987                 {
00988                     dimAxis[axis] = d;
00989                     done = false;
00990                 }
00991             }
00992         }
00993     }
00994 }
00995 
01007 void QwtPlotLayout::alignScales( Options options,
01008     QRectF &canvasRect, QRectF scaleRect[QwtPlot::axisCnt] ) const
01009 {
01010     int backboneOffset[QwtPlot::axisCnt];
01011     for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
01012     {
01013         backboneOffset[axis] = 0;
01014 
01015         if ( !d_data->alignCanvasToScales[axis] )
01016         {
01017             backboneOffset[axis] += d_data->canvasMargin[axis];
01018         }
01019 
01020         if ( !( options & IgnoreFrames ) )
01021         {
01022             backboneOffset[axis] += 
01023                 d_data->layoutData.canvas.contentsMargins[axis];
01024         }
01025     }
01026 
01027     for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
01028     {
01029         if ( !scaleRect[axis].isValid() )
01030             continue;
01031 
01032         const int startDist = d_data->layoutData.scale[axis].start;
01033         const int endDist = d_data->layoutData.scale[axis].end;
01034 
01035         QRectF &axisRect = scaleRect[axis];
01036 
01037         if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom )
01038         {
01039             const QRectF &leftScaleRect = scaleRect[QwtPlot::yLeft];
01040             const int leftOffset =
01041                 backboneOffset[QwtPlot::yLeft] - startDist;
01042 
01043             if ( leftScaleRect.isValid() )
01044             {
01045                 const double dx = leftOffset + leftScaleRect.width();
01046                 if ( d_data->alignCanvasToScales[QwtPlot::yLeft] && dx < 0.0 )
01047                 {
01048                     /*
01049                       The axis needs more space than the width
01050                       of the left scale.
01051                      */
01052                     const double cLeft = canvasRect.left(); // qreal -> double
01053                     canvasRect.setLeft( qMax( cLeft, axisRect.left() - dx ) );
01054                 }
01055                 else
01056                 {
01057                     const double minLeft = leftScaleRect.left();
01058                     const double left = axisRect.left() + leftOffset;
01059                     axisRect.setLeft( qMax( left, minLeft ) );
01060                 }
01061             }
01062             else
01063             {
01064                 if ( d_data->alignCanvasToScales[QwtPlot::yLeft] && leftOffset < 0 )
01065                 {
01066                     canvasRect.setLeft( qMax( canvasRect.left(),
01067                         axisRect.left() - leftOffset ) );
01068                 }
01069                 else
01070                 {
01071                     if ( leftOffset > 0 )
01072                         axisRect.setLeft( axisRect.left() + leftOffset );
01073                 }
01074             }
01075 
01076             const QRectF &rightScaleRect = scaleRect[QwtPlot::yRight];
01077             const int rightOffset =
01078                 backboneOffset[QwtPlot::yRight] - endDist + 1;
01079 
01080             if ( rightScaleRect.isValid() )
01081             {
01082                 const double dx = rightOffset + rightScaleRect.width();
01083                 if ( d_data->alignCanvasToScales[QwtPlot::yRight] && dx < 0 )
01084                 {
01085                     /*
01086                       The axis needs more space than the width
01087                       of the right scale.
01088                      */
01089                     const double cRight = canvasRect.right(); // qreal -> double
01090                     canvasRect.setRight( qMin( cRight, axisRect.right() + dx ) );
01091                 }   
01092 
01093                 const double maxRight = rightScaleRect.right();
01094                 const double right = axisRect.right() - rightOffset;
01095                 axisRect.setRight( qMin( right, maxRight ) );
01096             }
01097             else
01098             {
01099                 if ( d_data->alignCanvasToScales[QwtPlot::yRight] && rightOffset < 0 )
01100                 {
01101                     canvasRect.setRight( qMin( canvasRect.right(),
01102                         axisRect.right() + rightOffset ) );
01103                 }
01104                 else
01105                 {
01106                     if ( rightOffset > 0 )
01107                         axisRect.setRight( axisRect.right() - rightOffset );
01108                 }
01109             }
01110         }
01111         else // QwtPlot::yLeft, QwtPlot::yRight
01112         {
01113             const QRectF &bottomScaleRect = scaleRect[QwtPlot::xBottom];
01114             const int bottomOffset =
01115                 backboneOffset[QwtPlot::xBottom] - endDist + 1;
01116 
01117             if ( bottomScaleRect.isValid() )
01118             {
01119                 const double dy = bottomOffset + bottomScaleRect.height();
01120                 if ( d_data->alignCanvasToScales[QwtPlot::xBottom] && dy < 0 )
01121                 {
01122                     /*
01123                       The axis needs more space than the height
01124                       of the bottom scale.
01125                      */
01126                     const double cBottom = canvasRect.bottom(); // qreal -> double
01127                     canvasRect.setBottom( qMin( cBottom, axisRect.bottom() + dy ) );
01128                 }
01129                 else
01130                 {
01131                     const double maxBottom = bottomScaleRect.top() +
01132                         d_data->layoutData.scale[QwtPlot::xBottom].tickOffset;
01133                     const double bottom = axisRect.bottom() - bottomOffset;
01134                     axisRect.setBottom( qMin( bottom, maxBottom ) );
01135                 }
01136             }
01137             else
01138             {
01139                 if ( d_data->alignCanvasToScales[QwtPlot::xBottom] && bottomOffset < 0 )
01140                 {
01141                     canvasRect.setBottom( qMin( canvasRect.bottom(),
01142                         axisRect.bottom() + bottomOffset ) );
01143                 }
01144                 else
01145                 {
01146                     if ( bottomOffset > 0 )
01147                         axisRect.setBottom( axisRect.bottom() - bottomOffset );
01148                 }
01149             }
01150 
01151             const QRectF &topScaleRect = scaleRect[QwtPlot::xTop];
01152             const int topOffset = backboneOffset[QwtPlot::xTop] - startDist;
01153 
01154             if ( topScaleRect.isValid() )
01155             {
01156                 const double dy = topOffset + topScaleRect.height();
01157                 if ( d_data->alignCanvasToScales[QwtPlot::xTop] && dy < 0 )
01158                 {
01159                     /*
01160                       The axis needs more space than the height
01161                       of the top scale.
01162                      */
01163                     const double cTop = canvasRect.top(); // qreal -> double
01164                     canvasRect.setTop( qMax( cTop, axisRect.top() - dy ) );
01165                 }
01166                 else
01167                 {
01168                     const double minTop = topScaleRect.bottom() -
01169                         d_data->layoutData.scale[QwtPlot::xTop].tickOffset;
01170                     const double top = axisRect.top() + topOffset;
01171                     axisRect.setTop( qMax( top, minTop ) );
01172                 }
01173             }
01174             else
01175             {
01176                 if ( d_data->alignCanvasToScales[QwtPlot::xTop] && topOffset < 0 )
01177                 {
01178                     canvasRect.setTop( qMax( canvasRect.top(),
01179                         axisRect.top() - topOffset ) );
01180                 }
01181                 else
01182                 {
01183                     if ( topOffset > 0 )
01184                         axisRect.setTop( axisRect.top() + topOffset );
01185                 }
01186             }
01187         }
01188     }
01189 
01190     /*
01191       The canvas has been aligned to the scale with largest
01192       border distances. Now we have to realign the other scale.
01193      */
01194 
01195 
01196     for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
01197     {
01198         QRectF &sRect = scaleRect[axis];
01199 
01200         if ( !sRect.isValid() )
01201             continue;
01202 
01203         if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop )
01204         {
01205             if ( d_data->alignCanvasToScales[QwtPlot::yLeft] )
01206             {
01207                 double y = canvasRect.left() - d_data->layoutData.scale[axis].start;
01208                 if ( !( options & IgnoreFrames ) )
01209                     y += d_data->layoutData.canvas.contentsMargins[ QwtPlot::yLeft ];
01210 
01211                 sRect.setLeft( y );
01212             }
01213             if ( d_data->alignCanvasToScales[QwtPlot::yRight] )
01214             {
01215                 double y = canvasRect.right() - 1 + d_data->layoutData.scale[axis].end;
01216                 if ( !( options & IgnoreFrames ) )
01217                     y -= d_data->layoutData.canvas.contentsMargins[ QwtPlot::yRight ];
01218 
01219                 sRect.setRight( y );
01220             }
01221 
01222             if ( d_data->alignCanvasToScales[ axis ] )
01223             {
01224                 if ( axis == QwtPlot::xTop )
01225                     sRect.setBottom( canvasRect.top() );
01226                 else
01227                     sRect.setTop( canvasRect.bottom() );
01228             }
01229         }
01230         else
01231         {
01232             if ( d_data->alignCanvasToScales[QwtPlot::xTop] )
01233             {
01234                 double x = canvasRect.top() - d_data->layoutData.scale[axis].start;
01235                 if ( !( options & IgnoreFrames ) )
01236                     x += d_data->layoutData.canvas.contentsMargins[ QwtPlot::xTop ];
01237 
01238                 sRect.setTop( x );
01239             }
01240             if ( d_data->alignCanvasToScales[QwtPlot::xBottom] )
01241             {
01242                 double x = canvasRect.bottom() - 1 + d_data->layoutData.scale[axis].end;
01243                 if ( !( options & IgnoreFrames ) )
01244                     x -= d_data->layoutData.canvas.contentsMargins[ QwtPlot::xBottom ];
01245 
01246                 sRect.setBottom( x );
01247             }
01248 
01249             if ( d_data->alignCanvasToScales[ axis ] )
01250             {
01251                 if ( axis == QwtPlot::yLeft )
01252                     sRect.setRight( canvasRect.left() );
01253                 else
01254                     sRect.setLeft( canvasRect.right() );
01255             }
01256         }
01257     }
01258 }
01259 
01270 void QwtPlotLayout::activate( const QwtPlot *plot,
01271     const QRectF &plotRect, Options options )
01272 {
01273     invalidate();
01274 
01275     QRectF rect( plotRect );  // undistributed rest of the plot rect
01276 
01277     // We extract all layout relevant parameters from the widgets,
01278     // and save them to d_data->layoutData.
01279 
01280     d_data->layoutData.init( plot, rect );
01281 
01282     if ( !( options & IgnoreLegend )
01283         && plot->legend() && !plot->legend()->isEmpty() )
01284     {
01285         d_data->legendRect = layoutLegend( options, rect );
01286 
01287         // subtract d_data->legendRect from rect
01288 
01289         const QRegion region( rect.toRect() );
01290         rect = region.subtracted( d_data->legendRect.toRect() ).boundingRect();
01291 
01292         switch ( d_data->legendPos )
01293         {
01294             case QwtPlot::LeftLegend:
01295                 rect.setLeft( rect.left() + d_data->spacing );
01296                 break;
01297             case QwtPlot::RightLegend:
01298                 rect.setRight( rect.right() - d_data->spacing );
01299                 break;
01300             case QwtPlot::TopLegend:
01301                 rect.setTop( rect.top() + d_data->spacing );
01302                 break;
01303             case QwtPlot::BottomLegend:
01304                 rect.setBottom( rect.bottom() - d_data->spacing );
01305                 break;
01306         }
01307     }
01308 
01309     /*
01310      +---+-----------+---+
01311      |       Title       |
01312      +---+-----------+---+
01313      |   |   Axis    |   |
01314      +---+-----------+---+
01315      | A |           | A |
01316      | x |  Canvas   | x |
01317      | i |           | i |
01318      | s |           | s |
01319      +---+-----------+---+
01320      |   |   Axis    |   |
01321      +---+-----------+---+
01322      |      Footer       |
01323      +---+-----------+---+
01324     */
01325 
01326     // title, footer and axes include text labels. The height of each
01327     // label depends on its line breaks, that depend on the width
01328     // for the label. A line break in a horizontal text will reduce
01329     // the available width for vertical texts and vice versa.
01330     // expandLineBreaks finds the height/width for title, footer and axes
01331     // including all line breaks.
01332 
01333     int dimTitle, dimFooter, dimAxes[QwtPlot::axisCnt];
01334     expandLineBreaks( options, rect, dimTitle, dimFooter, dimAxes );
01335 
01336     if ( dimTitle > 0 )
01337     {
01338         d_data->titleRect.setRect(
01339             rect.left(), rect.top(), rect.width(), dimTitle );
01340 
01341         rect.setTop( d_data->titleRect.bottom() + d_data->spacing );
01342 
01343         if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled !=
01344             d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
01345         {
01346             // if only one of the y axes is missing we align
01347             // the title centered to the canvas
01348 
01349             d_data->titleRect.setX( rect.left() + dimAxes[QwtPlot::yLeft] );
01350             d_data->titleRect.setWidth( rect.width()
01351                 - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight] );
01352         }
01353     }
01354 
01355     if ( dimFooter > 0 )
01356     {
01357         d_data->footerRect.setRect(
01358             rect.left(), rect.bottom() - dimFooter, rect.width(), dimFooter );
01359 
01360         rect.setBottom( d_data->footerRect.top() - d_data->spacing );
01361 
01362         if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled !=
01363             d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
01364         {
01365             // if only one of the y axes is missing we align
01366             // the footer centered to the canvas
01367 
01368             d_data->footerRect.setX( rect.left() + dimAxes[QwtPlot::yLeft] );
01369             d_data->footerRect.setWidth( rect.width()
01370                 - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight] );
01371         }
01372     }
01373 
01374     d_data->canvasRect.setRect(
01375         rect.x() + dimAxes[QwtPlot::yLeft],
01376         rect.y() + dimAxes[QwtPlot::xTop],
01377         rect.width() - dimAxes[QwtPlot::yRight] - dimAxes[QwtPlot::yLeft],
01378         rect.height() - dimAxes[QwtPlot::xBottom] - dimAxes[QwtPlot::xTop] );
01379 
01380     for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
01381     {
01382         // set the rects for the axes
01383 
01384         if ( dimAxes[axis] )
01385         {
01386             int dim = dimAxes[axis];
01387             QRectF &scaleRect = d_data->scaleRect[axis];
01388 
01389             scaleRect = d_data->canvasRect;
01390             switch ( axis )
01391             {
01392                 case QwtPlot::yLeft:
01393                     scaleRect.setX( d_data->canvasRect.left() - dim );
01394                     scaleRect.setWidth( dim );
01395                     break;
01396                 case QwtPlot::yRight:
01397                     scaleRect.setX( d_data->canvasRect.right() );
01398                     scaleRect.setWidth( dim );
01399                     break;
01400                 case QwtPlot::xBottom:
01401                     scaleRect.setY( d_data->canvasRect.bottom() );
01402                     scaleRect.setHeight( dim );
01403                     break;
01404                 case QwtPlot::xTop:
01405                     scaleRect.setY( d_data->canvasRect.top() - dim );
01406                     scaleRect.setHeight( dim );
01407                     break;
01408             }
01409             scaleRect = scaleRect.normalized();
01410         }
01411     }
01412 
01413     // +---+-----------+---+
01414     // |  <-   Axis   ->   |
01415     // +-^-+-----------+-^-+
01416     // | | |           | | |
01417     // |   |           |   |
01418     // | A |           | A |
01419     // | x |  Canvas   | x |
01420     // | i |           | i |
01421     // | s |           | s |
01422     // |   |           |   |
01423     // | | |           | | |
01424     // +-V-+-----------+-V-+
01425     // |   <-  Axis   ->   |
01426     // +---+-----------+---+
01427 
01428     // The ticks of the axes - not the labels above - should
01429     // be aligned to the canvas. So we try to use the empty
01430     // corners to extend the axes, so that the label texts
01431     // left/right of the min/max ticks are moved into them.
01432 
01433     alignScales( options, d_data->canvasRect, d_data->scaleRect );
01434 
01435     if ( !d_data->legendRect.isEmpty() )
01436     {
01437         // We prefer to align the legend to the canvas - not to
01438         // the complete plot - if possible.
01439 
01440         d_data->legendRect = alignLegend( d_data->canvasRect, d_data->legendRect );
01441     }
01442 }


plotjuggler
Author(s): Davide Faconti
autogenerated on Wed Jul 3 2019 19:28:05