1 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
2  * Qwt Widget Library
3  * Copyright (C) 1997   Josef Wilgen
4  * Copyright (C) 2002   Uwe Rathmann
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the Qwt License, Version 1.0
8  *****************************************************************************/
9 
10 // vim: expandtab
11 
12 #include <qscrollbar.h>
13 #include "qwt_text.h"
14 #include "qwt_text_label.h"
15 #include "qwt_plot_canvas.h"
16 #include "qwt_scale_widget.h"
17 #include "qwt_legend.h"
18 #include "qwt_plot_layout.h"
19 
20 class QwtPlotLayout::LayoutData
21 {
22 public:
23     void init(const QwtPlot *, const QRect &rect);
24 
25     struct t_legendData
26     {
27         int frameWidth;
28         int vScrollBarWidth;
29         int hScrollBarHeight;
30         QSize hint;
31     } legend;
32 
33     struct t_titleData
34     {
35         QwtText text;
36         int frameWidth;
37     } title;
38 
39     struct t_scaleData
40     {
41         bool isEnabled;
42         const QwtScaleWidget *scaleWidget;
43         QFont scaleFont;
44         int start;
45         int end;
46         int baseLineOffset;
47         int tickOffset;
48         int dimWithoutTitle;
49     } scale[QwtPlot::axisCnt];
50 
51     struct t_canvasData
52     {
53         int frameWidth;
54     } canvas;
55 };
56 
57 /*
58   Extract all layout relevant data from the plot components
59 */
60 
init(const QwtPlot * plot,const QRect & rect)61 void QwtPlotLayout::LayoutData::init(const QwtPlot *plot, const QRect &rect)
62 {
63     // legend
64 
65     if ( plot->plotLayout()->legendPosition() != QwtPlot::ExternalLegend
66         && plot->legend() )
67     {
68         legend.frameWidth = plot->legend()->frameWidth();
69         legend.vScrollBarWidth =
70             plot->legend()->verticalScrollBar()->sizeHint().width();
71         legend.hScrollBarHeight =
72             plot->legend()->horizontalScrollBar()->sizeHint().height();
73 
74         const QSize hint = plot->legend()->sizeHint();
75 
76         int w = qwtMin(hint.width(), rect.width());
77         int h = plot->legend()->heightForWidth(w);
78         if ( h == 0 )
79             h = hint.height();
80 
81         if ( h > rect.height() )
82             w += legend.vScrollBarWidth;
83 
84         legend.hint = QSize(w, h);
85     }
86 
87     // title
88 
89     title.frameWidth = 0;
90     title.text = QwtText();
91 
92     if (plot->titleLabel() )
93     {
94         const QwtTextLabel *label = plot->titleLabel();
95         title.text = label->text();
96         if ( !(title.text.testPaintAttribute(QwtText::PaintUsingTextFont)) )
97             title.text.setFont(label->font());
98 
99         title.frameWidth = plot->titleLabel()->frameWidth();
100     }
101 
102     // scales
103 
104     for (int axis = 0; axis < QwtPlot::axisCnt; axis++ )
105     {
106         if ( plot->axisEnabled(axis) )
107         {
108             const QwtScaleWidget *scaleWidget = plot->axisWidget(axis);
109 
110             scale[axis].isEnabled = true;
111 
112             scale[axis].scaleWidget = scaleWidget;
113 
114             scale[axis].scaleFont = scaleWidget->font();
115 
116             scale[axis].start = scaleWidget->startBorderDist();
117             scale[axis].end = scaleWidget->endBorderDist();
118 
119             scale[axis].baseLineOffset = scaleWidget->margin();
120             scale[axis].tickOffset = scaleWidget->margin();
121             if ( scaleWidget->scaleDraw()->hasComponent(
122                 QwtAbstractScaleDraw::Ticks) )
123             {
124                 scale[axis].tickOffset +=
125                     (int)scaleWidget->scaleDraw()->majTickLength();
126             }
127 
128             scale[axis].dimWithoutTitle = scaleWidget->dimForLength(
129                 QWIDGETSIZE_MAX, scale[axis].scaleFont);
130 
131             if ( !scaleWidget->title().isEmpty() )
132             {
133                 scale[axis].dimWithoutTitle -=
134                     scaleWidget->titleHeightForWidth(QWIDGETSIZE_MAX);
135             }
136         }
137         else
138         {
139             scale[axis].isEnabled = false;
140             scale[axis].start = 0;
141             scale[axis].end = 0;
142             scale[axis].baseLineOffset = 0;
143             scale[axis].tickOffset = 0;
144             scale[axis].dimWithoutTitle = 0;
145         }
146     }
147 
148     // canvas
149 
150     canvas.frameWidth = plot->canvas()->frameWidth();
151 }
152 
153 class QwtPlotLayout::PrivateData
154 {
155 public:
PrivateData()156     PrivateData():
157         margin(0),
158         spacing(5),
159         alignCanvasToScales(false)
160     {
161     }
162 
163     QRect titleRect;
164     QRect legendRect;
165     QRect scaleRect[QwtPlot::axisCnt];
166     QRect canvasRect;
167 
168     QwtPlotLayout::LayoutData layoutData;
169 
170     QwtPlot::LegendPosition legendPos;
171     double legendRatio;
172     unsigned int margin;
173     unsigned int spacing;
174     unsigned int canvasMargin[QwtPlot::axisCnt];
175     bool alignCanvasToScales;
176 };
177 
178 /*!
179   \brief Constructor
180  */
181 
QwtPlotLayout()182 QwtPlotLayout::QwtPlotLayout()
183 {
184     d_data = new PrivateData;
185 
186     setLegendPosition(QwtPlot::BottomLegend);
187     setCanvasMargin(4);
188 
189     invalidate();
190 }
191 
192 //! Destructor
~QwtPlotLayout()193 QwtPlotLayout::~QwtPlotLayout()
194 {
195     delete d_data;
196 }
197 
198 /*!
199   Change the margin of the plot. The margin is the space
200   around all components.
201 
202   \param margin new margin
203   \sa margin(), setSpacing(),
204       QwtPlot::setMargin()
205 */
setMargin(int margin)206 void QwtPlotLayout::setMargin(int margin)
207 {
208     if ( margin < 0 )
209         margin = 0;
210     d_data->margin = margin;
211 }
212 
213 /*!
214     \return margin
215     \sa setMargin(), spacing(), QwtPlot::margin()
216 */
margin() const217 int QwtPlotLayout::margin() const
218 {
219     return d_data->margin;
220 }
221 
222 /*!
223   Change a margin of the canvas. The margin is the space
224   above/below the scale ticks. A negative margin will
225   be set to -1, excluding the borders of the scales.
226 
227   \param margin New margin
228   \param axis One of QwtPlot::Axis. Specifies where the position of the margin.
229               -1 means margin at all borders.
230   \sa canvasMargin()
231 
232   \warning The margin will have no effect when alignCanvasToScales is true
233 */
234 
setCanvasMargin(int margin,int axis)235 void QwtPlotLayout::setCanvasMargin(int margin, int axis)
236 {
237     if ( margin < -1 )
238         margin = -1;
239 
240     if ( axis == -1 )
241     {
242         for (axis = 0; axis < QwtPlot::axisCnt; axis++)
243             d_data->canvasMargin[axis] = margin;
244     }
245     else if ( axis >= 0 && axis < QwtPlot::axisCnt )
246         d_data->canvasMargin[axis] = margin;
247 }
248 
249 /*!
250     \return Margin around the scale tick borders
251     \sa setCanvasMargin()
252 */
canvasMargin(int axis) const253 int QwtPlotLayout::canvasMargin(int axis) const
254 {
255     if ( axis < 0 || axis >= QwtPlot::axisCnt )
256         return 0;
257 
258     return d_data->canvasMargin[axis];
259 }
260 
261 /*!
262   Change the align-canvas-to-axis-scales setting. The canvas may:
263   - extend beyond the axis scale ends to maximize its size,
264   - align with the axis scale ends to control its size.
265 
266   \param alignCanvasToScales New align-canvas-to-axis-scales setting
267 
268   \sa setCanvasMargin()
269   \note In this context the term 'scale' means the backbone of a scale.
270   \warning In case of alignCanvasToScales == true canvasMargin will have
271            no effect
272 */
setAlignCanvasToScales(bool alignCanvasToScales)273 void QwtPlotLayout::setAlignCanvasToScales(bool alignCanvasToScales)
274 {
275     d_data->alignCanvasToScales = alignCanvasToScales;
276 }
277 
278 /*!
279   Return the align-canvas-to-axis-scales setting. The canvas may:
280   - extend beyond the axis scale ends to maximize its size
281   - align with the axis scale ends to control its size.
282 
283   \return align-canvas-to-axis-scales setting
284   \sa setAlignCanvasToScales, setCanvasMargin()
285   \note In this context the term 'scale' means the backbone of a scale.
286 */
alignCanvasToScales() const287 bool QwtPlotLayout::alignCanvasToScales() const
288 {
289     return d_data->alignCanvasToScales;
290 }
291 
292 /*!
293   Change the spacing of the plot. The spacing is the distance
294   between the plot components.
295 
296   \param spacing new spacing
297   \sa setMargin(), spacing()
298 */
setSpacing(int spacing)299 void QwtPlotLayout::setSpacing(int spacing)
300 {
301     d_data->spacing = qwtMax(0, spacing);
302 }
303 
304 /*!
305   \return spacing
306   \sa margin(), setSpacing()
307 */
spacing() const308 int QwtPlotLayout::spacing() const
309 {
310     return d_data->spacing;
311 }
312 
313 /*!
314   \brief Specify the position of the legend
315   \param pos The legend's position.
316   \param ratio Ratio between legend and the bounding rect
317                of title, canvas and axes. The legend will be shrinked
318                if it would need more space than the given ratio.
319                The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0
320                it will be reset to the default ratio.
321                The default vertical/horizontal ratio is 0.33/0.5.
322 
323   \sa QwtPlot::setLegendPosition()
324 */
325 
setLegendPosition(QwtPlot::LegendPosition pos,double ratio)326 void QwtPlotLayout::setLegendPosition(QwtPlot::LegendPosition pos, double ratio)
327 {
328     if ( ratio > 1.0 )
329         ratio = 1.0;
330 
331     switch(pos)
332     {
333         case QwtPlot::TopLegend:
334         case QwtPlot::BottomLegend:
335             if ( ratio <= 0.0 )
336                 ratio = 0.33;
337             d_data->legendRatio = ratio;
338             d_data->legendPos = pos;
339             break;
340         case QwtPlot::LeftLegend:
341         case QwtPlot::RightLegend:
342             if ( ratio <= 0.0 )
343                 ratio = 0.5;
344             d_data->legendRatio = ratio;
345             d_data->legendPos = pos;
346             break;
347         case QwtPlot::ExternalLegend:
348             d_data->legendRatio = ratio; // meaningless
349             d_data->legendPos = pos;
350         default:
351             break;
352     }
353 }
354 
355 /*!
356   \brief Specify the position of the legend
357   \param pos The legend's position. Valid values are
358       \c QwtPlot::LeftLegend, \c QwtPlot::RightLegend,
359       \c QwtPlot::TopLegend, \c QwtPlot::BottomLegend.
360 
361   \sa QwtPlot::setLegendPosition()
362 */
setLegendPosition(QwtPlot::LegendPosition pos)363 void QwtPlotLayout::setLegendPosition(QwtPlot::LegendPosition pos)
364 {
365     setLegendPosition(pos, 0.0);
366 }
367 
368 /*!
369   \return Position of the legend
370   \sa setLegendPosition(), QwtPlot::setLegendPosition(),
371       QwtPlot::legendPosition()
372 */
legendPosition() const373 QwtPlot::LegendPosition QwtPlotLayout::legendPosition() const
374 {
375     return d_data->legendPos;
376 }
377 
378 /*!
379   Specify the relative size of the legend in the plot
380   \param ratio Ratio between legend and the bounding rect
381                of title, canvas and axes. The legend will be shrinked
382                if it would need more space than the given ratio.
383                The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0
384                it will be reset to the default ratio.
385                The default vertical/horizontal ratio is 0.33/0.5.
386 */
setLegendRatio(double ratio)387 void QwtPlotLayout::setLegendRatio(double ratio)
388 {
389     setLegendPosition(legendPosition(), ratio);
390 }
391 
392 /*!
393   \return The relative size of the legend in the plot.
394   \sa setLegendPosition()
395 */
legendRatio() const396 double QwtPlotLayout::legendRatio() const
397 {
398     return d_data->legendRatio;
399 }
400 
401 /*!
402   \return Geometry for the title
403   \sa activate(), invalidate()
404 */
405 
titleRect() const406 const QRect &QwtPlotLayout::titleRect() const
407 {
408     return d_data->titleRect;
409 }
410 
411 /*!
412   \return Geometry for the legend
413   \sa activate(), invalidate()
414 */
415 
legendRect() const416 const QRect &QwtPlotLayout::legendRect() const
417 {
418     return d_data->legendRect;
419 }
420 
421 /*!
422   \param axis Axis index
423   \return Geometry for the scale
424   \sa activate(), invalidate()
425 */
426 
scaleRect(int axis) const427 const QRect &QwtPlotLayout::scaleRect(int axis) const
428 {
429     if ( axis < 0 || axis >= QwtPlot::axisCnt )
430     {
431         static QRect dummyRect;
432         return dummyRect;
433     }
434     return d_data->scaleRect[axis];
435 }
436 
437 /*!
438   \return Geometry for the canvas
439   \sa activate(), invalidate()
440 */
441 
canvasRect() const442 const QRect &QwtPlotLayout::canvasRect() const
443 {
444     return d_data->canvasRect;
445 }
446 
447 /*!
448   Invalidate the geometry of all components.
449   \sa activate()
450 */
invalidate()451 void QwtPlotLayout::invalidate()
452 {
453     d_data->titleRect = d_data->legendRect = d_data->canvasRect = QRect();
454     for (int axis = 0; axis < QwtPlot::axisCnt; axis++ )
455         d_data->scaleRect[axis] = QRect();
456 }
457 
458 /*!
459   \brief Return a minimum size hint
460   \sa QwtPlot::minimumSizeHint()
461 */
462 
minimumSizeHint(const QwtPlot * plot) const463 QSize QwtPlotLayout::minimumSizeHint(const QwtPlot *plot) const
464 {
465     class ScaleData
466     {
467     public:
468         ScaleData()
469         {
470             w = h = minLeft = minRight = tickOffset = 0;
471         }
472 
473         int w;
474         int h;
475         int minLeft;
476         int minRight;
477         int tickOffset;
478     } scaleData[QwtPlot::axisCnt];
479 
480     int canvasBorder[QwtPlot::axisCnt];
481 
482     int axis;
483     for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
484     {
485         if ( plot->axisEnabled(axis) )
486         {
487             const QwtScaleWidget *scl = plot->axisWidget(axis);
488             ScaleData &sd = scaleData[axis];
489 
490             const QSize hint = scl->minimumSizeHint();
491             sd.w = hint.width();
492             sd.h = hint.height();
493             scl->getBorderDistHint(sd.minLeft, sd.minRight);
494             sd.tickOffset = scl->margin();
495             if ( scl->scaleDraw()->hasComponent(QwtAbstractScaleDraw::Ticks) )
496                 sd.tickOffset += scl->scaleDraw()->majTickLength();
497         }
498 
499         canvasBorder[axis] = plot->canvas()->frameWidth() +
500             d_data->canvasMargin[axis] + 1;
501 
502     }
503 
504 
505     for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
506     {
507         ScaleData &sd = scaleData[axis];
508         if ( sd.w && (axis == QwtPlot::xBottom || axis == QwtPlot::xTop) )
509         {
510             if ( (sd.minLeft > canvasBorder[QwtPlot::yLeft])
511                 && scaleData[QwtPlot::yLeft].w )
512             {
513                 int shiftLeft = sd.minLeft - canvasBorder[QwtPlot::yLeft];
514                 if ( shiftLeft > scaleData[QwtPlot::yLeft].w )
515                     shiftLeft = scaleData[QwtPlot::yLeft].w;
516 
517                 sd.w -= shiftLeft;
518             }
519             if ( (sd.minRight > canvasBorder[QwtPlot::yRight])
520                 && scaleData[QwtPlot::yRight].w )
521             {
522                 int shiftRight = sd.minRight - canvasBorder[QwtPlot::yRight];
523                 if ( shiftRight > scaleData[QwtPlot::yRight].w )
524                     shiftRight = scaleData[QwtPlot::yRight].w;
525 
526                 sd.w -= shiftRight;
527             }
528         }
529 
530         if ( sd.h && (axis == QwtPlot::yLeft || axis == QwtPlot::yRight) )
531         {
532             if ( (sd.minLeft > canvasBorder[QwtPlot::xBottom]) &&
533                 scaleData[QwtPlot::xBottom].h )
534             {
535                 int shiftBottom = sd.minLeft - canvasBorder[QwtPlot::xBottom];
536                 if ( shiftBottom > scaleData[QwtPlot::xBottom].tickOffset )
537                     shiftBottom = scaleData[QwtPlot::xBottom].tickOffset;
538 
539                 sd.h -= shiftBottom;
540             }
541             if ( (sd.minLeft > canvasBorder[QwtPlot::xTop]) &&
542                 scaleData[QwtPlot::xTop].h )
543             {
544                 int shiftTop = sd.minRight - canvasBorder[QwtPlot::xTop];
545                 if ( shiftTop > scaleData[QwtPlot::xTop].tickOffset )
546                     shiftTop = scaleData[QwtPlot::xTop].tickOffset;
547 
548                 sd.h -= shiftTop;
549             }
550         }
551     }
552 
553     const QwtPlotCanvas *canvas = plot->canvas();
554     const QSize minCanvasSize = canvas->minimumSize();
555 
556     int w = scaleData[QwtPlot::yLeft].w + scaleData[QwtPlot::yRight].w;
557     int cw = qwtMax(scaleData[QwtPlot::xBottom].w, scaleData[QwtPlot::xTop].w)
558         + 2 * (canvas->frameWidth() + 1);
559     w += qwtMax(cw, minCanvasSize.width());
560 
561     int h = scaleData[QwtPlot::xBottom].h + scaleData[QwtPlot::xTop].h;
562     int ch = qwtMax(scaleData[QwtPlot::yLeft].h, scaleData[QwtPlot::yRight].h)
563         + 2 * (canvas->frameWidth() + 1);
564     h += qwtMax(ch, minCanvasSize.height());
565 
566     const QwtTextLabel *title = plot->titleLabel();
567     if (title && !title->text().isEmpty())
568     {
569         // If only QwtPlot::yLeft or QwtPlot::yRight is showing,
570         // we center on the plot canvas.
571         const bool centerOnCanvas = !(plot->axisEnabled(QwtPlot::yLeft)
572             && plot->axisEnabled(QwtPlot::yRight));
573 
574         int titleW = w;
575         if ( centerOnCanvas )
576         {
577             titleW -= scaleData[QwtPlot::yLeft].w
578                 + scaleData[QwtPlot::yRight].w;
579         }
580 
581         int titleH = title->heightForWidth(titleW);
582         if ( titleH > titleW ) // Compensate for a long title
583         {
584             w = titleW = titleH;
585             if ( centerOnCanvas )
586             {
587                 w += scaleData[QwtPlot::yLeft].w
588                     + scaleData[QwtPlot::yRight].w;
589             }
590 
591             titleH = title->heightForWidth(titleW);
592         }
593         h += titleH + d_data->spacing;
594     }
595 
596     // Compute the legend contribution
597 
598     const QwtLegend *legend = plot->legend();
599     if ( d_data->legendPos != QwtPlot::ExternalLegend
600         && legend && !legend->isEmpty() )
601     {
602         if ( d_data->legendPos == QwtPlot::LeftLegend
603             || d_data->legendPos == QwtPlot::RightLegend )
604         {
605             int legendW = legend->sizeHint().width();
606             int legendH = legend->heightForWidth(legendW);
607 
608             if ( legend->frameWidth() > 0 )
609                 w += d_data->spacing;
610 
611             if ( legendH > h )
612                 legendW += legend->verticalScrollBar()->sizeHint().height();
613 
614             if ( d_data->legendRatio < 1.0 )
615                 legendW = qwtMin(legendW, int(w / (1.0 - d_data->legendRatio)));
616 
617             w += legendW;
618         }
619         else // QwtPlot::Top, QwtPlot::Bottom
620         {
621             int legendW = qwtMin(legend->sizeHint().width(), w);
622             int legendH = legend->heightForWidth(legendW);
623 
624             if ( legend->frameWidth() > 0 )
625                 h += d_data->spacing;
626 
627             if ( d_data->legendRatio < 1.0 )
628                 legendH = qwtMin(legendH, int(h / (1.0 - d_data->legendRatio)));
629 
630             h += legendH;
631         }
632     }
633 
634     w += 2 * d_data->margin;
635     h += 2 * d_data->margin;
636 
637     return QSize( w, h );
638 }
639 
640 /*!
641   Find the geometry for the legend
642   \param options Options how to layout the legend
643   \param rect Rectangle where to place the legend
644   \return Geometry for the legend
645   \sa Options
646 */
647 
layoutLegend(int options,const QRect & rect) const648 QRect QwtPlotLayout::layoutLegend(int options,
649     const QRect &rect) const
650 {
651     const QSize hint(d_data->layoutData.legend.hint);
652 
653     int dim;
654     if ( d_data->legendPos == QwtPlot::LeftLegend
655         || d_data->legendPos == QwtPlot::RightLegend )
656     {
657         // We don't allow vertical legends to take more than
658         // half of the available space.
659 
660         dim = qwtMin(hint.width(), int(rect.width() * d_data->legendRatio));
661 
662         if ( !(options & IgnoreScrollbars) )
663         {
664             if ( hint.height() > rect.height() )
665             {
666                 // The legend will need additional
667                 // space for the vertical scrollbar.
668 
669                 dim += d_data->layoutData.legend.vScrollBarWidth;
670             }
671         }
672     }
673     else
674     {
675         dim = qwtMin(hint.height(), int(rect.height() * d_data->legendRatio));
676         dim = qwtMax(dim, d_data->layoutData.legend.hScrollBarHeight);
677     }
678 
679     QRect legendRect = rect;
680     switch(d_data->legendPos)
681     {
682         case QwtPlot::LeftLegend:
683             legendRect.setWidth(dim);
684             break;
685         case QwtPlot::RightLegend:
686             legendRect.setX(rect.right() - dim + 1);
687             legendRect.setWidth(dim);
688             break;
689         case QwtPlot::TopLegend:
690             legendRect.setHeight(dim);
691             break;
692         case QwtPlot::BottomLegend:
693             legendRect.setY(rect.bottom() - dim + 1);
694             legendRect.setHeight(dim);
695             break;
696         case QwtPlot::ExternalLegend:
697             break;
698     }
699 
700     return legendRect;
701 }
702 
703 /*!
704   Align the legend to the canvas
705   \param canvasRect Geometry of the canvas
706   \param legendRect Maximum geometry for the legend
707   \return Geometry for the aligned legend
708 */
alignLegend(const QRect & canvasRect,const QRect & legendRect) const709 QRect QwtPlotLayout::alignLegend(const QRect &canvasRect,
710     const QRect &legendRect) const
711 {
712     QRect alignedRect = legendRect;
713 
714     if ( d_data->legendPos == QwtPlot::BottomLegend
715         || d_data->legendPos == QwtPlot::TopLegend )
716     {
717         if ( d_data->layoutData.legend.hint.width() < canvasRect.width() )
718         {
719             alignedRect.setX(canvasRect.x());
720             alignedRect.setWidth(canvasRect.width());
721         }
722     }
723     else
724     {
725         if ( d_data->layoutData.legend.hint.height() < canvasRect.height() )
726         {
727             alignedRect.setY(canvasRect.y());
728             alignedRect.setHeight(canvasRect.height());
729         }
730     }
731 
732     return alignedRect;
733 }
734 
735 /*!
736   Expand all line breaks in text labels, and calculate the height
737   of their widgets in orientation of the text.
738 
739   \param options Options how to layout the legend
740   \param rect Bounding rect for title, axes and canvas.
741   \param dimTitle Expanded height of the title widget
742   \param dimAxis Expanded heights of the axis in axis orientation.
743 
744   \sa Options
745 */
expandLineBreaks(int options,const QRect & rect,int & dimTitle,int dimAxis[QwtPlot::axisCnt]) const746 void QwtPlotLayout::expandLineBreaks(int options, const QRect &rect,
747     int &dimTitle, int dimAxis[QwtPlot::axisCnt]) const
748 {
749     dimTitle = 0;
750     for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
751         dimAxis[axis] = 0;
752 
753     int backboneOffset[QwtPlot::axisCnt];
754     for (int axis = 0; axis < QwtPlot::axisCnt; axis++ )
755     {
756         backboneOffset[axis] = 0;
757         if ( !d_data->alignCanvasToScales )
758             backboneOffset[axis] += d_data->canvasMargin[axis];
759         if ( !(options & IgnoreFrames) )
760             backboneOffset[axis] += d_data->layoutData.canvas.frameWidth;
761     }
762 
763     bool done = false;
764     while (!done)
765     {
766         done = true;
767 
768         // the size for the 4 axis depend on each other. Expanding
769         // the height of a horizontal axis will shrink the height
770         // for the vertical axis, shrinking the height of a vertical
771         // axis will result in a line break what will expand the
772         // width and results in shrinking the width of a horizontal
773         // axis what might result in a line break of a horizontal
774         // axis ... . So we loop as long until no size changes.
775 
776         if ( !d_data->layoutData.title.text.isEmpty() )
777         {
778             int w = rect.width();
779 
780             if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled
781                 != d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
782             {
783                 // center to the canvas
784                 w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight];
785             }
786 
787             int d = d_data->layoutData.title.text.heightForWidth(w);
788             if ( !(options & IgnoreFrames) )
789                 d += 2 * d_data->layoutData.title.frameWidth;
790 
791             if ( d > dimTitle )
792             {
793                 dimTitle = d;
794                 done = false;
795             }
796         }
797 
798         for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
799         {
800             const struct LayoutData::t_scaleData &scaleData =
801                 d_data->layoutData.scale[axis];
802 
803             if (scaleData.isEnabled)
804             {
805                 int length;
806                 if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom )
807                 {
808                     length = rect.width() - dimAxis[QwtPlot::yLeft]
809                         - dimAxis[QwtPlot::yRight];
810                     length -= scaleData.start + scaleData.end;
811 
812                     if ( dimAxis[QwtPlot::yRight] > 0 )
813                         length -= 1;
814 
815                     length += qwtMin(dimAxis[QwtPlot::yLeft],
816                         scaleData.start - backboneOffset[QwtPlot::yLeft]);
817                     length += qwtMin(dimAxis[QwtPlot::yRight],
818                         scaleData.end - backboneOffset[QwtPlot::yRight]);
819                 }
820                 else // QwtPlot::yLeft, QwtPlot::yRight
821                 {
822                     length = rect.height() - dimAxis[QwtPlot::xTop]
823                         - dimAxis[QwtPlot::xBottom];
824                     length -= scaleData.start + scaleData.end;
825                     length -= 1;
826 
827                     if ( dimAxis[QwtPlot::xBottom] <= 0 )
828                         length -= 1;
829                     if ( dimAxis[QwtPlot::xTop] <= 0 )
830                         length -= 1;
831 
832                     if ( dimAxis[QwtPlot::xBottom] > 0 )
833                     {
834                         length += qwtMin(
835                             d_data->layoutData.scale[QwtPlot::xBottom].tickOffset,
836                             scaleData.start - backboneOffset[QwtPlot::xBottom]);
837                     }
838                     if ( dimAxis[QwtPlot::xTop] > 0 )
839                     {
840                         length += qwtMin(
841                             d_data->layoutData.scale[QwtPlot::xTop].tickOffset,
842                             scaleData.end - backboneOffset[QwtPlot::xTop]);
843                     }
844 
845                     if ( dimTitle > 0 )
846                         length -= dimTitle + d_data->spacing;
847                 }
848 
849                 int d = scaleData.dimWithoutTitle;
850                 if ( !scaleData.scaleWidget->title().isEmpty() )
851                 {
852                     d += scaleData.scaleWidget->titleHeightForWidth(length);
853                 }
854 
855 
856                 if ( d > dimAxis[axis] )
857                 {
858                     dimAxis[axis] = d;
859                     done = false;
860                 }
861             }
862         }
863     }
864 }
865 
866 /*!
867   Align the ticks of the axis to the canvas borders using
868   the empty corners.
869 
870   \sa Options
871 */
alignScales(int options,QRect & canvasRect,QRect scaleRect[QwtPlot::axisCnt]) const872 void QwtPlotLayout::alignScales( int options, QRect &canvasRect, QRect scaleRect[QwtPlot::axisCnt] ) const
873 {
874 	int backboneOffset[QwtPlot::axisCnt];
875 	for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
876 	{
877 		backboneOffset[axis] = 0;
878 		if ( !d_data->alignCanvasToScales )
879 			backboneOffset[axis] += d_data->canvasMargin[axis];
880 		if ( !( options & IgnoreFrames ) )
881 			backboneOffset[axis] += d_data->layoutData.canvas.frameWidth;
882 	}
883 
884 	for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
885 	{
886 		if ( !scaleRect[axis].isValid() )
887 			continue;
888 
889 		const int startDist = d_data->layoutData.scale[axis].start;
890 		const int endDist = d_data->layoutData.scale[axis].end;
891 
892 		QRect &axisRect = scaleRect[axis];
893 
894 		if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom )
895 		{
896 			const QRect &leftScaleRect = scaleRect[QwtPlot::yLeft];
897 			const int leftOffset =
898 				backboneOffset[QwtPlot::yLeft] - startDist;
899 
900 			if ( leftScaleRect.isValid() )
901 			{
902 				const int dx = leftOffset + leftScaleRect.width();
903 				if ( d_data->alignCanvasToScales && dx < 0 )
904 				{
905 					/*
906 					  The axis needs more space than the width
907 					  of the left scale.
908 					 */
909 					canvasRect.setLeft( qMax( canvasRect.left(),
910 						axisRect.left() - dx ) );
911 				}
912 				else
913 				{
914 					const double minLeft = leftScaleRect.left();
915 					const double left = axisRect.left() + leftOffset;
916 					axisRect.setLeft( qMax( left, minLeft ) );
917 				}
918 			}
919 			else
920 			{
921 				if ( d_data->alignCanvasToScales && leftOffset < 0 )
922 				{
923 					canvasRect.setLeft( qMax( canvasRect.left(),
924 						axisRect.left() - leftOffset ) );
925 				}
926 				else
927 				{
928 					if ( leftOffset > 0 )
929 						axisRect.setLeft( axisRect.left() + leftOffset );
930 				}
931 			}
932 
933 			const QRect &rightScaleRect = scaleRect[QwtPlot::yRight];
934 			const int rightOffset =
935 				backboneOffset[QwtPlot::yRight] - endDist + 1;
936 
937 			if ( rightScaleRect.isValid() )
938 			{
939 				const int dx = rightOffset + rightScaleRect.width();
940 				if ( d_data->alignCanvasToScales && dx < 0 )
941 				{
942 					/*
943 					  The axis needs more space than the width
944 					  of the right scale.
945 					 */
946 					canvasRect.setRight( qMin( canvasRect.right(),
947 						axisRect.right() + dx ) );
948 				}
949 
950 				const double maxRight = rightScaleRect.right();
951 				const double right = axisRect.right() - rightOffset;
952 				axisRect.setRight( qMin( right, maxRight ) );
953 			}
954 			else
955 			{
956 				if ( d_data->alignCanvasToScales && rightOffset < 0 )
957 				{
958 					canvasRect.setRight( qMin( canvasRect.right(),
959 						axisRect.right() + rightOffset ) );
960 				}
961 				else
962 				{
963 					if ( rightOffset > 0 )
964 						axisRect.setRight( axisRect.right() - rightOffset );
965 				}
966 			}
967 		}
968 		else // QwtPlot::yLeft, QwtPlot::yRight
969 		{
970 			const QRect &bottomScaleRect = scaleRect[QwtPlot::xBottom];
971 			const int bottomOffset =
972 				backboneOffset[QwtPlot::xBottom] - endDist + 1;
973 
974 			if ( bottomScaleRect.isValid() )
975 			{
976 				const int dy = bottomOffset + bottomScaleRect.height();
977 				if ( d_data->alignCanvasToScales && dy < 0 )
978 				{
979 					/*
980 					  The axis needs more space than the height
981 					  of the bottom scale.
982 					 */
983 					canvasRect.setBottom( qMin( canvasRect.bottom(),
984 						axisRect.bottom() + dy ) );
985 				}
986 				else
987 				{
988 					const double maxBottom = bottomScaleRect.top() +
989 						d_data->layoutData.scale[QwtPlot::xBottom].tickOffset;
990 					const double bottom = axisRect.bottom() - bottomOffset;
991 					axisRect.setBottom( qMin( bottom, maxBottom ) );
992 				}
993 			}
994 			else
995 			{
996 				if ( d_data->alignCanvasToScales && bottomOffset < 0 )
997 				{
998 					canvasRect.setBottom( qMin( canvasRect.bottom(),
999 						axisRect.bottom() + bottomOffset ) );
1000 				}
1001 				else
1002 				{
1003 					if ( bottomOffset > 0 )
1004 						axisRect.setBottom( axisRect.bottom() - bottomOffset );
1005 				}
1006 			}
1007 
1008 			const QRect &topScaleRect = scaleRect[QwtPlot::xTop];
1009 			const int topOffset = backboneOffset[QwtPlot::xTop] - startDist;
1010 
1011 			if ( topScaleRect.isValid() )
1012 			{
1013 				const int dy = topOffset + topScaleRect.height();
1014 				if ( d_data->alignCanvasToScales && dy < 0 )
1015 				{
1016 					/*
1017 					  The axis needs more space than the height
1018 					  of the top scale.
1019 					 */
1020 					canvasRect.setTop( qMax( canvasRect.top(),
1021 						axisRect.top() - dy ) );
1022 				}
1023 				else
1024 				{
1025 					const double minTop = topScaleRect.bottom() -
1026 						d_data->layoutData.scale[QwtPlot::xTop].tickOffset;
1027 					const double top = axisRect.top() + topOffset;
1028 					axisRect.setTop( qMax( top, minTop ) );
1029 				}
1030 			}
1031 			else
1032 			{
1033 				if ( d_data->alignCanvasToScales && topOffset < 0 )
1034 				{
1035 					canvasRect.setTop( qMax( canvasRect.top(),
1036 						axisRect.top() - topOffset ) );
1037 				}
1038 				else
1039 				{
1040 					if ( topOffset > 0 )
1041 						axisRect.setTop( axisRect.top() + topOffset );
1042 				}
1043 			}
1044 		}
1045 	}
1046 
1047 	if ( d_data->alignCanvasToScales )
1048 	{
1049 		/*
1050 		  The canvas has been aligned to the scale with largest
1051 		  border distances. Now we have to realign the other scale.
1052 		 */
1053 
1054 		int fw = 0;
1055 		if ( !( options & IgnoreFrames ) )
1056 			fw = d_data->layoutData.canvas.frameWidth;
1057 
1058 		for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
1059 		{
1060 			if ( !scaleRect[axis].isValid() )
1061 				continue;
1062 
1063 			if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop )
1064 			{
1065 				scaleRect[axis].setLeft( canvasRect.left() + fw
1066 					- d_data->layoutData.scale[axis].start );
1067 				scaleRect[axis].setRight( canvasRect.right() - fw - 1
1068 					+ d_data->layoutData.scale[axis].end );
1069 			}
1070 			else
1071 			{
1072 				scaleRect[axis].setTop( canvasRect.top() + fw
1073 					- d_data->layoutData.scale[axis].start );
1074 				scaleRect[axis].setBottom( canvasRect.bottom() - fw - 1
1075 					+ d_data->layoutData.scale[axis].end );
1076 			}
1077 		}
1078 
1079 		if ( scaleRect[QwtPlot::xTop].isValid() )
1080 			scaleRect[QwtPlot::xTop].setBottom( canvasRect.top() - 1);
1081 		if ( scaleRect[QwtPlot::xBottom].isValid() )
1082 			scaleRect[QwtPlot::xBottom].setTop( canvasRect.bottom() + 1);
1083 		if ( scaleRect[QwtPlot::yLeft].isValid() )
1084 			scaleRect[QwtPlot::yLeft].setRight( canvasRect.left() - 1);
1085 		if ( scaleRect[QwtPlot::yRight].isValid() )
1086 			scaleRect[QwtPlot::yRight].setLeft( canvasRect.right() + 1);
1087 	}
1088 }
1089 
1090 /*!
1091   \brief Recalculate the geometry of all components.
1092 
1093   \param plot Plot to be layout
1094   \param plotRect Rect where to place the components
1095   \param options Options
1096 
1097   \sa invalidate(), Options, titleRect(),
1098       legendRect(), scaleRect(), canvasRect()
1099 */
activate(const QwtPlot * plot,const QRect & plotRect,int options)1100 void QwtPlotLayout::activate(const QwtPlot *plot,
1101     const QRect &plotRect, int options)
1102 {
1103     invalidate();
1104 
1105     QRect rect(plotRect);  // undistributed rest of the plot rect
1106 
1107     if ( !(options & IgnoreMargin) )
1108     {
1109         // subtract the margin
1110 
1111         rect.setRect(
1112             rect.x() + d_data->margin,
1113             rect.y() + d_data->margin,
1114             rect.width() - 2 * d_data->margin,
1115             rect.height() - 2 * d_data->margin
1116         );
1117     }
1118 
1119     // We extract all layout relevant data from the widgets,
1120     // filter them through pfilter and save them to d_data->layoutData.
1121 
1122     d_data->layoutData.init(plot, rect);
1123 
1124     if (!(options & IgnoreLegend)
1125         && d_data->legendPos != QwtPlot::ExternalLegend
1126         && plot->legend() && !plot->legend()->isEmpty() )
1127     {
1128         d_data->legendRect = layoutLegend(options, rect);
1129 
1130         // subtract d_data->legendRect from rect
1131 
1132         const QRegion region(rect);
1133         rect = region.subtract(d_data->legendRect).boundingRect();
1134 
1135         if ( d_data->layoutData.legend.frameWidth &&
1136             !(options & IgnoreFrames ) )
1137         {
1138             // In case of a frame we have to insert a spacing.
1139             // Otherwise the leading of the font separates
1140             // legend and scale/canvas
1141 
1142             switch(d_data->legendPos)
1143             {
1144                 case QwtPlot::LeftLegend:
1145                     rect.setLeft(rect.left() + d_data->spacing);
1146                     break;
1147                 case QwtPlot::RightLegend:
1148                     rect.setRight(rect.right() - d_data->spacing);
1149                     break;
1150                 case QwtPlot::TopLegend:
1151                     rect.setTop(rect.top() + d_data->spacing);
1152                     break;
1153                 case QwtPlot::BottomLegend:
1154                     rect.setBottom(rect.bottom() - d_data->spacing);
1155                     break;
1156                 case QwtPlot::ExternalLegend:
1157                     break; // suppress compiler warning
1158             }
1159         }
1160     }
1161 
1162 #ifdef __GNUC__
1163 #endif
1164     /*
1165      +---+-----------+---+
1166      |       Title       |
1167      +---+-----------+---+
1168      |   |   Axis    |   |
1169      +---+-----------+---+
1170      | A |           | A |
1171      | x |  Canvas   | x |
1172      | i |           | i |
1173      | s |           | s |
1174      +---+-----------+---+
1175      |   |   Axis    |   |
1176      +---+-----------+---+
1177     */
1178 
1179     // axes and title include text labels. The height of each
1180     // label depends on its line breaks, that depend on the width
1181     // for the label. A line break in a horizontal text will reduce
1182     // the available width for vertical texts and vice versa.
1183     // expandLineBreaks finds the height/width for title and axes
1184     // including all line breaks.
1185 
1186     int dimTitle, dimAxes[QwtPlot::axisCnt];
1187     expandLineBreaks(options, rect, dimTitle, dimAxes);
1188 
1189     if (dimTitle > 0 )
1190     {
1191         d_data->titleRect = QRect(rect.x(), rect.y(),
1192             rect.width(), dimTitle);
1193 
1194         if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled !=
1195             d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
1196         {
1197             // if only one of the y axes is missing we align
1198             // the title centered to the canvas
1199 
1200             d_data->titleRect.setX(rect.x() + dimAxes[QwtPlot::yLeft]);
1201             d_data->titleRect.setWidth(rect.width()
1202                 - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight]);
1203         }
1204 
1205         // subtract title
1206         rect.setTop(rect.top() + dimTitle + d_data->spacing);
1207     }
1208 
1209     d_data->canvasRect.setRect(
1210         rect.x() + dimAxes[QwtPlot::yLeft],
1211         rect.y() + dimAxes[QwtPlot::xTop],
1212         rect.width() - dimAxes[QwtPlot::yRight] - dimAxes[QwtPlot::yLeft],
1213         rect.height() - dimAxes[QwtPlot::xBottom] - dimAxes[QwtPlot::xTop]);
1214 
1215     for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
1216     {
1217         // set the rects for the axes
1218 
1219         if ( dimAxes[axis] )
1220         {
1221             int dim = dimAxes[axis];
1222             QRect &scaleRect = d_data->scaleRect[axis];
1223 
1224             scaleRect = d_data->canvasRect;
1225             switch(axis)
1226             {
1227                 case QwtPlot::yLeft:
1228                     scaleRect.setX(d_data->canvasRect.left() - dim);
1229                     scaleRect.setWidth(dim);
1230                     break;
1231                 case QwtPlot::yRight:
1232                     scaleRect.setX(d_data->canvasRect.right() + 1);
1233                     scaleRect.setWidth(dim);
1234                     break;
1235                 case QwtPlot::xBottom:
1236                     scaleRect.setY(d_data->canvasRect.bottom() + 1);
1237                     scaleRect.setHeight(dim);
1238                     break;
1239                 case QwtPlot::xTop:
1240                     scaleRect.setY(d_data->canvasRect.top() - dim);
1241                     scaleRect.setHeight(dim);
1242                     break;
1243             }
1244 #if QT_VERSION < 0x040000
1245             scaleRect = scaleRect.normalize();
1246 #else
1247             scaleRect = scaleRect.normalized();
1248 #endif
1249         }
1250     }
1251 
1252     // +---+-----------+---+
1253     // |  <-   Axis   ->   |
1254     // +-^-+-----------+-^-+
1255     // | | |           | | |
1256     // |   |           |   |
1257     // | A |           | A |
1258     // | x |  Canvas   | x |
1259     // | i |           | i |
1260     // | s |           | s |
1261     // |   |           |   |
1262     // | | |           | | |
1263     // +-V-+-----------+-V-+
1264     // |   <-  Axis   ->   |
1265     // +---+-----------+---+
1266 
1267     // The ticks of the axes - not the labels above - should
1268     // be aligned to the canvas. So we try to use the empty
1269     // corners to extend the axes, so that the label texts
1270     // left/right of the min/max ticks are moved into them.
1271 
1272     alignScales(options, d_data->canvasRect, d_data->scaleRect);
1273 
1274     if (!d_data->legendRect.isEmpty() )
1275     {
1276         // We prefer to align the legend to the canvas - not to
1277         // the complete plot - if possible.
1278 
1279         d_data->legendRect = alignLegend(d_data->canvasRect, d_data->legendRect);
1280     }
1281 }
1282