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