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

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