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 }

Generated on Sat May 24 18:47:40 2008 for Qwt User's Guide by  doxygen 1.5.0