# qwt_plot_layout.cpp

    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 // vim: expandtab
    00011
    00012 #include <qscrollbar.h>
    00013 #include "qwt_rect.h"
    00014 #include "qwt_text.h"
    00015 #include "qwt_text_label.h"
    00016 #include "qwt_plot_canvas.h"
    00017 #include "qwt_scale_widget.h"
    00018 #include "qwt_legend.h"
    00019 #include "qwt_plot_layout.h"
    00020
    00021 class QwtPlotLayout::LayoutData
    00022 {
    00023 public:
    00024     void init(const QwtPlot *, const QRect &rect);
    00025
    00026     struct t_legendData
    00027     {
    00028         int frameWidth;
    00029         int vScrollBarWidth;
    00030         int hScrollBarHeight;
    00031         QSize hint;
    00032     } legend;
    00033
    00034     struct t_titleData
    00035     {
    00036         QwtText text;
    00037         int frameWidth;
    00038     } title;
    00039
    00040     struct t_scaleData
    00041     {
    00042         bool isEnabled;
    00043         const QwtScaleWidget *scaleWidget;
    00044         QFont scaleFont;
    00045         int start;
    00046         int end;
    00047         int baseLineOffset;
    00048         int tickOffset;
    00049         int dimWithoutTitle;
    00050     } scale[QwtPlot::axisCnt];
    00051
    00052     struct t_canvasData
    00053     {
    00054         int frameWidth;
    00055     } canvas;
    00056 };
    00057
    00058 /*
    00059   Extract all layout relevant data from the plot components
    00060 */
    00061
    00062 void QwtPlotLayout::LayoutData::init(const QwtPlot *plot, const QRect &rect)
    00063 {
    00064     // legend
    00065
    00066     if ( plot->plotLayout()->legendPosition() != QwtPlot::ExternalLegend
    00067         && plot->legend() )
    00068     {
    00069         legend.frameWidth = plot->legend()->frameWidth();
    00070         legend.vScrollBarWidth =
    00071             plot->legend()->verticalScrollBar()->sizeHint().width();
    00072         legend.hScrollBarHeight =
    00073             plot->legend()->horizontalScrollBar()->sizeHint().height();
    00074
    00075         const QSize hint = plot->legend()->sizeHint();
    00076
    00077         int w = qwtMin(hint.width(), rect.width());
    00078         int h = plot->legend()->heightForWidth(w);
    00079         if ( h == 0 )
    00080             h = hint.height();
    00081
    00082         if ( h > rect.height() )
    00083             w += legend.vScrollBarWidth;
    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     // scales
    00104
    00105     for (int axis = 0; axis < QwtPlot::axisCnt; axis++ )
    00106     {
    00107         if ( plot->axisEnabled(axis) )
    00108         {
    00109             const QwtScaleWidget *scaleWidget = plot->axisWidget(axis);
    00110
    00111             scale[axis].isEnabled = true;
    00112
    00113             scale[axis].scaleWidget = scaleWidget;
    00114
    00115             scale[axis].scaleFont = scaleWidget->font();
    00116
    00117             scale[axis].start = scaleWidget->startBorderDist();
    00118             scale[axis].end = scaleWidget->endBorderDist();
    00119
    00120             scale[axis].baseLineOffset = scaleWidget->margin();
    00121             scale[axis].tickOffset = scaleWidget->margin();
    00122             if ( scaleWidget->scaleDraw()->hasComponent(
    00123                 QwtAbstractScaleDraw::Ticks) )
    00124             {
    00125                 scale[axis].tickOffset +=
    00126                     (int)scaleWidget->scaleDraw()->majTickLength();
    00127             }
    00128
    00129             scale[axis].dimWithoutTitle = scaleWidget->dimForLength(
    00130                 QWIDGETSIZE_MAX, scale[axis].scaleFont);
    00131
    00132             if ( !scaleWidget->title().isEmpty() )
    00133             {
    00134                 scale[axis].dimWithoutTitle -=
    00135                     scaleWidget->titleHeightForWidth(QWIDGETSIZE_MAX);
    00136             }
    00137         }
    00138         else
    00139         {
    00140             scale[axis].isEnabled = false;
    00141             scale[axis].start = 0;
    00142             scale[axis].end = 0;
    00143             scale[axis].baseLineOffset = 0;
    00144             scale[axis].tickOffset = 0;
    00145             scale[axis].dimWithoutTitle = 0;
    00146         }
    00147     }
    00148
    00149     // canvas
    00150
    00151     canvas.frameWidth = plot->canvas()->frameWidth();
    00152 }
    00153
    00154 class QwtPlotLayout::PrivateData
    00155 {
    00156 public:
    00157     PrivateData():
    00158         margin(0),
    00159         spacing(5),
    00160         alignCanvasToScales(false)
    00161     {
    00162     }
    00163
    00164     QRect titleRect;
    00165     QRect legendRect;
    00166     QRect scaleRect[QwtPlot::axisCnt];
    00167     QRect canvasRect;
    00168
    00169     QwtPlotLayout::LayoutData layoutData;
    00170
    00171     QwtPlot::LegendPosition legendPos;
    00172     double legendRatio;
    00173     unsigned int margin;
    00174     unsigned int spacing;
    00175     unsigned int canvasMargin[QwtPlot::axisCnt];
    00176     bool alignCanvasToScales;
    00177 };
    00178
    00183 QwtPlotLayout::QwtPlotLayout()
    00184 {
    00185     d_data = new PrivateData;
    00186
    00187     setLegendPosition(QwtPlot::BottomLegend);
    00188     setCanvasMargin(4);
    00189
    00190     invalidate();
    00191 }
    00192
    00194 QwtPlotLayout::~QwtPlotLayout()
    00195 {
    00196     delete d_data;
    00197 }
    00198
    00207 void QwtPlotLayout::setMargin(int margin)
    00208 {
    00209     if ( margin < 0 )
    00210         margin = 0;
    00211     d_data->margin = margin;
    00212 }
    00213
    00218 int QwtPlotLayout::margin() const
    00219 {
    00220     return d_data->margin;
    00221 }
    00222
    00236 void QwtPlotLayout::setCanvasMargin(int margin, int axis)
    00237 {
    00238     if ( margin < -1 )
    00239         margin = -1;
    00240
    00241     if ( axis == -1 )
    00242     {
    00243         for (axis = 0; axis < QwtPlot::axisCnt; axis++)
    00244             d_data->canvasMargin[axis] = margin;
    00245     }
    00246     else if ( axis >= 0 || axis < QwtPlot::axisCnt )
    00247         d_data->canvasMargin[axis] = margin;
    00248 }
    00249
    00254 int QwtPlotLayout::canvasMargin(int axis) const
    00255 {
    00256     if ( axis < 0 || axis >= QwtPlot::axisCnt )
    00257         return 0;
    00258
    00259     return d_data->canvasMargin[axis];
    00260 }
    00261
    00274 void QwtPlotLayout::setAlignCanvasToScales(bool alignCanvasToScales)
    00275 {
    00276     d_data->alignCanvasToScales = alignCanvasToScales;
    00277 }
    00278
    00288 bool QwtPlotLayout::alignCanvasToScales() const
    00289 {
    00290     return d_data->alignCanvasToScales;
    00291 }
    00292
    00300 void QwtPlotLayout::setSpacing(int spacing)
    00301 {
    00302     d_data->spacing = qwtMax(0, spacing);
    00303 }
    00304
    00309 int QwtPlotLayout::spacing() const
    00310 {
    00311     return d_data->spacing;
    00312 }
    00313
    00327 void QwtPlotLayout::setLegendPosition(QwtPlot::LegendPosition pos, double ratio)
    00328 {
    00329     if ( ratio > 1.0 )
    00330         ratio = 1.0;
    00331
    00332     switch(pos)
    00333     {
    00334         case QwtPlot::TopLegend:
    00335         case QwtPlot::BottomLegend:
    00336             if ( ratio <= 0.0 )
    00337                 ratio = 0.33;
    00338             d_data->legendRatio = ratio;
    00339             d_data->legendPos = pos;
    00340             break;
    00341         case QwtPlot::LeftLegend:
    00342         case QwtPlot::RightLegend:
    00343             if ( ratio <= 0.0 )
    00344                 ratio = 0.5;
    00345             d_data->legendRatio = ratio;
    00346             d_data->legendPos = pos;
    00347             break;
    00348         case QwtPlot::ExternalLegend:
    00349             d_data->legendRatio = ratio; // meaningless
    00350             d_data->legendPos = pos;
    00351         default:
    00352             break;
    00353     }
    00354 }
    00355
    00364 void QwtPlotLayout::setLegendPosition(QwtPlot::LegendPosition pos)
    00365 {
    00366     setLegendPosition(pos, 0.0);
    00367 }
    00368
    00374 QwtPlot::LegendPosition QwtPlotLayout::legendPosition() const
    00375 {
    00376     return d_data->legendPos;
    00377 }
    00378
    00388 void QwtPlotLayout::setLegendRatio(double ratio)
    00389 {
    00390     setLegendPosition(legendPosition(), ratio);
    00391 }
    00392
    00397 double QwtPlotLayout::legendRatio() const
    00398 {
    00399     return d_data->legendRatio;
    00400 }
    00401
    00407 const QRect &QwtPlotLayout::titleRect() const
    00408 {
    00409     return d_data->titleRect;
    00410 }
    00411
    00417 const QRect &QwtPlotLayout::legendRect() const
    00418 {
    00419     return d_data->legendRect;
    00420 }
    00421
    00428 const QRect &QwtPlotLayout::scaleRect(int axis) const
    00429 {
    00430     if ( axis < 0 || axis >= QwtPlot::axisCnt )
    00431     {
    00432         static QRect dummyRect;
    00433         return dummyRect;
    00434     }
    00435     return d_data->scaleRect[axis];
    00436 }
    00437
    00443 const QRect &QwtPlotLayout::canvasRect() const
    00444 {
    00445     return d_data->canvasRect;
    00446 }
    00447
    00452 void QwtPlotLayout::invalidate()
    00453 {
    00454     d_data->titleRect = d_data->legendRect = d_data->canvasRect = QRect();
    00455     for (int axis = 0; axis < QwtPlot::axisCnt; axis++ )
    00456         d_data->scaleRect[axis] = QRect();
    00457 }
    00458
    00464 QSize QwtPlotLayout::minimumSizeHint(const QwtPlot *plot) const
    00465 {
    00466     class ScaleData
    00467     {
    00468     public:
    00469         ScaleData()
    00470         {
    00471             w = h = minLeft = minRight = tickOffset = 0;
    00472         }
    00473
    00474         int w;
    00475         int h;
    00476         int minLeft;
    00477         int minRight;
    00478         int tickOffset;
    00479     } scaleData[QwtPlot::axisCnt];
    00480
    00481     int canvasBorder[QwtPlot::axisCnt];
    00482
    00483     int axis;
    00484     for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
    00485     {
    00486         if ( plot->axisEnabled(axis) )
    00487         {
    00488             const QwtScaleWidget *scl = plot->axisWidget(axis);
    00489             ScaleData &sd = scaleData[axis];
    00490
    00491             const QSize hint = scl->minimumSizeHint();
    00492             sd.w = hint.width();
    00493             sd.h = hint.height();
    00494             scl->getBorderDistHint(sd.minLeft, sd.minRight);
    00495             sd.tickOffset = scl->margin();
    00496             if ( scl->scaleDraw()->hasComponent(QwtAbstractScaleDraw::Ticks) )
    00497                 sd.tickOffset += scl->scaleDraw()->majTickLength();
    00498         }
    00499
    00500         canvasBorder[axis] = plot->canvas()->frameWidth() +
    00501             d_data->canvasMargin[axis] + 1;
    00502
    00503     }
    00504
    00505
    00506     for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
    00507     {
    00508         ScaleData &sd = scaleData[axis];
    00509         if ( sd.w && (axis == QwtPlot::xBottom || axis == QwtPlot::xTop) )
    00510         {
    00511             if ( (sd.minLeft > canvasBorder[QwtPlot::yLeft])
    00512                 && scaleData[QwtPlot::yLeft].w )
    00513             {
    00514                 int shiftLeft = sd.minLeft - canvasBorder[QwtPlot::yLeft];
    00515                 if ( shiftLeft > scaleData[QwtPlot::yLeft].w )
    00516                     shiftLeft = scaleData[QwtPlot::yLeft].w;
    00517
    00518                 sd.w -= shiftLeft;
    00519             }
    00520             if ( (sd.minRight > canvasBorder[QwtPlot::yRight])
    00521                 && scaleData[QwtPlot::yRight].w )
    00522             {
    00523                 int shiftRight = sd.minRight - canvasBorder[QwtPlot::yRight];
    00524                 if ( shiftRight > scaleData[QwtPlot::yRight].w )
    00525                     shiftRight = scaleData[QwtPlot::yRight].w;
    00526
    00527                 sd.w -= shiftRight;
    00528             }
    00529         }
    00530
    00531         if ( sd.h && (axis == QwtPlot::yLeft || axis == QwtPlot::yRight) )
    00532         {
    00533             if ( (sd.minLeft > canvasBorder[QwtPlot::xBottom]) &&
    00534                 scaleData[QwtPlot::xBottom].h )
    00535             {
    00536                 int shiftBottom = sd.minLeft - canvasBorder[QwtPlot::xBottom];
    00537                 if ( shiftBottom > scaleData[QwtPlot::xBottom].tickOffset )
    00538                     shiftBottom = scaleData[QwtPlot::xBottom].tickOffset;
    00539
    00540                 sd.h -= shiftBottom;
    00541             }
    00542             if ( (sd.minLeft > canvasBorder[QwtPlot::xTop]) &&
    00543                 scaleData[QwtPlot::xTop].h )
    00544             {
    00545                 int shiftTop = sd.minRight - canvasBorder[QwtPlot::xTop];
    00546                 if ( shiftTop > scaleData[QwtPlot::xTop].tickOffset )
    00547                     shiftTop = scaleData[QwtPlot::xTop].tickOffset;
    00548
    00549                 sd.h -= shiftTop;
    00550             }
    00551         }
    00552     }
    00553
    00554     const QwtPlotCanvas *canvas = plot->canvas();
    00555     const QSize minCanvasSize = canvas->minimumSize();
    00556
    00557     int w = scaleData[QwtPlot::yLeft].w + scaleData[QwtPlot::yRight].w;
    00558     int cw = qwtMax(scaleData[QwtPlot::xBottom].w, scaleData[QwtPlot::xTop].w)
    00559         + 2 * (canvas->frameWidth() + 1);
    00560     w += qwtMax(cw, minCanvasSize.width());
    00561
    00562     int h = scaleData[QwtPlot::xBottom].h + scaleData[QwtPlot::xTop].h;
    00563     int ch = qwtMax(scaleData[QwtPlot::yLeft].h, scaleData[QwtPlot::yRight].h)
    00564         + 2 * (canvas->frameWidth() + 1);
    00565     h += qwtMax(ch, minCanvasSize.height());
    00566
    00567     const QwtTextLabel *title = plot->titleLabel();
    00568     if (title && !title->text().isEmpty())
    00569     {
    00570         // If only QwtPlot::yLeft or QwtPlot::yRight is showing,
    00571         // we center on the plot canvas.
    00572         const bool centerOnCanvas = !(plot->axisEnabled(QwtPlot::yLeft)
    00573             && plot->axisEnabled(QwtPlot::yRight));
    00574
    00575         int titleW = w;
    00576         if ( centerOnCanvas )
    00577         {
    00578             titleW -= scaleData[QwtPlot::yLeft].w
    00579                 + scaleData[QwtPlot::yRight].w;
    00580         }
    00581
    00582         int titleH = title->heightForWidth(titleW);
    00583         if ( titleH > titleW ) // Compensate for a long title
    00584         {
    00585             w = titleW = titleH;
    00586             if ( centerOnCanvas )
    00587             {
    00588                 w += scaleData[QwtPlot::yLeft].w
    00589                     + scaleData[QwtPlot::yRight].w;
    00590             }
    00591
    00592             titleH = title->heightForWidth(titleW);
    00593         }
    00594         h += titleH + d_data->spacing;
    00595     }
    00596
    00597     // Compute the legend contribution
    00598
    00599     const QwtLegend *legend = plot->legend();
    00600     if ( d_data->legendPos != QwtPlot::ExternalLegend
    00601         && legend && !legend->isEmpty() )
    00602     {
    00603         if ( d_data->legendPos == QwtPlot::LeftLegend
    00604             || d_data->legendPos == QwtPlot::RightLegend )
    00605         {
    00606             int legendW = legend->sizeHint().width();
    00607             int legendH = legend->heightForWidth(legendW);
    00608
    00609             if ( legend->frameWidth() > 0 )
    00610                 w += d_data->spacing;
    00611
    00612             if ( legendH > h )
    00613                 legendW += legend->verticalScrollBar()->sizeHint().height();
    00614
    00615             if ( d_data->legendRatio < 1.0 )
    00616                 legendW = qwtMin(legendW, int(w / (1.0 - d_data->legendRatio)));
    00617
    00618             w += legendW;
    00619         }
    00620         else // QwtPlot::Top, QwtPlot::Bottom
    00621         {
    00622             int legendW = qwtMin(legend->sizeHint().width(), w);
    00623             int legendH = legend->heightForWidth(legendW);
    00624
    00625             if ( legend->frameWidth() > 0 )
    00626                 h += d_data->spacing;
    00627
    00628             if ( d_data->legendRatio < 1.0 )
    00629                 legendH = qwtMin(legendH, int(h / (1.0 - d_data->legendRatio)));
    00630
    00631             h += legendH;
    00632         }
    00633     }
    00634
    00635     w += 2 * d_data->margin;
    00636     h += 2 * d_data->margin;
    00637
    00638     return QSize( w, h );
    00639 }
    00640
    00648 QRect QwtPlotLayout::layoutLegend(int options,
    00649     const QRect &rect) const
    00650 {
    00651     const QSize hint(d_data->layoutData.legend.hint);
    00652
    00653     int dim;
    00654     if ( d_data->legendPos == QwtPlot::LeftLegend
    00655         || d_data->legendPos == QwtPlot::RightLegend )
    00656     {
    00657         // We don't allow vertical legends to take more than
    00658         // half of the available space.
    00659
    00660         dim = qwtMin(hint.width(), int(rect.width() * d_data->legendRatio));
    00661
    00662         if ( !(options & IgnoreScrollbars) )
    00663         {
    00664             if ( hint.height() > rect.height() )
    00665             {
    00666                 // The legend will need additional
    00667                 // space for the vertical scrollbar.
    00668
    00669                 dim += d_data->layoutData.legend.vScrollBarWidth;
    00670             }
    00671         }
    00672     }
    00673     else
    00674     {
    00675         dim = qwtMin(hint.height(), int(rect.height() * d_data->legendRatio));
    00676         dim = qwtMax(dim, d_data->layoutData.legend.hScrollBarHeight);
    00677     }
    00678
    00679     QRect legendRect = rect;
    00680     switch(d_data->legendPos)
    00681     {
    00682         case QwtPlot::LeftLegend:
    00683             legendRect.setWidth(dim);
    00684             break;
    00685         case QwtPlot::RightLegend:
    00686             legendRect.setX(rect.right() - dim + 1);
    00687             legendRect.setWidth(dim);
    00688             break;
    00689         case QwtPlot::TopLegend:
    00690             legendRect.setHeight(dim);
    00691             break;
    00692         case QwtPlot::BottomLegend:
    00693             legendRect.setY(rect.bottom() - dim + 1);
    00694             legendRect.setHeight(dim);
    00695             break;
    00696         case QwtPlot::ExternalLegend:
    00697             break;
    00698     }
    00699
    00700     return legendRect;
    00701 }
    00702
    00709 QRect QwtPlotLayout::alignLegend(const QRect &canvasRect,
    00710     const QRect &legendRect) const
    00711 {
    00712     QRect alignedRect = legendRect;
    00713
    00714     if ( d_data->legendPos == QwtPlot::BottomLegend
    00715         || d_data->legendPos == QwtPlot::TopLegend )
    00716     {
    00717         if ( d_data->layoutData.legend.hint.width() < canvasRect.width() )
    00718         {
    00719             alignedRect.setX(canvasRect.x());
    00720             alignedRect.setWidth(canvasRect.width());
    00721         }
    00722     }
    00723     else
    00724     {
    00725         if ( d_data->layoutData.legend.hint.height() < canvasRect.height() )
    00726         {
    00727             alignedRect.setY(canvasRect.y());
    00728             alignedRect.setHeight(canvasRect.height());
    00729         }
    00730     }
    00731
    00732     return alignedRect;
    00733 }
    00734
    00744 void QwtPlotLayout::expandLineBreaks(int options, const QRect &rect,
    00745     int &dimTitle, int dimAxis[QwtPlot::axisCnt]) const
    00746 {
    00747     dimTitle = 0;
    00748     for ( int i = 0; i < QwtPlot::axisCnt; i++ )
    00749         dimAxis[i] = 0;
    00750
    00751     bool done = false;
    00752     while (!done)
    00753     {
    00754         done = true;
    00755
    00756         // the size for the 4 axis depend on each other. Expanding
    00757         // the height of a horizontal axis will shrink the height
    00758         // for the vertical axis, shrinking the height of a vertical
    00759         // axis will result in a line break what will expand the
    00760         // width and results in shrinking the width of a horizontal
    00761         // axis what might result in a line break of a horizontal
    00762         // axis ... . So we loop as long until no size changes.
    00763
    00764         if ( !d_data->layoutData.title.text.isEmpty() )
    00765         {
    00766             int w = rect.width();
    00767
    00768             if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled
    00769                 != d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
    00770             {
    00771                 // center to the canvas
    00772                 w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight];
    00773             }
    00774
    00775             int d = d_data->layoutData.title.text.heightForWidth(w);
    00776             if ( !(options & IgnoreFrames) )
    00777                 d += 2 * d_data->layoutData.title.frameWidth;
    00778
    00779             if ( d > dimTitle )
    00780             {
    00781                 dimTitle = d;
    00782                 done = false;
    00783             }
    00784         }
    00785
    00786         for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
    00787         {
    00788             int backboneOffset = d_data->canvasMargin[axis];
    00789             if ( !(options & IgnoreFrames) )
    00790                 backboneOffset += d_data->layoutData.canvas.frameWidth;
    00791
    00792             const struct LayoutData::t_scaleData &scaleData =
    00793                 d_data->layoutData.scale[axis];
    00794
    00795             if (scaleData.isEnabled)
    00796             {
    00797                 int length;
    00798                 if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom )
    00799                 {
    00800                     length = rect.width() - dimAxis[QwtPlot::yLeft]
    00801                         - dimAxis[QwtPlot::yRight];
    00802                     length += qwtMin(dimAxis[QwtPlot::yLeft],
    00803                         scaleData.start - backboneOffset);
    00804                     length += qwtMin(dimAxis[QwtPlot::yRight],
    00805                         scaleData.end - backboneOffset);
    00806                 }
    00807                 else // QwtPlot::yLeft, QwtPlot::yRight
    00808                 {
    00809                     length = rect.height() - dimAxis[QwtPlot::xTop]
    00810                         - dimAxis[QwtPlot::xBottom];
    00811
    00812                     if ( dimAxis[QwtPlot::xBottom] > 0 )
    00813                     {
    00814                         length += qwtMin(
    00815                             d_data->layoutData.scale[QwtPlot::xBottom].tickOffset,
    00816                             scaleData.start - backboneOffset);
    00817                     }
    00818                     if ( dimAxis[QwtPlot::xTop] > 0 )
    00819                     {
    00820                         length += qwtMin(
    00821                             d_data->layoutData.scale[QwtPlot::xTop].tickOffset,
    00822                             scaleData.end - backboneOffset);
    00823                     }
    00824
    00825                     if ( dimTitle > 0 )
    00826                         length -= dimTitle + d_data->spacing;
    00827                 }
    00828
    00829                 int d = scaleData.dimWithoutTitle;
    00830                 if ( !scaleData.scaleWidget->title().isEmpty() )
    00831                 {
    00832                     d += scaleData.scaleWidget->titleHeightForWidth(length);
    00833                 }
    00834
    00835                 if ( d > dimAxis[axis] )
    00836                 {
    00837                     dimAxis[axis] = d;
    00838                     done = false;
    00839                 }
    00840             }
    00841         }
    00842     }
    00843 }
    00844
    00850 void QwtPlotLayout::alignScales(int options,
    00851     QRect &canvasRect, QRect scaleRect[QwtPlot::axisCnt]) const
    00852 {
    00853     int axis;
    00854
    00855     int backboneOffset[QwtPlot::axisCnt];
    00856     for (axis = 0; axis < QwtPlot::axisCnt; axis++ )
    00857     {
    00858         backboneOffset[axis] = 0;
    00859         if ( !d_data->alignCanvasToScales )
    00860             backboneOffset[axis] += d_data->canvasMargin[axis];
    00861         if ( !(options & IgnoreFrames) )
    00862             backboneOffset[axis] += d_data->layoutData.canvas.frameWidth;
    00863     }
    00864
    00865     for (axis = 0; axis < QwtPlot::axisCnt; axis++ )
    00866     {
    00867         if ( !scaleRect[axis].isValid() )
    00868             continue;
    00869
    00870         const int startDist = d_data->layoutData.scale[axis].start;
    00871         const int endDist = d_data->layoutData.scale[axis].end;
    00872
    00873         QRect &axisRect = scaleRect[axis];
    00874
    00875         if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom )
    00876         {
    00877             const int leftOffset = backboneOffset[QwtPlot::yLeft] - startDist;
    00878
    00879             if ( scaleRect[QwtPlot::yLeft].isValid() )
    00880             {
    00881                 int minLeft = scaleRect[QwtPlot::yLeft].left();
    00882                 int left = axisRect.left() + leftOffset;
    00883                 axisRect.setLeft(qwtMax(left, minLeft));
    00884             }
    00885             else
    00886             {
    00887                 if ( d_data->alignCanvasToScales && leftOffset < 0 )
    00888                 {
    00889                     canvasRect.setLeft(qwtMax(canvasRect.left(),
    00890                         axisRect.left() - leftOffset));
    00891                 }
    00892                 else
    00893                 {
    00894                     if ( leftOffset > 0 )
    00895                         axisRect.setLeft(axisRect.left() + leftOffset);
    00896                 }
    00897             }
    00898
    00899             const int rightOffset = backboneOffset[QwtPlot::yRight] - endDist;
    00900
    00901             if ( scaleRect[QwtPlot::yRight].isValid() )
    00902             {
    00903                 int maxRight = scaleRect[QwtPlot::yRight].right();
    00904                 int right = axisRect.right() - rightOffset;
    00905                 axisRect.setRight(qwtMin(right, maxRight));
    00906             }
    00907             else
    00908             {
    00909                 if ( d_data->alignCanvasToScales && rightOffset < 0 )
    00910                 {
    00911                     canvasRect.setRight( qwtMin(canvasRect.right(),
    00912                         axisRect.right() + rightOffset) );
    00913                 }
    00914                 else
    00915                 {
    00916                     if ( rightOffset > 0 )
    00917                         axisRect.setRight(axisRect.right() - rightOffset);
    00918                 }
    00919             }
    00920         }
    00921         else // QwtPlot::yLeft, QwtPlot::yRight
    00922         {
    00923             const int bottomOffset =
    00924                 backboneOffset[QwtPlot::xBottom] - endDist;
    00925
    00926             if ( scaleRect[QwtPlot::xBottom].isValid() )
    00927             {
    00928                 int maxBottom = scaleRect[QwtPlot::xBottom].top() +
    00929                     d_data->layoutData.scale[QwtPlot::xBottom].tickOffset;
    00930
    00931                 int bottom = axisRect.bottom() - bottomOffset;
    00932                 axisRect.setBottom(qwtMin(bottom, maxBottom));
    00933             }
    00934             else
    00935             {
    00936                 if ( d_data->alignCanvasToScales && bottomOffset < 0 )
    00937                 {
    00938                     canvasRect.setBottom(qwtMin(canvasRect.bottom(),
    00939                         axisRect.bottom() + bottomOffset));
    00940                 }
    00941                 else
    00942                 {
    00943                     if ( bottomOffset > 0 )
    00944                         axisRect.setBottom(axisRect.bottom() - bottomOffset);
    00945                 }
    00946             }
    00947
    00948             const int topOffset = backboneOffset[QwtPlot::xTop] - startDist;
    00949
    00950             if ( scaleRect[QwtPlot::xTop].isValid() )
    00951             {
    00952                 int minTop = scaleRect[QwtPlot::xTop].bottom() -
    00953                     d_data->layoutData.scale[QwtPlot::xTop].tickOffset;
    00954
    00955                 int top = axisRect.top() + topOffset;
    00956                 axisRect.setTop(qwtMax(top, minTop));
    00957             }
    00958             else
    00959             {
    00960                 if ( d_data->alignCanvasToScales && topOffset < 0 )
    00961                 {
    00962                     canvasRect.setTop(qwtMax(canvasRect.top(),
    00963                         axisRect.top() - topOffset));
    00964                 }
    00965                 else
    00966                 {
    00967                     if ( topOffset > 0 )
    00968                         axisRect.setTop(axisRect.top() + topOffset);
    00969                 }
    00970             }
    00971         }
    00972     }
    00973
    00974     if ( d_data->alignCanvasToScales )
    00975     {
    00976         /*
    00977           The canvas has been aligned to the scale with largest
    00978           border distances. Now we have to realign the other scale.
    00979          */
    00980
    00981         if ( scaleRect[QwtPlot::xBottom].isValid() &&
    00982             scaleRect[QwtPlot::xTop].isValid() )
    00983         {
    00984             for ( int axis = QwtPlot::xBottom; axis <= QwtPlot::xTop; axis++ )
    00985             {
    00986                 scaleRect[axis].setLeft(canvasRect.left() + 1
    00987                     - d_data->layoutData.scale[axis].start);
    00988                 scaleRect[axis].setRight(canvasRect.right() - 1
    00989                     + d_data->layoutData.scale[axis].end);
    00990             }
    00991         }
    00992
    00993         if ( scaleRect[QwtPlot::yLeft].isValid() &&
    00994             scaleRect[QwtPlot::yRight].isValid() )
    00995         {
    00996             for ( int axis = QwtPlot::yLeft; axis <= QwtPlot::yRight; axis++ )
    00997             {
    00998                 scaleRect[axis].setTop(canvasRect.top() + 1
    00999                     - d_data->layoutData.scale[axis].start);
    01000                 scaleRect[axis].setBottom(canvasRect.bottom() - 1
    01001                     + d_data->layoutData.scale[axis].end);
    01002             }
    01003         }
    01004     }
    01005 }
    01006
    01017 void QwtPlotLayout::activate(const QwtPlot *plot,
    01018     const QRect &plotRect, int options)
    01019 {
    01020     invalidate();
    01021
    01022     QRect rect(plotRect);  // undistributed rest of the plot rect
    01023
    01024     if ( !(options & IgnoreMargin) )
    01025     {
    01026         // subtract the margin
    01027
    01028         rect.setRect(
    01029             rect.x() + d_data->margin,
    01030             rect.y() + d_data->margin,
    01031             rect.width() - 2 * d_data->margin,
    01032             rect.height() - 2 * d_data->margin
    01033         );
    01034     }
    01035
    01036     // We extract all layout relevant data from the widgets,
    01037     // filter them through pfilter and save them to d_data->layoutData.
    01038
    01039     d_data->layoutData.init(plot, rect);
    01040
    01041     if (!(options & IgnoreLegend)
    01042         && d_data->legendPos != QwtPlot::ExternalLegend
    01043         && plot->legend() && !plot->legend()->isEmpty() )
    01044     {
    01045         d_data->legendRect = layoutLegend(options, rect);
    01046
    01047         // subtract d_data->legendRect from rect
    01048
    01049         const QRegion region(rect);
    01050         rect = region.subtract(d_data->legendRect).boundingRect();
    01051
    01052         if ( d_data->layoutData.legend.frameWidth &&
    01053             !(options & IgnoreFrames ) )
    01054         {
    01055             // In case of a frame we have to insert a spacing.
    01056             // Otherwise the leading of the font separates
    01057             // legend and scale/canvas
    01058
    01059             switch(d_data->legendPos)
    01060             {
    01061                 case QwtPlot::LeftLegend:
    01062                     rect.setLeft(rect.left() + d_data->spacing);
    01063                     break;
    01064                 case QwtPlot::RightLegend:
    01065                     rect.setRight(rect.right() - d_data->spacing);
    01066                     break;
    01067                 case QwtPlot::TopLegend:
    01068                     rect.setTop(rect.top() + d_data->spacing);
    01069                     break;
    01070                 case QwtPlot::BottomLegend:
    01071                     rect.setBottom(rect.bottom() - d_data->spacing);
    01072                     break;
    01073                 case QwtPlot::ExternalLegend:
    01074                     break; // suppress compiler warning
    01075             }
    01076         }
    01077     }
    01078
    01079     /*
    01080      +---+-----------+---+
    01081      |       Title       |
    01082      +---+-----------+---+
    01083      |   |   Axis    |   |
    01084      +---+-----------+---+
    01085      | A |           | A |
    01086      | x |  Canvas   | x |
    01087      | i |           | i |
    01088      | s |           | s |
    01089      +---+-----------+---+
    01090      |   |   Axis    |   |
    01091      +---+-----------+---+
    01092     */
    01093
    01094
    01095     // axes and title include text labels. The height of each
    01096     // label depends on its line breaks, that depend on the width
    01097     // for the label. A line break in a horizontal text will reduce
    01098     // the available width for vertical texts and vice versa.
    01099     // expandLineBreaks finds the height/width for title and axes
    01100     // including all line breaks.
    01101
    01102     int dimTitle, dimAxes[QwtPlot::axisCnt];
    01103     expandLineBreaks(options, rect, dimTitle, dimAxes);
    01104
    01105     if (dimTitle > 0 )
    01106     {
    01107         d_data->titleRect = QRect(rect.x(), rect.y(),
    01108             rect.width(), dimTitle);
    01109
    01110         if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled !=
    01111             d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
    01112         {
    01113             // if only one of the y axes is missing we align
    01114             // the title centered to the canvas
    01115
    01116             d_data->titleRect.setX(rect.x() + dimAxes[QwtPlot::yLeft]);
    01117             d_data->titleRect.setWidth(rect.width()
    01118                 - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight]);
    01119         }
    01120
    01121         // subtract title
    01122         rect.setTop(rect.top() + dimTitle + d_data->spacing);
    01123     }
    01124
    01125     d_data->canvasRect.setRect(
    01126         rect.x() + dimAxes[QwtPlot::yLeft],
    01127         rect.y() + dimAxes[QwtPlot::xTop],
    01128         rect.width() - dimAxes[QwtPlot::yRight] - dimAxes[QwtPlot::yLeft],
    01129         rect.height() - dimAxes[QwtPlot::xBottom] - dimAxes[QwtPlot::xTop]);
    01130
    01131     for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
    01132     {
    01133         // set the rects for the axes
    01134
    01135         if ( dimAxes[axis] )
    01136         {
    01137             int dim = dimAxes[axis];
    01138             QRect &scaleRect = d_data->scaleRect[axis];
    01139
    01140             scaleRect = d_data->canvasRect;
    01141             switch(axis)
    01142             {
    01143                 case QwtPlot::yLeft:
    01144                     scaleRect.setX(d_data->canvasRect.left() - dim);
    01145                     scaleRect.setWidth(dim);
    01146                     break;
    01147                 case QwtPlot::yRight:
    01148                     scaleRect.setX(d_data->canvasRect.right() + 1);
    01149                     scaleRect.setWidth(dim);
    01150                     break;
    01151                 case QwtPlot::xBottom:
    01152                     scaleRect.setY(d_data->canvasRect.bottom() + 1);
    01153                     scaleRect.setHeight(dim);
    01154                     break;
    01155                 case QwtPlot::xTop:
    01156                     scaleRect.setY(d_data->canvasRect.top() - dim);
    01157                     scaleRect.setHeight(dim);
    01158                     break;
    01159             }
    01160 #if QT_VERSION < 0x040000
    01161             scaleRect = scaleRect.normalize();
    01162 #else
    01163             scaleRect = scaleRect.normalized();
    01164 #endif
    01165         }
    01166     }
    01167
    01168     // +---+-----------+---+
    01169     // |  <-   Axis   ->   |
    01170     // +-^-+-----------+-^-+
    01171     // | | |           | | |
    01172     // |   |           |   |
    01173     // | A |           | A |
    01174     // | x |  Canvas   | x |
    01175     // | i |           | i |
    01176     // | s |           | s |
    01177     // |   |           |   |
    01178     // | | |           | | |
    01179     // +-V-+-----------+-V-+
    01180     // |   <-  Axis   ->   |
    01181     // +---+-----------+---+
    01182
    01183     // The ticks of the axes - not the labels above - should
    01184     // be aligned to the canvas. So we try to use the empty
    01185     // corners to extend the axes, so that the label texts
    01186     // left/right of the min/max ticks are moved into them.
    01187
    01188     alignScales(options, d_data->canvasRect, d_data->scaleRect);
    01189
    01190     if (!d_data->legendRect.isEmpty() )
    01191     {
    01192         // We prefer to align the legend to the canvas - not to
    01193         // the complete plot - if possible.
    01194
    01195         d_data->legendRect = alignLegend(d_data->canvasRect, d_data->legendRect);
    01196     }
    01197 }