source: ntrip/trunk/BNC/qwt/qwt_plot_layout.cpp@ 9220

Last change on this file since 9220 was 8127, checked in by stoecker, 7 years ago

update qwt and qwtpolar, many QT5 fixes (unfinished)

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