qwt_plot_layout.cpp 39 KB

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