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