GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: plugins/pyqcustomplot/qcustomplot.cpp Lines: 0 9132 0.0 %
Date: 2023-03-14 11:04:37 Branches: 0 14589 0.0 %

Line Branch Exec Source
1
/***************************************************************************
2
**                                                                        **
3
**  QCustomPlot, an easy to use, modern plotting widget for Qt            **
4
**  Copyright (C) 2011-2015 Emanuel Eichhammer                            **
5
**                                                                        **
6
**  This program is free software: you can redistribute it and/or modify  **
7
**  it under the terms of the GNU General Public License as published by  **
8
**  the Free Software Foundation, either version 3 of the License, or     **
9
**  (at your option) any later version.                                   **
10
**                                                                        **
11
**  This program is distributed in the hope that it will be useful,       **
12
**  but WITHOUT ANY WARRANTY; without even the implied warranty of        **
13
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         **
14
**  GNU General Public License for more details.                          **
15
**                                                                        **
16
**  You should have received a copy of the GNU General Public License     **
17
**  along with this program.  If not, see http://www.gnu.org/licenses/.   **
18
**                                                                        **
19
****************************************************************************
20
**           Author: Emanuel Eichhammer                                   **
21
**  Website/Contact: http://www.qcustomplot.com/                          **
22
**             Date: 22.12.15                                             **
23
**          Version: 1.3.2                                                **
24
****************************************************************************/
25
26
#include "qcustomplot.h"
27
28
////////////////////////////////////////////////////////////////////////////////////////////////////
29
//////////////////// QCPPainter
30
////////////////////////////////////////////////////////////////////////////////////////////////////
31
32
/*! \class QCPPainter
33
  \brief QPainter subclass used internally
34
35
  This QPainter subclass is used to provide some extended functionality e.g. for
36
  tweaking position consistency between antialiased and non-antialiased
37
  painting. Further it provides workarounds for QPainter quirks.
38
39
  \warning This class intentionally hides non-virtual functions of QPainter,
40
  e.g. setPen, save and restore. So while it is possible to pass a QCPPainter
41
  instance to a function that expects a QPainter pointer, some of the
42
  workarounds and tweaks will be unavailable to the function (because it will
43
  call the base class implementations of the functions actually hidden by
44
  QCPPainter).
45
*/
46
47
/*!
48
  Creates a new QCPPainter instance and sets default values
49
*/
50
QCPPainter::QCPPainter()
51
    : QPainter(), mModes(pmDefault), mIsAntialiasing(false) {
52
  // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter
53
  // isn't active yet and a call to begin() will follow
54
}
55
56
/*!
57
  Creates a new QCPPainter instance on the specified paint \a device and sets
58
  default values. Just like the analogous QPainter constructor, begins painting
59
  on \a device immediately.
60
61
  Like \ref begin, this method sets QPainter::NonCosmeticDefaultPen in Qt
62
  versions before Qt5.
63
*/
64
QCPPainter::QCPPainter(QPaintDevice *device)
65
    : QPainter(device), mModes(pmDefault), mIsAntialiasing(false) {
66
#if QT_VERSION <           \
67
    QT_VERSION_CHECK(5, 0, \
68
                     0)  // before Qt5, default pens used to be cosmetic if
69
                         // NonCosmeticDefaultPen flag isn't set. So we set it
70
                         // to get consistency across Qt versions.
71
  if (isActive()) setRenderHint(QPainter::NonCosmeticDefaultPen);
72
#endif
73
}
74
75
QCPPainter::~QCPPainter() {}
76
77
/*!
78
  Sets the pen of the painter and applies certain fixes to it, depending on the
79
  mode of this QCPPainter.
80
81
  \note this function hides the non-virtual base class implementation.
82
*/
83
void QCPPainter::setPen(const QPen &pen) {
84
  QPainter::setPen(pen);
85
  if (mModes.testFlag(pmNonCosmetic)) makeNonCosmetic();
86
}
87
88
/*! \overload
89
90
  Sets the pen (by color) of the painter and applies certain fixes to it,
91
  depending on the mode of this QCPPainter.
92
93
  \note this function hides the non-virtual base class implementation.
94
*/
95
void QCPPainter::setPen(const QColor &color) {
96
  QPainter::setPen(color);
97
  if (mModes.testFlag(pmNonCosmetic)) makeNonCosmetic();
98
}
99
100
/*! \overload
101
102
  Sets the pen (by style) of the painter and applies certain fixes to it,
103
  depending on the mode of this QCPPainter.
104
105
  \note this function hides the non-virtual base class implementation.
106
*/
107
void QCPPainter::setPen(Qt::PenStyle penStyle) {
108
  QPainter::setPen(penStyle);
109
  if (mModes.testFlag(pmNonCosmetic)) makeNonCosmetic();
110
}
111
112
/*! \overload
113
114
  Works around a Qt bug introduced with Qt 4.8 which makes drawing QLineF
115
  unpredictable when antialiasing is disabled. Thus when antialiasing is
116
  disabled, it rounds the \a line to integer coordinates and then passes it to
117
  the original drawLine.
118
119
  \note this function hides the non-virtual base class implementation.
120
*/
121
void QCPPainter::drawLine(const QLineF &line) {
122
  if (mIsAntialiasing || mModes.testFlag(pmVectorized))
123
    QPainter::drawLine(line);
124
  else
125
    QPainter::drawLine(line.toLine());
126
}
127
128
/*!
129
  Sets whether painting uses antialiasing or not. Use this method instead of
130
  using setRenderHint with QPainter::Antialiasing directly, as it allows
131
  QCPPainter to regain pixel exactness between antialiased and non-antialiased
132
  painting (Since Qt < 5.0 uses slightly different coordinate systems for
133
  AA/Non-AA painting).
134
*/
135
void QCPPainter::setAntialiasing(bool enabled) {
136
  setRenderHint(QPainter::Antialiasing, enabled);
137
  if (mIsAntialiasing != enabled) {
138
    mIsAntialiasing = enabled;
139
    if (!mModes.testFlag(pmVectorized))  // antialiasing half-pixel shift only
140
                                         // needed for rasterized outputs
141
    {
142
      if (mIsAntialiasing)
143
        translate(0.5, 0.5);
144
      else
145
        translate(-0.5, -0.5);
146
    }
147
  }
148
}
149
150
/*!
151
  Sets the mode of the painter. This controls whether the painter shall adjust
152
  its fixes/workarounds optimized for certain output devices.
153
*/
154
void QCPPainter::setModes(QCPPainter::PainterModes modes) { mModes = modes; }
155
156
/*!
157
  Sets the QPainter::NonCosmeticDefaultPen in Qt versions before Qt5 after
158
  beginning painting on \a device. This is necessary to get cosmetic pen
159
  consistency across Qt versions, because since Qt5, all pens are non-cosmetic
160
  by default, and in Qt4 this render hint must be set to get that behaviour.
161
162
  The Constructor \ref QCPPainter(QPaintDevice *device) which directly starts
163
  painting also sets the render hint as appropriate.
164
165
  \note this function hides the non-virtual base class implementation.
166
*/
167
bool QCPPainter::begin(QPaintDevice *device) {
168
  bool result = QPainter::begin(device);
169
#if QT_VERSION <           \
170
    QT_VERSION_CHECK(5, 0, \
171
                     0)  // before Qt5, default pens used to be cosmetic if
172
                         // NonCosmeticDefaultPen flag isn't set. So we set it
173
                         // to get consistency across Qt versions.
174
  if (result) setRenderHint(QPainter::NonCosmeticDefaultPen);
175
#endif
176
  return result;
177
}
178
179
/*! \overload
180
181
  Sets the mode of the painter. This controls whether the painter shall adjust
182
  its fixes/workarounds optimized for certain output devices.
183
*/
184
void QCPPainter::setMode(QCPPainter::PainterMode mode, bool enabled) {
185
  if (!enabled && mModes.testFlag(mode))
186
    mModes &= ~mode;
187
  else if (enabled && !mModes.testFlag(mode))
188
    mModes |= mode;
189
}
190
191
/*!
192
  Saves the painter (see QPainter::save). Since QCPPainter adds some new
193
  internal state to QPainter, the save/restore functions are reimplemented to
194
  also save/restore those members.
195
196
  \note this function hides the non-virtual base class implementation.
197
198
  \see restore
199
*/
200
void QCPPainter::save() {
201
  mAntialiasingStack.push(mIsAntialiasing);
202
  QPainter::save();
203
}
204
205
/*!
206
  Restores the painter (see QPainter::restore). Since QCPPainter adds some new
207
  internal state to QPainter, the save/restore functions are reimplemented to
208
  also save/restore those members.
209
210
  \note this function hides the non-virtual base class implementation.
211
212
  \see save
213
*/
214
void QCPPainter::restore() {
215
  if (!mAntialiasingStack.isEmpty())
216
    mIsAntialiasing = mAntialiasingStack.pop();
217
  else
218
    qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
219
  QPainter::restore();
220
}
221
222
/*!
223
  Changes the pen width to 1 if it currently is 0. This function is called in
224
  the \ref setPen overrides when the \ref pmNonCosmetic mode is set.
225
*/
226
void QCPPainter::makeNonCosmetic() {
227
  if (qFuzzyIsNull(pen().widthF())) {
228
    QPen p = pen();
229
    p.setWidth(1);
230
    QPainter::setPen(p);
231
  }
232
}
233
234
////////////////////////////////////////////////////////////////////////////////////////////////////
235
//////////////////// QCPScatterStyle
236
////////////////////////////////////////////////////////////////////////////////////////////////////
237
238
/*! \class QCPScatterStyle
239
  \brief Represents the visual appearance of scatter points
240
241
  This class holds information about shape, color and size of scatter points. In
242
  plottables like QCPGraph it is used to store how scatter points shall be
243
  drawn. For example, \ref QCPGraph::setScatterStyle takes a QCPScatterStyle
244
  instance.
245
246
  A scatter style consists of a shape (\ref setShape), a line color (\ref
247
  setPen) and possibly a fill (\ref setBrush), if the shape provides a fillable
248
  area. Further, the size of the shape can be controlled with \ref setSize.
249
250
  \section QCPScatterStyle-defining Specifying a scatter style
251
252
  You can set all these configurations either by calling the respective
253
  functions on an instance: \snippet
254
  documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-creation-1
255
256
  Or you can use one of the various constructors that take different parameter
257
  combinations, making it easy to specify a scatter style in a single call, like
258
  so: \snippet documentation/doc-code-snippets/mainwindow.cpp
259
  qcpscatterstyle-creation-2
260
261
  \section QCPScatterStyle-undefinedpen Leaving the color/pen up to the
262
  plottable
263
264
  There are two constructors which leave the pen undefined: \ref
265
  QCPScatterStyle() and \ref QCPScatterStyle(ScatterShape shape, double size).
266
  If those constructors are used, a call to \ref isPenDefined will return false.
267
  It leads to scatter points that inherit the pen from the plottable that uses
268
  the scatter style. Thus, if such a scatter style is passed to QCPGraph, the
269
  line color of the graph (\ref QCPGraph::setPen) will be used by the scatter
270
  points. This makes it very convenient to set up typical scatter settings:
271
272
  \snippet documentation/doc-code-snippets/mainwindow.cpp
273
  qcpscatterstyle-shortcreation
274
275
  Notice that it wasn't even necessary to explicitly call a QCPScatterStyle
276
  constructor. This works because QCPScatterStyle provides a constructor that
277
  can transform a \ref ScatterShape directly into a QCPScatterStyle instance
278
  (that's the \ref QCPScatterStyle(ScatterShape shape, double size) constructor
279
  with a default for \a size). In those cases, C++ allows directly supplying a
280
  \ref ScatterShape, where actually a QCPScatterStyle is expected.
281
282
  \section QCPScatterStyle-custompath-and-pixmap Custom shapes and pixmaps
283
284
  QCPScatterStyle supports drawing custom shapes and arbitrary pixmaps as
285
  scatter points.
286
287
  For custom shapes, you can provide a QPainterPath with the desired shape to
288
  the \ref setCustomPath function or call the constructor that takes a painter
289
  path. The scatter shape will automatically be set to \ref ssCustom.
290
291
  For pixmaps, you call \ref setPixmap with the desired QPixmap. Alternatively
292
  you can use the constructor that takes a QPixmap. The scatter shape will
293
  automatically be set to \ref ssPixmap. Note that \ref setSize does not
294
  influence the appearance of the pixmap.
295
*/
296
297
/* start documentation of inline functions */
298
299
/*! \fn bool QCPScatterStyle::isNone() const
300
301
  Returns whether the scatter shape is \ref ssNone.
302
303
  \see setShape
304
*/
305
306
/*! \fn bool QCPScatterStyle::isPenDefined() const
307
308
  Returns whether a pen has been defined for this scatter style.
309
310
  The pen is undefined if a constructor is called that does not carry \a pen as
311
  parameter. Those are \ref QCPScatterStyle() and \ref
312
  QCPScatterStyle(ScatterShape shape, double size). If the pen is left
313
  undefined, the scatter color will be inherited from the plottable that uses
314
  this scatter style.
315
316
  \see setPen
317
*/
318
319
/* end documentation of inline functions */
320
321
/*!
322
  Creates a new QCPScatterStyle instance with size set to 6. No shape, pen or
323
  brush is defined.
324
325
  Since the pen is undefined (\ref isPenDefined returns false), the scatter
326
  color will be inherited from the plottable that uses this scatter style.
327
*/
328
QCPScatterStyle::QCPScatterStyle()
329
    : mSize(6),
330
      mShape(ssNone),
331
      mPen(Qt::NoPen),
332
      mBrush(Qt::NoBrush),
333
      mPenDefined(false) {}
334
335
/*!
336
  Creates a new QCPScatterStyle instance with shape set to \a shape and size to
337
  \a size. No pen or brush is defined.
338
339
  Since the pen is undefined (\ref isPenDefined returns false), the scatter
340
  color will be inherited from the plottable that uses this scatter style.
341
*/
342
QCPScatterStyle::QCPScatterStyle(ScatterShape shape, double size)
343
    : mSize(size),
344
      mShape(shape),
345
      mPen(Qt::NoPen),
346
      mBrush(Qt::NoBrush),
347
      mPenDefined(false) {}
348
349
/*!
350
  Creates a new QCPScatterStyle instance with shape set to \a shape, the pen
351
  color set to \a color, and size to \a size. No brush is defined, i.e. the
352
  scatter point will not be filled.
353
*/
354
QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color,
355
                                 double size)
356
    : mSize(size),
357
      mShape(shape),
358
      mPen(QPen(color)),
359
      mBrush(Qt::NoBrush),
360
      mPenDefined(true) {}
361
362
/*!
363
  Creates a new QCPScatterStyle instance with shape set to \a shape, the pen
364
  color set to \a color, the brush color to \a fill (with a solid pattern), and
365
  size to \a size.
366
*/
367
QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color,
368
                                 const QColor &fill, double size)
369
    : mSize(size),
370
      mShape(shape),
371
      mPen(QPen(color)),
372
      mBrush(QBrush(fill)),
373
      mPenDefined(true) {}
374
375
/*!
376
  Creates a new QCPScatterStyle instance with shape set to \a shape, the pen set
377
  to \a pen, the brush to \a brush, and size to \a size.
378
379
  \warning In some cases it might be tempting to directly use a pen style like
380
  <tt>Qt::NoPen</tt> as \a pen and a color like <tt>Qt::blue</tt> as \a brush.
381
  Notice however, that the corresponding call\n
382
  <tt>QCPScatterStyle(QCPScatterShape::ssCircle, Qt::NoPen, Qt::blue, 5)</tt>\n
383
  doesn't necessarily lead C++ to use this constructor in some cases, but might
384
  mistake <tt>Qt::NoPen</tt> for a QColor and use the \ref
385
  QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill,
386
  double size) constructor instead (which will lead to an unexpected look of the
387
  scatter points). To prevent this, be more explicit with the parameter types.
388
  For example, use <tt>QBrush(Qt::blue)</tt> instead of just <tt>Qt::blue</tt>,
389
  to clearly point out to the compiler that this constructor is wanted.
390
*/
391
QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen,
392
                                 const QBrush &brush, double size)
393
    : mSize(size),
394
      mShape(shape),
395
      mPen(pen),
396
      mBrush(brush),
397
      mPenDefined(pen.style() != Qt::NoPen) {}
398
399
/*!
400
  Creates a new QCPScatterStyle instance which will show the specified \a
401
  pixmap. The scatter shape is set to \ref ssPixmap.
402
*/
403
QCPScatterStyle::QCPScatterStyle(const QPixmap &pixmap)
404
    : mSize(5),
405
      mShape(ssPixmap),
406
      mPen(Qt::NoPen),
407
      mBrush(Qt::NoBrush),
408
      mPixmap(pixmap),
409
      mPenDefined(false) {}
410
411
/*!
412
  Creates a new QCPScatterStyle instance with a custom shape that is defined via
413
  \a customPath. The scatter shape is set to \ref ssCustom.
414
415
  The custom shape line will be drawn with \a pen and filled with \a brush. The
416
  size has a slightly different meaning than for built-in scatter points: The
417
  custom path will be drawn scaled by a factor of \a size/6.0. Since the default
418
  \a size is 6, the custom path will appear at a its natural size by default. To
419
  double the size of the path for example, set \a size to 12.
420
*/
421
QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath,
422
                                 const QPen &pen, const QBrush &brush,
423
                                 double size)
424
    : mSize(size),
425
      mShape(ssCustom),
426
      mPen(pen),
427
      mBrush(brush),
428
      mCustomPath(customPath),
429
      mPenDefined(pen.style() != Qt::NoPen) {}
430
431
/*!
432
  Sets the size (pixel diameter) of the drawn scatter points to \a size.
433
434
  \see setShape
435
*/
436
void QCPScatterStyle::setSize(double size) { mSize = size; }
437
438
/*!
439
  Sets the shape to \a shape.
440
441
  Note that the calls \ref setPixmap and \ref setCustomPath automatically set
442
  the shape to \ref ssPixmap and \ref ssCustom, respectively.
443
444
  \see setSize
445
*/
446
void QCPScatterStyle::setShape(QCPScatterStyle::ScatterShape shape) {
447
  mShape = shape;
448
}
449
450
/*!
451
  Sets the pen that will be used to draw scatter points to \a pen.
452
453
  If the pen was previously undefined (see \ref isPenDefined), the pen is
454
  considered defined after a call to this function, even if \a pen is
455
  <tt>Qt::NoPen</tt>.
456
457
  \see setBrush
458
*/
459
void QCPScatterStyle::setPen(const QPen &pen) {
460
  mPenDefined = true;
461
  mPen = pen;
462
}
463
464
/*!
465
  Sets the brush that will be used to fill scatter points to \a brush. Note that
466
  not all scatter shapes have fillable areas. For example, \ref ssPlus does not
467
  while \ref ssCircle does.
468
469
  \see setPen
470
*/
471
void QCPScatterStyle::setBrush(const QBrush &brush) { mBrush = brush; }
472
473
/*!
474
  Sets the pixmap that will be drawn as scatter point to \a pixmap.
475
476
  Note that \ref setSize does not influence the appearance of the pixmap.
477
478
  The scatter shape is automatically set to \ref ssPixmap.
479
*/
480
void QCPScatterStyle::setPixmap(const QPixmap &pixmap) {
481
  setShape(ssPixmap);
482
  mPixmap = pixmap;
483
}
484
485
/*!
486
  Sets the custom shape that will be drawn as scatter point to \a customPath.
487
488
  The scatter shape is automatically set to \ref ssCustom.
489
*/
490
void QCPScatterStyle::setCustomPath(const QPainterPath &customPath) {
491
  setShape(ssCustom);
492
  mCustomPath = customPath;
493
}
494
495
/*!
496
  Applies the pen and the brush of this scatter style to \a painter. If this
497
  scatter style has an undefined pen (\ref isPenDefined), sets the pen of \a
498
  painter to \a defaultPen instead.
499
500
  This function is used by plottables (or any class that wants to draw scatters)
501
  just before a number of scatters with this style shall be drawn with the \a
502
  painter.
503
504
  \see drawShape
505
*/
506
void QCPScatterStyle::applyTo(QCPPainter *painter,
507
                              const QPen &defaultPen) const {
508
  painter->setPen(mPenDefined ? mPen : defaultPen);
509
  painter->setBrush(mBrush);
510
}
511
512
/*!
513
  Draws the scatter shape with \a painter at position \a pos.
514
515
  This function does not modify the pen or the brush on the painter, as \ref
516
  applyTo is meant to be called before scatter points are drawn with \ref
517
  drawShape.
518
519
  \see applyTo
520
*/
521
void QCPScatterStyle::drawShape(QCPPainter *painter, QPointF pos) const {
522
  drawShape(painter, pos.x(), pos.y());
523
}
524
525
/*! \overload
526
  Draws the scatter shape with \a painter at position \a x and \a y.
527
*/
528
void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const {
529
  double w = mSize / 2.0;
530
  switch (mShape) {
531
    case ssNone:
532
      break;
533
    case ssDot: {
534
      painter->drawLine(QPointF(x, y), QPointF(x + 0.0001, y));
535
      break;
536
    }
537
    case ssCross: {
538
      painter->drawLine(QLineF(x - w, y - w, x + w, y + w));
539
      painter->drawLine(QLineF(x - w, y + w, x + w, y - w));
540
      break;
541
    }
542
    case ssPlus: {
543
      painter->drawLine(QLineF(x - w, y, x + w, y));
544
      painter->drawLine(QLineF(x, y + w, x, y - w));
545
      break;
546
    }
547
    case ssCircle: {
548
      painter->drawEllipse(QPointF(x, y), w, w);
549
      break;
550
    }
551
    case ssDisc: {
552
      QBrush b = painter->brush();
553
      painter->setBrush(painter->pen().color());
554
      painter->drawEllipse(QPointF(x, y), w, w);
555
      painter->setBrush(b);
556
      break;
557
    }
558
    case ssSquare: {
559
      painter->drawRect(QRectF(x - w, y - w, mSize, mSize));
560
      break;
561
    }
562
    case ssDiamond: {
563
      painter->drawLine(QLineF(x - w, y, x, y - w));
564
      painter->drawLine(QLineF(x, y - w, x + w, y));
565
      painter->drawLine(QLineF(x + w, y, x, y + w));
566
      painter->drawLine(QLineF(x, y + w, x - w, y));
567
      break;
568
    }
569
    case ssStar: {
570
      painter->drawLine(QLineF(x - w, y, x + w, y));
571
      painter->drawLine(QLineF(x, y + w, x, y - w));
572
      painter->drawLine(
573
          QLineF(x - w * 0.707, y - w * 0.707, x + w * 0.707, y + w * 0.707));
574
      painter->drawLine(
575
          QLineF(x - w * 0.707, y + w * 0.707, x + w * 0.707, y - w * 0.707));
576
      break;
577
    }
578
    case ssTriangle: {
579
      painter->drawLine(QLineF(x - w, y + 0.755 * w, x + w, y + 0.755 * w));
580
      painter->drawLine(QLineF(x + w, y + 0.755 * w, x, y - 0.977 * w));
581
      painter->drawLine(QLineF(x, y - 0.977 * w, x - w, y + 0.755 * w));
582
      break;
583
    }
584
    case ssTriangleInverted: {
585
      painter->drawLine(QLineF(x - w, y - 0.755 * w, x + w, y - 0.755 * w));
586
      painter->drawLine(QLineF(x + w, y - 0.755 * w, x, y + 0.977 * w));
587
      painter->drawLine(QLineF(x, y + 0.977 * w, x - w, y - 0.755 * w));
588
      break;
589
    }
590
    case ssCrossSquare: {
591
      painter->drawLine(QLineF(x - w, y - w, x + w * 0.95, y + w * 0.95));
592
      painter->drawLine(QLineF(x - w, y + w * 0.95, x + w * 0.95, y - w));
593
      painter->drawRect(QRectF(x - w, y - w, mSize, mSize));
594
      break;
595
    }
596
    case ssPlusSquare: {
597
      painter->drawLine(QLineF(x - w, y, x + w * 0.95, y));
598
      painter->drawLine(QLineF(x, y + w, x, y - w));
599
      painter->drawRect(QRectF(x - w, y - w, mSize, mSize));
600
      break;
601
    }
602
    case ssCrossCircle: {
603
      painter->drawLine(
604
          QLineF(x - w * 0.707, y - w * 0.707, x + w * 0.670, y + w * 0.670));
605
      painter->drawLine(
606
          QLineF(x - w * 0.707, y + w * 0.670, x + w * 0.670, y - w * 0.707));
607
      painter->drawEllipse(QPointF(x, y), w, w);
608
      break;
609
    }
610
    case ssPlusCircle: {
611
      painter->drawLine(QLineF(x - w, y, x + w, y));
612
      painter->drawLine(QLineF(x, y + w, x, y - w));
613
      painter->drawEllipse(QPointF(x, y), w, w);
614
      break;
615
    }
616
    case ssPeace: {
617
      painter->drawLine(QLineF(x, y - w, x, y + w));
618
      painter->drawLine(QLineF(x, y, x - w * 0.707, y + w * 0.707));
619
      painter->drawLine(QLineF(x, y, x + w * 0.707, y + w * 0.707));
620
      painter->drawEllipse(QPointF(x, y), w, w);
621
      break;
622
    }
623
    case ssPixmap: {
624
      painter->drawPixmap(x - mPixmap.width() * 0.5, y - mPixmap.height() * 0.5,
625
                          mPixmap);
626
      break;
627
    }
628
    case ssCustom: {
629
      QTransform oldTransform = painter->transform();
630
      painter->translate(x, y);
631
      painter->scale(mSize / 6.0, mSize / 6.0);
632
      painter->drawPath(mCustomPath);
633
      painter->setTransform(oldTransform);
634
      break;
635
    }
636
  }
637
}
638
639
////////////////////////////////////////////////////////////////////////////////////////////////////
640
//////////////////// QCPLayer
641
////////////////////////////////////////////////////////////////////////////////////////////////////
642
643
/*! \class QCPLayer
644
  \brief A layer that may contain objects, to control the rendering order
645
646
  The Layering system of QCustomPlot is the mechanism to control the rendering
647
  order of the elements inside the plot.
648
649
  It is based on the two classes QCPLayer and QCPLayerable. QCustomPlot holds an
650
  ordered list of one or more instances of QCPLayer (see QCustomPlot::addLayer,
651
  QCustomPlot::layer, QCustomPlot::moveLayer, etc.). When replotting,
652
  QCustomPlot goes through the list of layers bottom to top and successively
653
  draws the layerables of the layers.
654
655
  A QCPLayer contains an ordered list of QCPLayerable instances. QCPLayerable is
656
  an abstract base class from which almost all visible objects derive, like
657
  axes, grids, graphs, items, etc.
658
659
  Initially, QCustomPlot has five layers: "background", "grid", "main", "axes"
660
  and "legend" (in that order). The top two layers "axes" and "legend" contain
661
  the default axes and legend, so they will be drawn on top. In the middle,
662
  there is the "main" layer. It is initially empty and set as the current layer
663
  (see QCustomPlot::setCurrentLayer). This means, all new plottables, items etc.
664
  are created on this layer by default. Then comes the "grid" layer which
665
  contains the QCPGrid instances (which belong tightly to QCPAxis, see \ref
666
  QCPAxis::grid). The Axis rect background shall be drawn behind everything
667
  else, thus the default QCPAxisRect instance is placed on the "background"
668
  layer. Of course, the layer affiliation of the individual objects can be
669
  changed as required (\ref QCPLayerable::setLayer).
670
671
  Controlling the ordering of objects is easy: Create a new layer in the
672
  position you want it to be, e.g. above "main", with QCustomPlot::addLayer.
673
  Then set the current layer with QCustomPlot::setCurrentLayer to that new layer
674
  and finally create the objects normally. They will be placed on the new layer
675
  automatically, due to the current layer setting. Alternatively you could have
676
  also ignored the current layer setting and just moved the objects with
677
  QCPLayerable::setLayer to the desired layer after creating them.
678
679
  It is also possible to move whole layers. For example, If you want the grid to
680
  be shown in front of all plottables/items on the "main" layer, just move it
681
  above "main" with QCustomPlot::moveLayer.
682
683
  The rendering order within one layer is simply by order of creation or
684
  insertion. The item created last (or added last to the layer), is drawn on top
685
  of all other objects on that layer.
686
687
  When a layer is deleted, the objects on it are not deleted with it, but fall
688
  on the layer below the deleted layer, see QCustomPlot::removeLayer.
689
*/
690
691
/* start documentation of inline functions */
692
693
/*! \fn QList<QCPLayerable*> QCPLayer::children() const
694
695
  Returns a list of all layerables on this layer. The order corresponds to the
696
  rendering order: layerables with higher indices are drawn above layerables
697
  with lower indices.
698
*/
699
700
/*! \fn int QCPLayer::index() const
701
702
  Returns the index this layer has in the QCustomPlot. The index is the integer
703
  number by which this layer can be accessed via \ref QCustomPlot::layer.
704
705
  Layers with higher indices will be drawn above layers with lower indices.
706
*/
707
708
/* end documentation of inline functions */
709
710
/*!
711
  Creates a new QCPLayer instance.
712
713
  Normally you shouldn't directly instantiate layers, use \ref
714
  QCustomPlot::addLayer instead.
715
716
  \warning It is not checked that \a layerName is actually a unique layer name
717
  in \a parentPlot. This check is only performed by \ref QCustomPlot::addLayer.
718
*/
719
QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName)
720
    : QObject(parentPlot),
721
      mParentPlot(parentPlot),
722
      mName(layerName),
723
      mIndex(-1),  // will be set to a proper value by the QCustomPlot layer
724
                   // creation function
725
      mVisible(true) {
726
  // Note: no need to make sure layerName is unique, because layer
727
  // management is done with QCustomPlot functions.
728
}
729
730
QCPLayer::~QCPLayer() {
731
  // If child layerables are still on this layer, detach them, so they don't try
732
  // to reach back to this then invalid layer once they get deleted/moved
733
  // themselves. This only happens when layers are deleted directly, like in the
734
  // QCustomPlot destructor. (The regular layer removal procedure for the user
735
  // is to call QCustomPlot::removeLayer, which moves all layerables off this
736
  // layer before deleting it.)
737
738
  while (!mChildren.isEmpty())
739
    mChildren.last()->setLayer(
740
        0);  // removes itself from mChildren via removeChild()
741
742
  if (mParentPlot->currentLayer() == this)
743
    qDebug() << Q_FUNC_INFO
744
             << "The parent plot's mCurrentLayer will be a dangling pointer. "
745
                "Should have been set to a valid layer or 0 beforehand.";
746
}
747
748
/*!
749
  Sets whether this layer is visible or not. If \a visible is set to false, all
750
  layerables on this layer will be invisible.
751
752
  This function doesn't change the visibility property of the layerables (\ref
753
  QCPLayerable::setVisible), but the \ref QCPLayerable::realVisibility of each
754
  layerable takes the visibility of the parent layer into account.
755
*/
756
void QCPLayer::setVisible(bool visible) { mVisible = visible; }
757
758
/*! \internal
759
760
  Adds the \a layerable to the list of this layer. If \a prepend is set to true,
761
  the layerable will be prepended to the list, i.e. be drawn beneath the other
762
  layerables already in the list.
763
764
  This function does not change the \a mLayer member of \a layerable to this
765
  layer. (Use QCPLayerable::setLayer to change the layer of an object, not this
766
  function.)
767
768
  \see removeChild
769
*/
770
void QCPLayer::addChild(QCPLayerable *layerable, bool prepend) {
771
  if (!mChildren.contains(layerable)) {
772
    if (prepend)
773
      mChildren.prepend(layerable);
774
    else
775
      mChildren.append(layerable);
776
  } else
777
    qDebug() << Q_FUNC_INFO << "layerable is already child of this layer"
778
             << reinterpret_cast<quintptr>(layerable);
779
}
780
781
/*! \internal
782
783
  Removes the \a layerable from the list of this layer.
784
785
  This function does not change the \a mLayer member of \a layerable. (Use
786
  QCPLayerable::setLayer to change the layer of an object, not this function.)
787
788
  \see addChild
789
*/
790
void QCPLayer::removeChild(QCPLayerable *layerable) {
791
  if (!mChildren.removeOne(layerable))
792
    qDebug() << Q_FUNC_INFO << "layerable is not child of this layer"
793
             << reinterpret_cast<quintptr>(layerable);
794
}
795
796
////////////////////////////////////////////////////////////////////////////////////////////////////
797
//////////////////// QCPLayerable
798
////////////////////////////////////////////////////////////////////////////////////////////////////
799
800
/*! \class QCPLayerable
801
  \brief Base class for all drawable objects
802
803
  This is the abstract base class most visible objects derive from, e.g.
804
  plottables, axes, grid etc.
805
806
  Every layerable is on a layer (QCPLayer) which allows controlling the
807
  rendering order by stacking the layers accordingly.
808
809
  For details about the layering mechanism, see the QCPLayer documentation.
810
*/
811
812
/* start documentation of inline functions */
813
814
/*! \fn QCPLayerable *QCPLayerable::parentLayerable() const
815
816
  Returns the parent layerable of this layerable. The parent layerable is used
817
  to provide visibility hierarchies in conjunction with the method \ref
818
  realVisibility. This way, layerables only get drawn if their parent layerables
819
  are visible, too.
820
821
  Note that a parent layerable is not necessarily also the QObject parent for
822
  memory management. Further, a layerable doesn't always have a parent
823
  layerable, so this function may return 0.
824
825
  A parent layerable is set implicitly with when placed inside layout elements
826
  and doesn't need to be set manually by the user.
827
*/
828
829
/* end documentation of inline functions */
830
/* start documentation of pure virtual functions */
831
832
/*! \fn virtual void QCPLayerable::applyDefaultAntialiasingHint(QCPPainter
833
  *painter) const = 0 \internal
834
835
  This function applies the default antialiasing setting to the specified \a
836
  painter, using the function \ref applyAntialiasingHint. It is the antialiasing
837
  state the painter is put in, when \ref draw is called on the layerable. If the
838
  layerable has multiple entities whose antialiasing setting may be specified
839
  individually, this function should set the antialiasing state of the most
840
  prominent entity. In this case however, the \ref draw function usually calls
841
  the specialized versions of this function before drawing each entity,
842
  effectively overriding the setting of the default antialiasing hint.
843
844
  <b>First example:</b> QCPGraph has multiple entities that have an antialiasing
845
  setting: The graph line, fills, scatters and error bars. Those can be
846
  configured via QCPGraph::setAntialiased, QCPGraph::setAntialiasedFill,
847
  QCPGraph::setAntialiasedScatters etc. Consequently, there isn't only the
848
  QCPGraph::applyDefaultAntialiasingHint function (which corresponds to the
849
  graph line's antialiasing), but specialized ones like
850
  QCPGraph::applyFillAntialiasingHint and
851
  QCPGraph::applyScattersAntialiasingHint. So before drawing one of those
852
  entities, QCPGraph::draw calls the respective specialized
853
  applyAntialiasingHint function.
854
855
  <b>Second example:</b> QCPItemLine consists only of a line so there is only
856
  one antialiasing setting which can be controlled with
857
  QCPItemLine::setAntialiased. (This function is inherited by all layerables.
858
  The specialized functions, as seen on QCPGraph, must be added explicitly to
859
  the respective layerable subclass.) Consequently it only has the normal
860
  QCPItemLine::applyDefaultAntialiasingHint. The \ref QCPItemLine::draw function
861
  doesn't need to care about setting any antialiasing states, because the
862
  default antialiasing hint is already set on the painter when the \ref draw
863
  function is called, and that's the state it wants to draw the line with.
864
*/
865
866
/*! \fn virtual void QCPLayerable::draw(QCPPainter *painter) const = 0
867
  \internal
868
869
  This function draws the layerable with the specified \a painter. It is only
870
  called by QCustomPlot, if the layerable is visible (\ref setVisible).
871
872
  Before this function is called, the painter's antialiasing state is set via
873
  \ref applyDefaultAntialiasingHint, see the documentation there. Further, the
874
  clipping rectangle was set to \ref clipRect.
875
*/
876
877
/* end documentation of pure virtual functions */
878
/* start documentation of signals */
879
880
/*! \fn void QCPLayerable::layerChanged(QCPLayer *newLayer);
881
882
  This signal is emitted when the layer of this layerable changes, i.e. this
883
  layerable is moved to a different layer.
884
885
  \see setLayer
886
*/
887
888
/* end documentation of signals */
889
890
/*!
891
  Creates a new QCPLayerable instance.
892
893
  Since QCPLayerable is an abstract base class, it can't be instantiated
894
  directly. Use one of the derived classes.
895
896
  If \a plot is provided, it automatically places itself on the layer named \a
897
  targetLayer. If \a targetLayer is an empty string, it places itself on the
898
  current layer of the plot (see \ref QCustomPlot::setCurrentLayer).
899
900
  It is possible to provide 0 as \a plot. In that case, you should assign a
901
  parent plot at a later time with \ref initializeParentPlot.
902
903
  The layerable's parent layerable is set to \a parentLayerable, if provided.
904
  Direct layerable parents are mainly used to control visibility in a hierarchy
905
  of layerables. This means a layerable is only drawn, if all its ancestor
906
  layerables are also visible. Note that \a parentLayerable does not become the
907
  QObject-parent (for memory management) of this layerable, \a plot does. It is
908
  not uncommon to set the QObject-parent to something else in the constructors
909
  of QCPLayerable subclasses, to guarantee a working destruction hierarchy.
910
*/
911
QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer,
912
                           QCPLayerable *parentLayerable)
913
    : QObject(plot),
914
      mVisible(true),
915
      mParentPlot(plot),
916
      mParentLayerable(parentLayerable),
917
      mLayer(0),
918
      mAntialiased(true) {
919
  if (mParentPlot) {
920
    if (targetLayer.isEmpty())
921
      setLayer(mParentPlot->currentLayer());
922
    else if (!setLayer(targetLayer))
923
      qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to"
924
               << targetLayer << "failed.";
925
  }
926
}
927
928
QCPLayerable::~QCPLayerable() {
929
  if (mLayer) {
930
    mLayer->removeChild(this);
931
    mLayer = 0;
932
  }
933
}
934
935
/*!
936
  Sets the visibility of this layerable object. If an object is not visible, it
937
  will not be drawn on the QCustomPlot surface, and user interaction with it
938
  (e.g. click and selection) is not possible.
939
*/
940
void QCPLayerable::setVisible(bool on) { mVisible = on; }
941
942
/*!
943
  Sets the \a layer of this layerable object. The object will be placed on top
944
  of the other objects already on \a layer.
945
946
  If \a layer is 0, this layerable will not be on any layer and thus not appear
947
  in the plot (or interact/receive events).
948
949
  Returns true if the layer of this layerable was successfully changed to \a
950
  layer.
951
*/
952
bool QCPLayerable::setLayer(QCPLayer *layer) {
953
  return moveToLayer(layer, false);
954
}
955
956
/*! \overload
957
  Sets the layer of this layerable object by name
958
959
  Returns true on success, i.e. if \a layerName is a valid layer name.
960
*/
961
bool QCPLayerable::setLayer(const QString &layerName) {
962
  if (!mParentPlot) {
963
    qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
964
    return false;
965
  }
966
  if (QCPLayer *layer = mParentPlot->layer(layerName)) {
967
    return setLayer(layer);
968
  } else {
969
    qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
970
    return false;
971
  }
972
}
973
974
/*!
975
  Sets whether this object will be drawn antialiased or not.
976
977
  Note that antialiasing settings may be overridden by
978
  QCustomPlot::setAntialiasedElements and
979
  QCustomPlot::setNotAntialiasedElements.
980
*/
981
void QCPLayerable::setAntialiased(bool enabled) { mAntialiased = enabled; }
982
983
/*!
984
  Returns whether this layerable is visible, taking the visibility of the
985
  layerable parent and the visibility of the layer this layerable is on into
986
  account. This is the method that is consulted to decide whether a layerable
987
  shall be drawn or not.
988
989
  If this layerable has a direct layerable parent (usually set via hierarchies
990
  implemented in subclasses, like in the case of QCPLayoutElement), this
991
  function returns true only if this layerable has its visibility set to true
992
  and the parent layerable's \ref realVisibility returns true.
993
994
  If this layerable doesn't have a direct layerable parent, returns the state of
995
  this layerable's visibility.
996
*/
997
bool QCPLayerable::realVisibility() const {
998
  return mVisible && (!mLayer || mLayer->visible()) &&
999
         (!mParentLayerable || mParentLayerable.data()->realVisibility());
1000
}
1001
1002
/*!
1003
  This function is used to decide whether a click hits a layerable object or
1004
  not.
1005
1006
  \a pos is a point in pixel coordinates on the QCustomPlot surface. This
1007
  function returns the shortest pixel distance of this point to the object. If
1008
  the object is either invisible or the distance couldn't be determined, -1.0 is
1009
  returned. Further, if \a onlySelectable is true and the object is not
1010
  selectable, -1.0 is returned, too.
1011
1012
  If the object is represented not by single lines but by an area like a \ref
1013
  QCPItemText or the bars of a \ref QCPBars plottable, a click inside the area
1014
  should also be considered a hit. In these cases this function thus returns a
1015
  constant value greater zero but still below the parent plot's selection
1016
  tolerance. (typically the selectionTolerance multiplied by 0.99).
1017
1018
  Providing a constant value for area objects allows selecting line objects even
1019
  when they are obscured by such area objects, by clicking close to the lines
1020
  (i.e. closer than 0.99*selectionTolerance).
1021
1022
  The actual setting of the selection state is not done by this function. This
1023
  is handled by the parent QCustomPlot when the mouseReleaseEvent occurs, and
1024
  the finally selected object is notified via the selectEvent/deselectEvent
1025
  methods.
1026
1027
  \a details is an optional output parameter. Every layerable subclass may place
1028
  any information in \a details. This information will be passed to \ref
1029
  selectEvent when the parent QCustomPlot decides on the basis of this
1030
  selectTest call, that the object was successfully selected. The subsequent
1031
  call to \ref selectEvent will carry the \a details. This is useful for
1032
  multi-part objects (like QCPAxis). This way, a possibly complex calculation to
1033
  decide which part was clicked is only done once in \ref selectTest. The result
1034
  (i.e. the actually clicked part) can then be placed in \a details. So in the
1035
  subsequent \ref selectEvent, the decision which part was selected doesn't have
1036
  to be done a second time for a single selection operation.
1037
1038
  You may pass 0 as \a details to indicate that you are not interested in those
1039
  selection details.
1040
1041
  \see selectEvent, deselectEvent, QCustomPlot::setInteractions
1042
*/
1043
double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable,
1044
                                QVariant *details) const {
1045
  Q_UNUSED(pos)
1046
  Q_UNUSED(onlySelectable)
1047
  Q_UNUSED(details)
1048
  return -1.0;
1049
}
1050
1051
/*! \internal
1052
1053
  Sets the parent plot of this layerable. Use this function once to set the
1054
  parent plot if you have passed 0 in the constructor. It can not be used to
1055
  move a layerable from one QCustomPlot to another one.
1056
1057
  Note that, unlike when passing a non-null parent plot in the constructor, this
1058
  function does not make \a parentPlot the QObject-parent of this layerable. If
1059
  you want this, call QObject::setParent(\a parentPlot) in addition to this
1060
  function.
1061
1062
  Further, you will probably want to set a layer (\ref setLayer) after calling
1063
  this function, to make the layerable appear on the QCustomPlot.
1064
1065
  The parent plot change will be propagated to subclasses via a call to \ref
1066
  parentPlotInitialized so they can react accordingly (e.g. also initialize the
1067
  parent plot of child layerables, like QCPLayout does).
1068
*/
1069
void QCPLayerable::initializeParentPlot(QCustomPlot *parentPlot) {
1070
  if (mParentPlot) {
1071
    qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized";
1072
    return;
1073
  }
1074
1075
  if (!parentPlot) qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
1076
1077
  mParentPlot = parentPlot;
1078
  parentPlotInitialized(mParentPlot);
1079
}
1080
1081
/*! \internal
1082
1083
  Sets the parent layerable of this layerable to \a parentLayerable. Note that
1084
  \a parentLayerable does not become the QObject-parent (for memory management)
1085
  of this layerable.
1086
1087
  The parent layerable has influence on the return value of the \ref
1088
  realVisibility method. Only layerables with a fully visible parent tree will
1089
  return true for \ref realVisibility, and thus be drawn.
1090
1091
  \see realVisibility
1092
*/
1093
void QCPLayerable::setParentLayerable(QCPLayerable *parentLayerable) {
1094
  mParentLayerable = parentLayerable;
1095
}
1096
1097
/*! \internal
1098
1099
  Moves this layerable object to \a layer. If \a prepend is true, this object
1100
  will be prepended to the new layer's list, i.e. it will be drawn below the
1101
  objects already on the layer. If it is false, the object will be appended.
1102
1103
  Returns true on success, i.e. if \a layer is a valid layer.
1104
*/
1105
bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend) {
1106
  if (layer && !mParentPlot) {
1107
    qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1108
    return false;
1109
  }
1110
  if (layer && layer->parentPlot() != mParentPlot) {
1111
    qDebug() << Q_FUNC_INFO << "layer" << layer->name()
1112
             << "is not in same QCustomPlot as this layerable";
1113
    return false;
1114
  }
1115
1116
  QCPLayer *oldLayer = mLayer;
1117
  if (mLayer) mLayer->removeChild(this);
1118
  mLayer = layer;
1119
  if (mLayer) mLayer->addChild(this, prepend);
1120
  if (mLayer != oldLayer) emit layerChanged(mLayer);
1121
  return true;
1122
}
1123
1124
/*! \internal
1125
1126
  Sets the QCPainter::setAntialiasing state on the provided \a painter,
1127
  depending on the \a localAntialiased value as well as the overrides \ref
1128
  QCustomPlot::setAntialiasedElements and \ref
1129
  QCustomPlot::setNotAntialiasedElements. Which override enum this function
1130
  takes into account is controlled via \a overrideElement.
1131
*/
1132
void QCPLayerable::applyAntialiasingHint(
1133
    QCPPainter *painter, bool localAntialiased,
1134
    QCP::AntialiasedElement overrideElement) const {
1135
  if (mParentPlot &&
1136
      mParentPlot->notAntialiasedElements().testFlag(overrideElement))
1137
    painter->setAntialiasing(false);
1138
  else if (mParentPlot &&
1139
           mParentPlot->antialiasedElements().testFlag(overrideElement))
1140
    painter->setAntialiasing(true);
1141
  else
1142
    painter->setAntialiasing(localAntialiased);
1143
}
1144
1145
/*! \internal
1146
1147
  This function is called by \ref initializeParentPlot, to allow subclasses to
1148
  react on the setting of a parent plot. This is the case when 0 was passed as
1149
  parent plot in the constructor, and the parent plot is set at a later time.
1150
1151
  For example, QCPLayoutElement/QCPLayout hierarchies may be created
1152
  independently of any QCustomPlot at first. When they are then added to a
1153
  layout inside the QCustomPlot, the top level element of the hierarchy gets its
1154
  parent plot initialized with \ref initializeParentPlot. To propagate the
1155
  parent plot to all the children of the hierarchy, the top level element then
1156
  uses this function to pass the parent plot on to its child elements.
1157
1158
  The default implementation does nothing.
1159
1160
  \see initializeParentPlot
1161
*/
1162
void QCPLayerable::parentPlotInitialized(QCustomPlot *parentPlot) {
1163
  Q_UNUSED(parentPlot)
1164
}
1165
1166
/*! \internal
1167
1168
  Returns the selection category this layerable shall belong to. The selection
1169
  category is used in conjunction with \ref QCustomPlot::setInteractions to
1170
  control which objects are selectable and which aren't.
1171
1172
  Subclasses that don't fit any of the normal \ref QCP::Interaction values can
1173
  use \ref QCP::iSelectOther. This is what the default implementation returns.
1174
1175
  \see QCustomPlot::setInteractions
1176
*/
1177
QCP::Interaction QCPLayerable::selectionCategory() const {
1178
  return QCP::iSelectOther;
1179
}
1180
1181
/*! \internal
1182
1183
  Returns the clipping rectangle of this layerable object. By default, this is
1184
  the viewport of the parent QCustomPlot. Specific subclasses may reimplement
1185
  this function to provide different clipping rects.
1186
1187
  The returned clipping rect is set on the painter before the draw function of
1188
  the respective object is called.
1189
*/
1190
QRect QCPLayerable::clipRect() const {
1191
  if (mParentPlot)
1192
    return mParentPlot->viewport();
1193
  else
1194
    return QRect();
1195
}
1196
1197
/*! \internal
1198
1199
  This event is called when the layerable shall be selected, as a consequence of
1200
  a click by the user. Subclasses should react to it by setting their selection
1201
  state appropriately. The default implementation does nothing.
1202
1203
  \a event is the mouse event that caused the selection. \a additive indicates,
1204
  whether the user was holding the multi-select-modifier while performing the
1205
  selection (see \ref QCustomPlot::setMultiSelectModifier). if \a additive is
1206
  true, the selection state must be toggled (i.e. become selected when
1207
  unselected and unselected when selected).
1208
1209
  Every selectEvent is preceded by a call to \ref selectTest, which has returned
1210
  positively (i.e. returned a value greater than 0 and less than the selection
1211
  tolerance of the parent QCustomPlot). The \a details data you output from \ref
1212
  selectTest is fed back via \a details here. You may use it to transport any
1213
  kind of information from the selectTest to the possibly subsequent
1214
  selectEvent. Usually \a details is used to transfer which part was clicked, if
1215
  it is a layerable that has multiple individually selectable parts (like
1216
  QCPAxis). This way selectEvent doesn't need to do the calculation again to
1217
  find out which part was actually clicked.
1218
1219
  \a selectionStateChanged is an output parameter. If the pointer is non-null,
1220
  this function must set the value either to true or false, depending on whether
1221
  the selection state of this layerable was actually changed. For layerables
1222
  that only are selectable as a whole and not in parts, this is simple: if \a
1223
  additive is true, \a selectionStateChanged must also be set to true, because
1224
  the selection toggles. If \a additive is false, \a selectionStateChanged is
1225
  only set to true, if the layerable was previously unselected and now is
1226
  switched to the selected state.
1227
1228
  \see selectTest, deselectEvent
1229
*/
1230
void QCPLayerable::selectEvent(QMouseEvent *event, bool additive,
1231
                               const QVariant &details,
1232
                               bool *selectionStateChanged) {
1233
  Q_UNUSED(event)
1234
  Q_UNUSED(additive)
1235
  Q_UNUSED(details)
1236
  Q_UNUSED(selectionStateChanged)
1237
}
1238
1239
/*! \internal
1240
1241
  This event is called when the layerable shall be deselected, either as
1242
  consequence of a user interaction or a call to \ref QCustomPlot::deselectAll.
1243
  Subclasses should react to it by unsetting their selection appropriately.
1244
1245
  just as in \ref selectEvent, the output parameter \a selectionStateChanged (if
1246
  non-null), must return true or false when the selection state of this
1247
  layerable has changed or not changed, respectively.
1248
1249
  \see selectTest, selectEvent
1250
*/
1251
void QCPLayerable::deselectEvent(bool *selectionStateChanged) {
1252
  Q_UNUSED(selectionStateChanged)
1253
}
1254
1255
////////////////////////////////////////////////////////////////////////////////////////////////////
1256
//////////////////// QCPRange
1257
////////////////////////////////////////////////////////////////////////////////////////////////////
1258
/*! \class QCPRange
1259
  \brief Represents the range an axis is encompassing.
1260
1261
  contains a \a lower and \a upper double value and provides convenience input,
1262
  output and modification functions.
1263
1264
  \see QCPAxis::setRange
1265
*/
1266
1267
/*!
1268
  Minimum range size (\a upper - \a lower) the range changing functions will
1269
  accept. Smaller intervals would cause errors due to the 11-bit exponent of
1270
  double precision numbers, corresponding to a minimum magnitude of roughly
1271
  1e-308. \see validRange, maxRange
1272
*/
1273
const double QCPRange::minRange = 1e-280;
1274
1275
/*!
1276
  Maximum values (negative and positive) the range will accept in range-changing
1277
  functions. Larger absolute values would cause errors due to the 11-bit
1278
  exponent of double precision numbers, corresponding to a maximum magnitude of
1279
  roughly 1e308. Since the number of planck-volumes in the entire visible
1280
  universe is only ~1e183, this should be enough. \see validRange, minRange
1281
*/
1282
const double QCPRange::maxRange = 1e250;
1283
1284
/*!
1285
  Constructs a range with \a lower and \a upper set to zero.
1286
*/
1287
QCPRange::QCPRange() : lower(0), upper(0) {}
1288
1289
/*! \overload
1290
  Constructs a range with the specified \a lower and \a upper values.
1291
*/
1292
QCPRange::QCPRange(double lower, double upper) : lower(lower), upper(upper) {
1293
  normalize();
1294
}
1295
1296
/*!
1297
  Returns the size of the range, i.e. \a upper-\a lower
1298
*/
1299
double QCPRange::size() const { return upper - lower; }
1300
1301
/*!
1302
  Returns the center of the range, i.e. (\a upper+\a lower)*0.5
1303
*/
1304
double QCPRange::center() const { return (upper + lower) * 0.5; }
1305
1306
/*!
1307
  Makes sure \a lower is numerically smaller than \a upper. If this is not the
1308
  case, the values are swapped.
1309
*/
1310
void QCPRange::normalize() {
1311
  if (lower > upper) qSwap(lower, upper);
1312
}
1313
1314
/*!
1315
  Expands this range such that \a otherRange is contained in the new range. It
1316
  is assumed that both this range and \a otherRange are normalized (see \ref
1317
  normalize).
1318
1319
  If \a otherRange is already inside the current range, this function does
1320
  nothing.
1321
1322
  \see expanded
1323
*/
1324
void QCPRange::expand(const QCPRange &otherRange) {
1325
  if (lower > otherRange.lower) lower = otherRange.lower;
1326
  if (upper < otherRange.upper) upper = otherRange.upper;
1327
}
1328
1329
/*!
1330
  Returns an expanded range that contains this and \a otherRange. It is assumed
1331
  that both this range and \a otherRange are normalized (see \ref normalize).
1332
1333
  \see expand
1334
*/
1335
QCPRange QCPRange::expanded(const QCPRange &otherRange) const {
1336
  QCPRange result = *this;
1337
  result.expand(otherRange);
1338
  return result;
1339
}
1340
1341
/*!
1342
  Returns a sanitized version of the range. Sanitized means for logarithmic
1343
  scales, that the range won't span the positive and negative sign domain, i.e.
1344
  contain zero. Further \a lower will always be numerically smaller (or equal)
1345
  to \a upper.
1346
1347
  If the original range does span positive and negative sign domains or contains
1348
  zero, the returned range will try to approximate the original range as good as
1349
  possible. If the positive interval of the original range is wider than the
1350
  negative interval, the returned range will only contain the positive interval,
1351
  with lower bound set to \a rangeFac or \a rangeFac *\a upper, whichever is
1352
  closer to zero. Same procedure is used if the negative interval is wider than
1353
  the positive interval, this time by changing the \a upper bound.
1354
*/
1355
QCPRange QCPRange::sanitizedForLogScale() const {
1356
  double rangeFac = 1e-3;
1357
  QCPRange sanitizedRange(lower, upper);
1358
  sanitizedRange.normalize();
1359
  // can't have range spanning negative and positive values in log plot, so
1360
  // change range to fix it
1361
  // if (qFuzzyCompare(sanitizedRange.lower+1, 1) &&
1362
  // !qFuzzyCompare(sanitizedRange.upper+1, 1))
1363
  if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0) {
1364
    // case lower is 0
1365
    if (rangeFac < sanitizedRange.upper * rangeFac)
1366
      sanitizedRange.lower = rangeFac;
1367
    else
1368
      sanitizedRange.lower = sanitizedRange.upper * rangeFac;
1369
  }  // else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
1370
  else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0) {
1371
    // case upper is 0
1372
    if (-rangeFac > sanitizedRange.lower * rangeFac)
1373
      sanitizedRange.upper = -rangeFac;
1374
    else
1375
      sanitizedRange.upper = sanitizedRange.lower * rangeFac;
1376
  } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0) {
1377
    // find out whether negative or positive interval is wider to decide which
1378
    // sign domain will be chosen
1379
    if (-sanitizedRange.lower > sanitizedRange.upper) {
1380
      // negative is wider, do same as in case upper is 0
1381
      if (-rangeFac > sanitizedRange.lower * rangeFac)
1382
        sanitizedRange.upper = -rangeFac;
1383
      else
1384
        sanitizedRange.upper = sanitizedRange.lower * rangeFac;
1385
    } else {
1386
      // positive is wider, do same as in case lower is 0
1387
      if (rangeFac < sanitizedRange.upper * rangeFac)
1388
        sanitizedRange.lower = rangeFac;
1389
      else
1390
        sanitizedRange.lower = sanitizedRange.upper * rangeFac;
1391
    }
1392
  }
1393
  // due to normalization, case lower>0 && upper<0 should never occur, because
1394
  // that implies upper<lower
1395
  return sanitizedRange;
1396
}
1397
1398
/*!
1399
  Returns a sanitized version of the range. Sanitized means for linear scales,
1400
  that \a lower will always be numerically smaller (or equal) to \a upper.
1401
*/
1402
QCPRange QCPRange::sanitizedForLinScale() const {
1403
  QCPRange sanitizedRange(lower, upper);
1404
  sanitizedRange.normalize();
1405
  return sanitizedRange;
1406
}
1407
1408
/*!
1409
  Returns true when \a value lies within or exactly on the borders of the range.
1410
*/
1411
bool QCPRange::contains(double value) const {
1412
  return value >= lower && value <= upper;
1413
}
1414
1415
/*!
1416
  Checks, whether the specified range is within valid bounds, which are defined
1417
  as QCPRange::maxRange and QCPRange::minRange.
1418
  A valid range means:
1419
  \li range bounds within -maxRange and maxRange
1420
  \li range size above minRange
1421
  \li range size below maxRange
1422
*/
1423
bool QCPRange::validRange(double lower, double upper) {
1424
  return (lower > -maxRange && upper < maxRange &&
1425
          qAbs(lower - upper) > minRange && qAbs(lower - upper) < maxRange &&
1426
          !(lower > 0 && qIsInf(upper / lower)) &&
1427
          !(upper < 0 && qIsInf(lower / upper)));
1428
}
1429
1430
/*!
1431
  \overload
1432
  Checks, whether the specified range is within valid bounds, which are defined
1433
  as QCPRange::maxRange and QCPRange::minRange.
1434
  A valid range means:
1435
  \li range bounds within -maxRange and maxRange
1436
  \li range size above minRange
1437
  \li range size below maxRange
1438
*/
1439
bool QCPRange::validRange(const QCPRange &range) {
1440
  return (range.lower > -maxRange && range.upper < maxRange &&
1441
          qAbs(range.lower - range.upper) > minRange &&
1442
          qAbs(range.lower - range.upper) < maxRange &&
1443
          !(range.lower > 0 && qIsInf(range.upper / range.lower)) &&
1444
          !(range.upper < 0 && qIsInf(range.lower / range.upper)));
1445
}
1446
1447
////////////////////////////////////////////////////////////////////////////////////////////////////
1448
//////////////////// QCPMarginGroup
1449
////////////////////////////////////////////////////////////////////////////////////////////////////
1450
1451
/*! \class QCPMarginGroup
1452
  \brief A margin group allows synchronization of margin sides if working with
1453
  multiple layout elements.
1454
1455
  QCPMarginGroup allows you to tie a margin side of two or more layout elements
1456
  together, such that they will all have the same size, based on the largest
1457
  required margin in the group.
1458
1459
  \n
1460
  \image html QCPMarginGroup.png "Demonstration of QCPMarginGroup"
1461
  \n
1462
1463
  In certain situations it is desirable that margins at specific sides are
1464
  synchronized across layout elements. For example, if one QCPAxisRect is below
1465
  another one in a grid layout, it will provide a cleaner look to the user if
1466
  the left and right margins of the two axis rects are of the same size. The
1467
  left axis of the top axis rect will then be at the same horizontal position as
1468
  the left axis of the lower axis rect, making them appear aligned. The same
1469
  applies for the right axes. This is what QCPMarginGroup makes possible.
1470
1471
  To add/remove a specific side of a layout element to/from a margin group, use
1472
  the \ref QCPLayoutElement::setMarginGroup method. To completely break apart
1473
  the margin group, either call \ref clear, or just delete the margin group.
1474
1475
  \section QCPMarginGroup-example Example
1476
1477
  First create a margin group:
1478
  \snippet documentation/doc-code-snippets/mainwindow.cpp
1479
  qcpmargingroup-creation-1 Then set this group on the layout element sides:
1480
  \snippet documentation/doc-code-snippets/mainwindow.cpp
1481
  qcpmargingroup-creation-2 Here, we've used the first two axis rects of the
1482
  plot and synchronized their left margins with each other and their right
1483
  margins with each other.
1484
*/
1485
1486
/* start documentation of inline functions */
1487
1488
/*! \fn QList<QCPLayoutElement*> QCPMarginGroup::elements(QCP::MarginSide side)
1489
  const
1490
1491
  Returns a list of all layout elements that have their margin \a side
1492
  associated with this margin group.
1493
*/
1494
1495
/* end documentation of inline functions */
1496
1497
/*!
1498
  Creates a new QCPMarginGroup instance in \a parentPlot.
1499
*/
1500
QCPMarginGroup::QCPMarginGroup(QCustomPlot *parentPlot)
1501
    : QObject(parentPlot), mParentPlot(parentPlot) {
1502
  mChildren.insert(QCP::msLeft, QList<QCPLayoutElement *>());
1503
  mChildren.insert(QCP::msRight, QList<QCPLayoutElement *>());
1504
  mChildren.insert(QCP::msTop, QList<QCPLayoutElement *>());
1505
  mChildren.insert(QCP::msBottom, QList<QCPLayoutElement *>());
1506
}
1507
1508
QCPMarginGroup::~QCPMarginGroup() { clear(); }
1509
1510
/*!
1511
  Returns whether this margin group is empty. If this function returns true, no
1512
  layout elements use this margin group to synchronize margin sides.
1513
*/
1514
bool QCPMarginGroup::isEmpty() const {
1515
  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement *> > it(mChildren);
1516
  while (it.hasNext()) {
1517
    it.next();
1518
    if (!it.value().isEmpty()) return false;
1519
  }
1520
  return true;
1521
}
1522
1523
/*!
1524
  Clears this margin group. The synchronization of the margin sides that use
1525
  this margin group is lifted and they will use their individual margin sizes
1526
  again.
1527
*/
1528
void QCPMarginGroup::clear() {
1529
  // make all children remove themselves from this margin group:
1530
  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement *> > it(mChildren);
1531
  while (it.hasNext()) {
1532
    it.next();
1533
    const QList<QCPLayoutElement *> elements = it.value();
1534
    for (int i = elements.size() - 1; i >= 0; --i)
1535
      elements.at(i)->setMarginGroup(
1536
          it.key(), 0);  // removes itself from mChildren via removeChild
1537
  }
1538
}
1539
1540
/*! \internal
1541
1542
  Returns the synchronized common margin for \a side. This is the margin value
1543
  that will be used by the layout element on the respective side, if it is part
1544
  of this margin group.
1545
1546
  The common margin is calculated by requesting the automatic margin (\ref
1547
  QCPLayoutElement::calculateAutoMargin) of each element associated with \a side
1548
  in this margin group, and choosing the largest returned value.
1549
  (QCPLayoutElement::minimumMargins is taken into account, too.)
1550
*/
1551
int QCPMarginGroup::commonMargin(QCP::MarginSide side) const {
1552
  // query all automatic margins of the layout elements in this margin group
1553
  // side and find maximum:
1554
  int result = 0;
1555
  const QList<QCPLayoutElement *> elements = mChildren.value(side);
1556
  for (int i = 0; i < elements.size(); ++i) {
1557
    if (!elements.at(i)->autoMargins().testFlag(side)) continue;
1558
    int m = qMax(elements.at(i)->calculateAutoMargin(side),
1559
                 QCP::getMarginValue(elements.at(i)->minimumMargins(), side));
1560
    if (m > result) result = m;
1561
  }
1562
  return result;
1563
}
1564
1565
/*! \internal
1566
1567
  Adds \a element to the internal list of child elements, for the margin \a
1568
  side.
1569
1570
  This function does not modify the margin group property of \a element.
1571
*/
1572
void QCPMarginGroup::addChild(QCP::MarginSide side, QCPLayoutElement *element) {
1573
  if (!mChildren[side].contains(element))
1574
    mChildren[side].append(element);
1575
  else
1576
    qDebug() << Q_FUNC_INFO
1577
             << "element is already child of this margin group side"
1578
             << reinterpret_cast<quintptr>(element);
1579
}
1580
1581
/*! \internal
1582
1583
  Removes \a element from the internal list of child elements, for the margin \a
1584
  side.
1585
1586
  This function does not modify the margin group property of \a element.
1587
*/
1588
void QCPMarginGroup::removeChild(QCP::MarginSide side,
1589
                                 QCPLayoutElement *element) {
1590
  if (!mChildren[side].removeOne(element))
1591
    qDebug() << Q_FUNC_INFO << "element is not child of this margin group side"
1592
             << reinterpret_cast<quintptr>(element);
1593
}
1594
1595
////////////////////////////////////////////////////////////////////////////////////////////////////
1596
//////////////////// QCPLayoutElement
1597
////////////////////////////////////////////////////////////////////////////////////////////////////
1598
1599
/*! \class QCPLayoutElement
1600
  \brief The abstract base class for all objects that form \ref thelayoutsystem
1601
  "the layout system".
1602
1603
  This is an abstract base class. As such, it can't be instantiated directly,
1604
  rather use one of its subclasses.
1605
1606
  A Layout element is a rectangular object which can be placed in layouts. It
1607
  has an outer rect (QCPLayoutElement::outerRect) and an inner rect (\ref
1608
  QCPLayoutElement::rect). The difference between outer and inner rect is called
1609
  its margin. The margin can either be set to automatic or manual (\ref
1610
  setAutoMargins) on a per-side basis. If a side is set to manual, that margin
1611
  can be set explicitly with \ref setMargins and will stay fixed at that value.
1612
  If it's set to automatic, the layout element subclass will control the value
1613
  itself (via \ref calculateAutoMargin).
1614
1615
  Layout elements can be placed in layouts (base class QCPLayout) like
1616
  QCPLayoutGrid. The top level layout is reachable via \ref
1617
  QCustomPlot::plotLayout, and is a \ref QCPLayoutGrid. Since \ref QCPLayout
1618
  itself derives from \ref QCPLayoutElement, layouts can be nested.
1619
1620
  Thus in QCustomPlot one can divide layout elements into two categories: The
1621
  ones that are invisible by themselves, because they don't draw anything. Their
1622
  only purpose is to manage the position and size of other layout elements. This
1623
  category of layout elements usually use QCPLayout as base class. Then there is
1624
  the category of layout elements which actually draw something. For example,
1625
  QCPAxisRect, QCPLegend and QCPPlotTitle are of this category. This does not
1626
  necessarily mean that the latter category can't have child layout elements.
1627
  QCPLegend for instance, actually derives from QCPLayoutGrid and the individual
1628
  legend items are child layout elements in the grid layout.
1629
*/
1630
1631
/* start documentation of inline functions */
1632
1633
/*! \fn QCPLayout *QCPLayoutElement::layout() const
1634
1635
  Returns the parent layout of this layout element.
1636
*/
1637
1638
/*! \fn QRect QCPLayoutElement::rect() const
1639
1640
  Returns the inner rect of this layout element. The inner rect is the outer
1641
  rect (\ref setOuterRect) shrinked by the margins (\ref setMargins, \ref
1642
  setAutoMargins).
1643
1644
  In some cases, the area between outer and inner rect is left blank. In other
1645
  cases the margin area is used to display peripheral graphics while the main
1646
  content is in the inner rect. This is where automatic margin calculation
1647
  becomes interesting because it allows the layout element to adapt the margins
1648
  to the peripheral graphics it wants to draw. For example, \ref QCPAxisRect
1649
  draws the axis labels and tick labels in the margin area, thus needs to adjust
1650
  the margins (if \ref setAutoMargins is enabled) according to the space
1651
  required by the labels of the axes.
1652
*/
1653
1654
/*! \fn virtual void QCPLayoutElement::mousePressEvent(QMouseEvent *event)
1655
1656
  This event is called, if the mouse was pressed while being inside the outer
1657
  rect of this layout element.
1658
*/
1659
1660
/*! \fn virtual void QCPLayoutElement::mouseMoveEvent(QMouseEvent *event)
1661
1662
  This event is called, if the mouse is moved inside the outer rect of this
1663
  layout element.
1664
*/
1665
1666
/*! \fn virtual void QCPLayoutElement::mouseReleaseEvent(QMouseEvent *event)
1667
1668
  This event is called, if the mouse was previously pressed inside the outer
1669
  rect of this layout element and is now released.
1670
*/
1671
1672
/*! \fn virtual void QCPLayoutElement::mouseDoubleClickEvent(QMouseEvent *event)
1673
1674
  This event is called, if the mouse is double-clicked inside the outer rect of
1675
  this layout element.
1676
*/
1677
1678
/*! \fn virtual void QCPLayoutElement::wheelEvent(QWheelEvent *event)
1679
1680
  This event is called, if the mouse wheel is scrolled while the cursor is
1681
  inside the rect of this layout element.
1682
*/
1683
1684
/* end documentation of inline functions */
1685
1686
/*!
1687
  Creates an instance of QCPLayoutElement and sets default values.
1688
*/
1689
QCPLayoutElement::QCPLayoutElement(QCustomPlot *parentPlot)
1690
    : QCPLayerable(
1691
          parentPlot),  // parenthood is changed as soon as layout element gets
1692
                        // inserted into a layout (except for top level layout)
1693
      mParentLayout(0),
1694
      mMinimumSize(),
1695
      mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
1696
      mRect(0, 0, 0, 0),
1697
      mOuterRect(0, 0, 0, 0),
1698
      mMargins(0, 0, 0, 0),
1699
      mMinimumMargins(0, 0, 0, 0),
1700
      mAutoMargins(QCP::msAll) {}
1701
1702
QCPLayoutElement::~QCPLayoutElement() {
1703
  setMarginGroup(QCP::msAll,
1704
                 0);  // unregister at margin groups, if there are any
1705
  // unregister at layout:
1706
  if (qobject_cast<QCPLayout *>(
1707
          mParentLayout))  // the qobject_cast is just a safeguard in case the
1708
                           // layout forgets to call clear() in its dtor and
1709
                           // this dtor is called by QObject dtor
1710
    mParentLayout->take(this);
1711
}
1712
1713
/*!
1714
  Sets the outer rect of this layout element. If the layout element is inside a
1715
  layout, the layout sets the position and size of this layout element using
1716
  this function.
1717
1718
  Calling this function externally has no effect, since the layout will
1719
  overwrite any changes to the outer rect upon the next replot.
1720
1721
  The layout element will adapt its inner \ref rect by applying the margins
1722
  inward to the outer rect.
1723
1724
  \see rect
1725
*/
1726
void QCPLayoutElement::setOuterRect(const QRect &rect) {
1727
  if (mOuterRect != rect) {
1728
    mOuterRect = rect;
1729
    mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(),
1730
                                -mMargins.right(), -mMargins.bottom());
1731
  }
1732
}
1733
1734
/*!
1735
  Sets the margins of this layout element. If \ref setAutoMargins is disabled
1736
  for some or all sides, this function is used to manually set the margin on
1737
  those sides. Sides that are still set to be handled automatically are ignored
1738
  and may have any value in \a margins.
1739
1740
  The margin is the distance between the outer rect (controlled by the parent
1741
  layout via \ref setOuterRect) and the inner \ref rect (which usually contains
1742
  the main content of this layout element).
1743
1744
  \see setAutoMargins
1745
*/
1746
void QCPLayoutElement::setMargins(const QMargins &margins) {
1747
  if (mMargins != margins) {
1748
    mMargins = margins;
1749
    mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(),
1750
                                -mMargins.right(), -mMargins.bottom());
1751
  }
1752
}
1753
1754
/*!
1755
  If \ref setAutoMargins is enabled on some or all margins, this function is
1756
  used to provide minimum values for those margins.
1757
1758
  The minimum values are not enforced on margin sides that were set to be under
1759
  manual control via \ref setAutoMargins.
1760
1761
  \see setAutoMargins
1762
*/
1763
void QCPLayoutElement::setMinimumMargins(const QMargins &margins) {
1764
  if (mMinimumMargins != margins) {
1765
    mMinimumMargins = margins;
1766
  }
1767
}
1768
1769
/*!
1770
  Sets on which sides the margin shall be calculated automatically. If a side is
1771
  calculated automatically, a minimum margin value may be provided with \ref
1772
  setMinimumMargins. If a side is set to be controlled manually, the value may
1773
  be specified with \ref setMargins.
1774
1775
  Margin sides that are under automatic control may participate in a \ref
1776
  QCPMarginGroup (see \ref setMarginGroup), to synchronize (align) it with other
1777
  layout elements in the plot.
1778
1779
  \see setMinimumMargins, setMargins
1780
*/
1781
void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides) {
1782
  mAutoMargins = sides;
1783
}
1784
1785
/*!
1786
  Sets the minimum size for the inner \ref rect of this layout element. A parent
1787
  layout tries to respect the \a size here by changing row/column sizes in the
1788
  layout accordingly.
1789
1790
  If the parent layout size is not sufficient to satisfy all minimum size
1791
  constraints of its child layout elements, the layout may set a size that is
1792
  actually smaller than \a size. QCustomPlot propagates the layout's size
1793
  constraints to the outside by setting its own minimum QWidget size
1794
  accordingly, so violations of \a size should be exceptions.
1795
*/
1796
void QCPLayoutElement::setMinimumSize(const QSize &size) {
1797
  if (mMinimumSize != size) {
1798
    mMinimumSize = size;
1799
    if (mParentLayout) mParentLayout->sizeConstraintsChanged();
1800
  }
1801
}
1802
1803
/*! \overload
1804
1805
  Sets the minimum size for the inner \ref rect of this layout element.
1806
*/
1807
void QCPLayoutElement::setMinimumSize(int width, int height) {
1808
  setMinimumSize(QSize(width, height));
1809
}
1810
1811
/*!
1812
  Sets the maximum size for the inner \ref rect of this layout element. A parent
1813
  layout tries to respect the \a size here by changing row/column sizes in the
1814
  layout accordingly.
1815
*/
1816
void QCPLayoutElement::setMaximumSize(const QSize &size) {
1817
  if (mMaximumSize != size) {
1818
    mMaximumSize = size;
1819
    if (mParentLayout) mParentLayout->sizeConstraintsChanged();
1820
  }
1821
}
1822
1823
/*! \overload
1824
1825
  Sets the maximum size for the inner \ref rect of this layout element.
1826
*/
1827
void QCPLayoutElement::setMaximumSize(int width, int height) {
1828
  setMaximumSize(QSize(width, height));
1829
}
1830
1831
/*!
1832
  Sets the margin \a group of the specified margin \a sides.
1833
1834
  Margin groups allow synchronizing specified margins across layout elements,
1835
  see the documentation of \ref QCPMarginGroup.
1836
1837
  To unset the margin group of \a sides, set \a group to 0.
1838
1839
  Note that margin groups only work for margin sides that are set to automatic
1840
  (\ref setAutoMargins).
1841
*/
1842
void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides,
1843
                                      QCPMarginGroup *group) {
1844
  QVector<QCP::MarginSide> sideVector;
1845
  if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
1846
  if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
1847
  if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
1848
  if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
1849
1850
  for (int i = 0; i < sideVector.size(); ++i) {
1851
    QCP::MarginSide side = sideVector.at(i);
1852
    if (marginGroup(side) != group) {
1853
      QCPMarginGroup *oldGroup = marginGroup(side);
1854
      if (oldGroup)  // unregister at old group
1855
        oldGroup->removeChild(side, this);
1856
1857
      if (!group)  // if setting to 0, remove hash entry. Else set hash entry to
1858
                   // new group and register there
1859
      {
1860
        mMarginGroups.remove(side);
1861
      } else  // setting to a new group
1862
      {
1863
        mMarginGroups[side] = group;
1864
        group->addChild(side, this);
1865
      }
1866
    }
1867
  }
1868
}
1869
1870
/*!
1871
  Updates the layout element and sub-elements. This function is automatically
1872
  called before every replot by the parent layout element. It is called multiple
1873
  times, once for every \ref UpdatePhase. The phases are run through in the
1874
  order of the enum values. For details about what happens at the different
1875
  phases, see the documentation of \ref UpdatePhase.
1876
1877
  Layout elements that have child elements should call the \ref update method of
1878
  their child elements, and pass the current \a phase unchanged.
1879
1880
  The default implementation executes the automatic margin mechanism in the \ref
1881
  upMargins phase. Subclasses should make sure to call the base class
1882
  implementation.
1883
*/
1884
void QCPLayoutElement::update(UpdatePhase phase) {
1885
  if (phase == upMargins) {
1886
    if (mAutoMargins != QCP::msNone) {
1887
      // set the margins of this layout element according to automatic margin
1888
      // calculation, either directly or via a margin group:
1889
      QMargins newMargins = mMargins;
1890
      QList<QCP::MarginSide> allMarginSides = QList<QCP::MarginSide>()
1891
                                              << QCP::msLeft << QCP::msRight
1892
                                              << QCP::msTop << QCP::msBottom;
1893
      foreach (QCP::MarginSide side, allMarginSides) {
1894
        if (mAutoMargins.testFlag(
1895
                side))  // this side's margin shall be calculated automatically
1896
        {
1897
          if (mMarginGroups.contains(side))
1898
            QCP::setMarginValue(
1899
                newMargins, side,
1900
                mMarginGroups[side]->commonMargin(
1901
                    side));  // this side is part of a margin group, so get the
1902
                             // margin value from that group
1903
          else
1904
            QCP::setMarginValue(
1905
                newMargins, side,
1906
                calculateAutoMargin(
1907
                    side));  // this side is not part of a group, so calculate
1908
                             // the value directly
1909
          // apply minimum margin restrictions:
1910
          if (QCP::getMarginValue(newMargins, side) <
1911
              QCP::getMarginValue(mMinimumMargins, side))
1912
            QCP::setMarginValue(newMargins, side,
1913
                                QCP::getMarginValue(mMinimumMargins, side));
1914
        }
1915
      }
1916
      setMargins(newMargins);
1917
    }
1918
  }
1919
}
1920
1921
/*!
1922
  Returns the minimum size this layout element (the inner \ref rect) may be
1923
  compressed to.
1924
1925
  if a minimum size (\ref setMinimumSize) was not set manually, parent layouts
1926
  consult this function to determine the minimum allowed size of this layout
1927
  element. (A manual minimum size is considered set if it is non-zero.)
1928
*/
1929
QSize QCPLayoutElement::minimumSizeHint() const { return mMinimumSize; }
1930
1931
/*!
1932
  Returns the maximum size this layout element (the inner \ref rect) may be
1933
  expanded to.
1934
1935
  if a maximum size (\ref setMaximumSize) was not set manually, parent layouts
1936
  consult this function to determine the maximum allowed size of this layout
1937
  element. (A manual maximum size is considered set if it is smaller than Qt's
1938
  QWIDGETSIZE_MAX.)
1939
*/
1940
QSize QCPLayoutElement::maximumSizeHint() const { return mMaximumSize; }
1941
1942
/*!
1943
  Returns a list of all child elements in this layout element. If \a recursive
1944
  is true, all sub-child elements are included in the list, too.
1945
1946
  \warning There may be entries with value 0 in the returned list. (For example,
1947
  QCPLayoutGrid may have empty cells which yield 0 at the respective index.)
1948
*/
1949
QList<QCPLayoutElement *> QCPLayoutElement::elements(bool recursive) const {
1950
  Q_UNUSED(recursive)
1951
  return QList<QCPLayoutElement *>();
1952
}
1953
1954
/*!
1955
  Layout elements are sensitive to events inside their outer rect. If \a pos is
1956
  within the outer rect, this method returns a value corresponding to 0.99 times
1957
  the parent plot's selection tolerance. However, layout elements are not
1958
  selectable by default. So if \a onlySelectable is true, -1.0 is returned.
1959
1960
  See \ref QCPLayerable::selectTest for a general explanation of this virtual
1961
  method.
1962
1963
  QCPLayoutElement subclasses may reimplement this method to provide more
1964
  specific selection test behaviour.
1965
*/
1966
double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable,
1967
                                    QVariant *details) const {
1968
  Q_UNUSED(details)
1969
1970
  if (onlySelectable) return -1;
1971
1972
  if (QRectF(mOuterRect).contains(pos)) {
1973
    if (mParentPlot)
1974
      return mParentPlot->selectionTolerance() * 0.99;
1975
    else {
1976
      qDebug() << Q_FUNC_INFO << "parent plot not defined";
1977
      return -1;
1978
    }
1979
  } else
1980
    return -1;
1981
}
1982
1983
/*! \internal
1984
1985
  propagates the parent plot initialization to all child elements, by calling
1986
  \ref QCPLayerable::initializeParentPlot on them.
1987
*/
1988
void QCPLayoutElement::parentPlotInitialized(QCustomPlot *parentPlot) {
1989
  foreach (QCPLayoutElement *el, elements(false)) {
1990
    if (!el->parentPlot()) el->initializeParentPlot(parentPlot);
1991
  }
1992
}
1993
1994
/*! \internal
1995
1996
  Returns the margin size for this \a side. It is used if automatic margins is
1997
  enabled for this \a side (see \ref setAutoMargins). If a minimum margin was
1998
  set with \ref setMinimumMargins, the returned value will not be smaller than
1999
  the specified minimum margin.
2000
2001
  The default implementation just returns the respective manual margin (\ref
2002
  setMargins) or the minimum margin, whichever is larger.
2003
*/
2004
int QCPLayoutElement::calculateAutoMargin(QCP::MarginSide side) {
2005
  return qMax(QCP::getMarginValue(mMargins, side),
2006
              QCP::getMarginValue(mMinimumMargins, side));
2007
}
2008
2009
////////////////////////////////////////////////////////////////////////////////////////////////////
2010
//////////////////// QCPLayout
2011
////////////////////////////////////////////////////////////////////////////////////////////////////
2012
2013
/*! \class QCPLayout
2014
  \brief The abstract base class for layouts
2015
2016
  This is an abstract base class for layout elements whose main purpose is to
2017
  define the position and size of other child layout elements. In most cases,
2018
  layouts don't draw anything themselves (but there are exceptions to this, e.g.
2019
  QCPLegend).
2020
2021
  QCPLayout derives from QCPLayoutElement, and thus can itself be nested in
2022
  other layouts.
2023
2024
  QCPLayout introduces a common interface for accessing and manipulating the
2025
  child elements. Those functions are most notably \ref elementCount, \ref
2026
  elementAt, \ref takeAt, \ref take, \ref simplify, \ref removeAt, \ref remove
2027
  and \ref clear. Individual subclasses may add more functions to this interface
2028
  which are more specialized to the form of the layout. For example, \ref
2029
  QCPLayoutGrid adds functions that take row and column indices to access cells
2030
  of the layout grid more conveniently.
2031
2032
  Since this is an abstract base class, you can't instantiate it directly.
2033
  Rather use one of its subclasses like QCPLayoutGrid or QCPLayoutInset.
2034
2035
  For a general introduction to the layout system, see the dedicated
2036
  documentation page \ref thelayoutsystem "The Layout System".
2037
*/
2038
2039
/* start documentation of pure virtual functions */
2040
2041
/*! \fn virtual int QCPLayout::elementCount() const = 0
2042
2043
  Returns the number of elements/cells in the layout.
2044
2045
  \see elements, elementAt
2046
*/
2047
2048
/*! \fn virtual QCPLayoutElement* QCPLayout::elementAt(int index) const = 0
2049
2050
  Returns the element in the cell with the given \a index. If \a index is
2051
  invalid, returns 0.
2052
2053
  Note that even if \a index is valid, the respective cell may be empty in some
2054
  layouts (e.g. QCPLayoutGrid), so this function may return 0 in those cases.
2055
  You may use this function to check whether a cell is empty or not.
2056
2057
  \see elements, elementCount, takeAt
2058
*/
2059
2060
/*! \fn virtual QCPLayoutElement* QCPLayout::takeAt(int index) = 0
2061
2062
  Removes the element with the given \a index from the layout and returns it.
2063
2064
  If the \a index is invalid or the cell with that index is empty, returns 0.
2065
2066
  Note that some layouts don't remove the respective cell right away but leave
2067
  an empty cell after successful removal of the layout element. To collapse
2068
  empty cells, use \ref simplify.
2069
2070
  \see elementAt, take
2071
*/
2072
2073
/*! \fn virtual bool QCPLayout::take(QCPLayoutElement* element) = 0
2074
2075
  Removes the specified \a element from the layout and returns true on success.
2076
2077
  If the \a element isn't in this layout, returns false.
2078
2079
  Note that some layouts don't remove the respective cell right away but leave
2080
  an empty cell after successful removal of the layout element. To collapse
2081
  empty cells, use \ref simplify.
2082
2083
  \see takeAt
2084
*/
2085
2086
/* end documentation of pure virtual functions */
2087
2088
/*!
2089
  Creates an instance of QCPLayout and sets default values. Note that since
2090
  QCPLayout is an abstract base class, it can't be instantiated directly.
2091
*/
2092
QCPLayout::QCPLayout() {}
2093
2094
/*!
2095
  First calls the QCPLayoutElement::update base class implementation to update
2096
  the margins on this layout.
2097
2098
  Then calls \ref updateLayout which subclasses reimplement to reposition and
2099
  resize their cells.
2100
2101
  Finally, \ref update is called on all child elements.
2102
*/
2103
void QCPLayout::update(UpdatePhase phase) {
2104
  QCPLayoutElement::update(phase);
2105
2106
  // set child element rects according to layout:
2107
  if (phase == upLayout) updateLayout();
2108
2109
  // propagate update call to child elements:
2110
  const int elCount = elementCount();
2111
  for (int i = 0; i < elCount; ++i) {
2112
    if (QCPLayoutElement *el = elementAt(i)) el->update(phase);
2113
  }
2114
}
2115
2116
/* inherits documentation from base class */
2117
QList<QCPLayoutElement *> QCPLayout::elements(bool recursive) const {
2118
  const int c = elementCount();
2119
  QList<QCPLayoutElement *> result;
2120
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2121
  result.reserve(c);
2122
#endif
2123
  for (int i = 0; i < c; ++i) result.append(elementAt(i));
2124
  if (recursive) {
2125
    for (int i = 0; i < c; ++i) {
2126
      if (result.at(i)) result << result.at(i)->elements(recursive);
2127
    }
2128
  }
2129
  return result;
2130
}
2131
2132
/*!
2133
  Simplifies the layout by collapsing empty cells. The exact behavior depends on
2134
  subclasses, the default implementation does nothing.
2135
2136
  Not all layouts need simplification. For example, QCPLayoutInset doesn't use
2137
  explicit simplification while QCPLayoutGrid does.
2138
*/
2139
void QCPLayout::simplify() {}
2140
2141
/*!
2142
  Removes and deletes the element at the provided \a index. Returns true on
2143
  success. If \a index is invalid or points to an empty cell, returns false.
2144
2145
  This function internally uses \ref takeAt to remove the element from the
2146
  layout and then deletes the returned element. Note that some layouts don't
2147
  remove the respective cell right away but leave an empty cell after successful
2148
  removal of the layout element. To collapse empty cells, use \ref simplify.
2149
2150
  \see remove, takeAt
2151
*/
2152
bool QCPLayout::removeAt(int index) {
2153
  if (QCPLayoutElement *el = takeAt(index)) {
2154
    delete el;
2155
    return true;
2156
  } else
2157
    return false;
2158
}
2159
2160
/*!
2161
  Removes and deletes the provided \a element. Returns true on success. If \a
2162
  element is not in the layout, returns false.
2163
2164
  This function internally uses \ref takeAt to remove the element from the
2165
  layout and then deletes the element. Note that some layouts don't remove the
2166
  respective cell right away but leave an empty cell after successful removal of
2167
  the layout element. To collapse empty cells, use \ref simplify.
2168
2169
  \see removeAt, take
2170
*/
2171
bool QCPLayout::remove(QCPLayoutElement *element) {
2172
  if (take(element)) {
2173
    delete element;
2174
    return true;
2175
  } else
2176
    return false;
2177
}
2178
2179
/*!
2180
  Removes and deletes all layout elements in this layout. Finally calls \ref
2181
  simplify to make sure all empty cells are collapsed.
2182
2183
  \see remove, removeAt
2184
*/
2185
void QCPLayout::clear() {
2186
  for (int i = elementCount() - 1; i >= 0; --i) {
2187
    if (elementAt(i)) removeAt(i);
2188
  }
2189
  simplify();
2190
}
2191
2192
/*!
2193
  Subclasses call this method to report changed (minimum/maximum) size
2194
  constraints.
2195
2196
  If the parent of this layout is again a QCPLayout, forwards the call to the
2197
  parent's \ref sizeConstraintsChanged. If the parent is a QWidget (i.e. is the
2198
  \ref QCustomPlot::plotLayout of QCustomPlot), calls QWidget::updateGeometry,
2199
  so if the QCustomPlot widget is inside a Qt QLayout, it may update itself and
2200
  resize cells accordingly.
2201
*/
2202
void QCPLayout::sizeConstraintsChanged() const {
2203
  if (QWidget *w = qobject_cast<QWidget *>(parent()))
2204
    w->updateGeometry();
2205
  else if (QCPLayout *l = qobject_cast<QCPLayout *>(parent()))
2206
    l->sizeConstraintsChanged();
2207
}
2208
2209
/*! \internal
2210
2211
  Subclasses reimplement this method to update the position and sizes of the
2212
  child elements/cells via calling their \ref QCPLayoutElement::setOuterRect.
2213
  The default implementation does nothing.
2214
2215
  The geometry used as a reference is the inner \ref rect of this layout. Child
2216
  elements should stay within that rect.
2217
2218
  \ref getSectionSizes may help with the reimplementation of this function.
2219
2220
  \see update
2221
*/
2222
void QCPLayout::updateLayout() {}
2223
2224
/*! \internal
2225
2226
  Associates \a el with this layout. This is done by setting the \ref
2227
  QCPLayoutElement::layout, the \ref QCPLayerable::parentLayerable and the
2228
  QObject parent to this layout.
2229
2230
  Further, if \a el didn't previously have a parent plot, calls \ref
2231
  QCPLayerable::initializeParentPlot on \a el to set the paret plot.
2232
2233
  This method is used by subclass specific methods that add elements to the
2234
  layout. Note that this method only changes properties in \a el. The removal
2235
  from the old layout and the insertion into the new layout must be done
2236
  additionally.
2237
*/
2238
void QCPLayout::adoptElement(QCPLayoutElement *el) {
2239
  if (el) {
2240
    el->mParentLayout = this;
2241
    el->setParentLayerable(this);
2242
    el->setParent(this);
2243
    if (!el->parentPlot()) el->initializeParentPlot(mParentPlot);
2244
  } else
2245
    qDebug() << Q_FUNC_INFO << "Null element passed";
2246
}
2247
2248
/*! \internal
2249
2250
  Disassociates \a el from this layout. This is done by setting the \ref
2251
  QCPLayoutElement::layout and the \ref QCPLayerable::parentLayerable to zero.
2252
  The QObject parent is set to the parent QCustomPlot.
2253
2254
  This method is used by subclass specific methods that remove elements from the
2255
  layout (e.g. \ref take or \ref takeAt). Note that this method only changes
2256
  properties in \a el. The removal from the old layout must be done
2257
  additionally.
2258
*/
2259
void QCPLayout::releaseElement(QCPLayoutElement *el) {
2260
  if (el) {
2261
    el->mParentLayout = 0;
2262
    el->setParentLayerable(0);
2263
    el->setParent(mParentPlot);
2264
    // Note: Don't initializeParentPlot(0) here, because layout element will
2265
    // stay in same parent plot
2266
  } else
2267
    qDebug() << Q_FUNC_INFO << "Null element passed";
2268
}
2269
2270
/*! \internal
2271
2272
  This is a helper function for the implementation of \ref updateLayout in
2273
  subclasses.
2274
2275
  It calculates the sizes of one-dimensional sections with provided constraints
2276
  on maximum section sizes, minimum section sizes, relative stretch factors and
2277
  the final total size of all sections.
2278
2279
  The QVector entries refer to the sections. Thus all QVectors must have the
2280
  same size.
2281
2282
  \a maxSizes gives the maximum allowed size of each section. If there shall be
2283
  no maximum size imposed, set all vector values to Qt's QWIDGETSIZE_MAX.
2284
2285
  \a minSizes gives the minimum allowed size of each section. If there shall be
2286
  no minimum size imposed, set all vector values to zero. If the \a minSizes
2287
  entries add up to a value greater than \a totalSize, sections will be scaled
2288
  smaller than the proposed minimum sizes. (In other words, not exceeding the
2289
  allowed total size is taken to be more important than not going below minimum
2290
  section sizes.)
2291
2292
  \a stretchFactors give the relative proportions of the sections to each other.
2293
  If all sections shall be scaled equally, set all values equal. If the first
2294
  section shall be double the size of each individual other section, set the
2295
  first number of \a stretchFactors to double the value of the other individual
2296
  values (e.g. {2, 1, 1, 1}).
2297
2298
  \a totalSize is the value that the final section sizes will add up to. Due to
2299
  rounding, the actual sum may differ slightly. If you want the section sizes to
2300
  sum up to exactly that value, you could distribute the remaining difference on
2301
  the sections.
2302
2303
  The return value is a QVector containing the section sizes.
2304
*/
2305
QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes,
2306
                                        QVector<int> minSizes,
2307
                                        QVector<double> stretchFactors,
2308
                                        int totalSize) const {
2309
  if (maxSizes.size() != minSizes.size() ||
2310
      minSizes.size() != stretchFactors.size()) {
2311
    qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes
2312
             << minSizes << stretchFactors;
2313
    return QVector<int>();
2314
  }
2315
  if (stretchFactors.isEmpty()) return QVector<int>();
2316
  int sectionCount = stretchFactors.size();
2317
  QVector<double> sectionSizes(sectionCount);
2318
  // if provided total size is forced smaller than total minimum size, ignore
2319
  // minimum sizes (squeeze sections):
2320
  int minSizeSum = 0;
2321
  for (int i = 0; i < sectionCount; ++i) minSizeSum += minSizes.at(i);
2322
  if (totalSize < minSizeSum) {
2323
    // new stretch factors are minimum sizes and minimum sizes are set to zero:
2324
    for (int i = 0; i < sectionCount; ++i) {
2325
      stretchFactors[i] = minSizes.at(i);
2326
      minSizes[i] = 0;
2327
    }
2328
  }
2329
2330
  QList<int> minimumLockedSections;
2331
  QList<int> unfinishedSections;
2332
  for (int i = 0; i < sectionCount; ++i) unfinishedSections.append(i);
2333
  double freeSize = totalSize;
2334
2335
  int outerIterations = 0;
2336
  while (!unfinishedSections.isEmpty() &&
2337
         outerIterations <
2338
             sectionCount * 2)  // the iteration check ist just a failsafe in
2339
                                // case something really strange happens
2340
  {
2341
    ++outerIterations;
2342
    int innerIterations = 0;
2343
    while (!unfinishedSections.isEmpty() &&
2344
           innerIterations <
2345
               sectionCount * 2)  // the iteration check ist just a failsafe in
2346
                                  // case something really strange happens
2347
    {
2348
      ++innerIterations;
2349
      // find section that hits its maximum next:
2350
      int nextId = -1;
2351
      double nextMax = 1e12;
2352
      for (int i = 0; i < unfinishedSections.size(); ++i) {
2353
        int secId = unfinishedSections.at(i);
2354
        double hitsMaxAt = (maxSizes.at(secId) - sectionSizes.at(secId)) /
2355
                           stretchFactors.at(secId);
2356
        if (hitsMaxAt < nextMax) {
2357
          nextMax = hitsMaxAt;
2358
          nextId = secId;
2359
        }
2360
      }
2361
      // check if that maximum is actually within the bounds of the total size
2362
      // (i.e. can we stretch all remaining sections so far that the found
2363
      // section actually hits its maximum, without exceeding the total size
2364
      // when we add up all sections)
2365
      double stretchFactorSum = 0;
2366
      for (int i = 0; i < unfinishedSections.size(); ++i)
2367
        stretchFactorSum += stretchFactors.at(unfinishedSections.at(i));
2368
      double nextMaxLimit = freeSize / stretchFactorSum;
2369
      if (nextMax <
2370
          nextMaxLimit)  // next maximum is actually hit, move forward to that
2371
                         // point and fix the size of that section
2372
      {
2373
        for (int i = 0; i < unfinishedSections.size(); ++i) {
2374
          sectionSizes[unfinishedSections.at(i)] +=
2375
              nextMax * stretchFactors.at(unfinishedSections.at(
2376
                            i));  // increment all sections
2377
          freeSize -= nextMax * stretchFactors.at(unfinishedSections.at(i));
2378
        }
2379
        unfinishedSections.removeOne(
2380
            nextId);  // exclude the section that is now at maximum from further
2381
                      // changes
2382
      } else  // next maximum isn't hit, just distribute rest of free space on
2383
              // remaining sections
2384
      {
2385
        for (int i = 0; i < unfinishedSections.size(); ++i)
2386
          sectionSizes[unfinishedSections.at(i)] +=
2387
              nextMaxLimit * stretchFactors.at(unfinishedSections.at(
2388
                                 i));  // increment all sections
2389
        unfinishedSections.clear();
2390
      }
2391
    }
2392
    if (innerIterations == sectionCount * 2)
2393
      qDebug() << Q_FUNC_INFO
2394
               << "Exceeded maximum expected inner iteration count, layouting "
2395
                  "aborted. Input was:"
2396
               << maxSizes << minSizes << stretchFactors << totalSize;
2397
2398
    // now check whether the resulting section sizes violate minimum
2399
    // restrictions:
2400
    bool foundMinimumViolation = false;
2401
    for (int i = 0; i < sectionSizes.size(); ++i) {
2402
      if (minimumLockedSections.contains(i)) continue;
2403
      if (sectionSizes.at(i) < minSizes.at(i))  // section violates minimum
2404
      {
2405
        sectionSizes[i] = minSizes.at(i);  // set it to minimum
2406
        foundMinimumViolation =
2407
            true;  // make sure we repeat the whole optimization process
2408
        minimumLockedSections.append(i);
2409
      }
2410
    }
2411
    if (foundMinimumViolation) {
2412
      freeSize = totalSize;
2413
      for (int i = 0; i < sectionCount; ++i) {
2414
        if (!minimumLockedSections.contains(
2415
                i))  // only put sections that haven't hit their minimum back
2416
                     // into the pool
2417
          unfinishedSections.append(i);
2418
        else
2419
          freeSize -=
2420
              sectionSizes.at(i);  // remove size of minimum locked sections
2421
                                   // from available space in next round
2422
      }
2423
      // reset all section sizes to zero that are in unfinished sections (all
2424
      // others have been set to their minimum):
2425
      for (int i = 0; i < unfinishedSections.size(); ++i)
2426
        sectionSizes[unfinishedSections.at(i)] = 0;
2427
    }
2428
  }
2429
  if (outerIterations == sectionCount * 2)
2430
    qDebug() << Q_FUNC_INFO
2431
             << "Exceeded maximum expected outer iteration count, layouting "
2432
                "aborted. Input was:"
2433
             << maxSizes << minSizes << stretchFactors << totalSize;
2434
2435
  QVector<int> result(sectionCount);
2436
  for (int i = 0; i < sectionCount; ++i) result[i] = qRound(sectionSizes.at(i));
2437
  return result;
2438
}
2439
2440
////////////////////////////////////////////////////////////////////////////////////////////////////
2441
//////////////////// QCPLayoutGrid
2442
////////////////////////////////////////////////////////////////////////////////////////////////////
2443
2444
/*! \class QCPLayoutGrid
2445
  \brief A layout that arranges child elements in a grid
2446
2447
  Elements are laid out in a grid with configurable stretch factors (\ref
2448
  setColumnStretchFactor, \ref setRowStretchFactor) and spacing (\ref
2449
  setColumnSpacing, \ref setRowSpacing).
2450
2451
  Elements can be added to cells via \ref addElement. The grid is expanded if
2452
  the specified row or column doesn't exist yet. Whether a cell contains a valid
2453
  layout element can be checked with \ref hasElement, that element can be
2454
  retrieved with \ref element. If rows and columns that only have empty cells
2455
  shall be removed, call \ref simplify. Removal of elements is either done by
2456
  just adding the element to a different layout or by using the QCPLayout
2457
  interface \ref take or \ref remove.
2458
2459
  Row and column insertion can be performed with \ref insertRow and \ref
2460
  insertColumn.
2461
*/
2462
2463
/*!
2464
  Creates an instance of QCPLayoutGrid and sets default values.
2465
*/
2466
QCPLayoutGrid::QCPLayoutGrid() : mColumnSpacing(5), mRowSpacing(5) {}
2467
2468
QCPLayoutGrid::~QCPLayoutGrid() {
2469
  // clear all child layout elements. This is important because only the
2470
  // specific layouts know how to handle removing elements (clear calls virtual
2471
  // removeAt method to do that).
2472
  clear();
2473
}
2474
2475
/*!
2476
  Returns the element in the cell in \a row and \a column.
2477
2478
  Returns 0 if either the row/column is invalid or if the cell is empty. In
2479
  those cases, a qDebug message is printed. To check whether a cell exists and
2480
  isn't empty, use \ref hasElement.
2481
2482
  \see addElement, hasElement
2483
*/
2484
QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const {
2485
  if (row >= 0 && row < mElements.size()) {
2486
    if (column >= 0 && column < mElements.first().size()) {
2487
      if (QCPLayoutElement *result = mElements.at(row).at(column))
2488
        return result;
2489
      else
2490
        qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row
2491
                 << "Column:" << column;
2492
    } else
2493
      qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row
2494
               << "Column:" << column;
2495
  } else
2496
    qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row
2497
             << "Column:" << column;
2498
  return 0;
2499
}
2500
2501
/*!
2502
  Returns the number of rows in the layout.
2503
2504
  \see columnCount
2505
*/
2506
int QCPLayoutGrid::rowCount() const { return mElements.size(); }
2507
2508
/*!
2509
  Returns the number of columns in the layout.
2510
2511
  \see rowCount
2512
*/
2513
int QCPLayoutGrid::columnCount() const {
2514
  if (mElements.size() > 0)
2515
    return mElements.first().size();
2516
  else
2517
    return 0;
2518
}
2519
2520
/*!
2521
  Adds the \a element to cell with \a row and \a column. If \a element is
2522
  already in a layout, it is first removed from there. If \a row or \a column
2523
  don't exist yet, the layout is expanded accordingly.
2524
2525
  Returns true if the element was added successfully, i.e. if the cell at \a row
2526
  and \a column didn't already have an element.
2527
2528
  \see element, hasElement, take, remove
2529
*/
2530
bool QCPLayoutGrid::addElement(int row, int column, QCPLayoutElement *element) {
2531
  if (element) {
2532
    if (!hasElement(row, column)) {
2533
      if (element->layout())  // remove from old layout first
2534
        element->layout()->take(element);
2535
      expandTo(row + 1, column + 1);
2536
      mElements[row][column] = element;
2537
      adoptElement(element);
2538
      return true;
2539
    } else
2540
      qDebug() << Q_FUNC_INFO
2541
               << "There is already an element in the specified row/column:"
2542
               << row << column;
2543
  } else
2544
    qDebug() << Q_FUNC_INFO << "Can't add null element to row/column:" << row
2545
             << column;
2546
  return false;
2547
}
2548
2549
/*!
2550
  Returns whether the cell at \a row and \a column exists and contains a valid
2551
  element, i.e. isn't empty.
2552
2553
  \see element
2554
*/
2555
bool QCPLayoutGrid::hasElement(int row, int column) {
2556
  if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
2557
    return mElements.at(row).at(column);
2558
  else
2559
    return false;
2560
}
2561
2562
/*!
2563
  Sets the stretch \a factor of \a column.
2564
2565
  Stretch factors control the relative sizes of rows and columns. Cells will not
2566
  be resized beyond their minimum and maximum widths/heights (\ref
2567
  QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize),
2568
  regardless of the stretch factor.
2569
2570
  The default stretch factor of newly created rows/columns is 1.
2571
2572
  \see setColumnStretchFactors, setRowStretchFactor
2573
*/
2574
void QCPLayoutGrid::setColumnStretchFactor(int column, double factor) {
2575
  if (column >= 0 && column < columnCount()) {
2576
    if (factor > 0)
2577
      mColumnStretchFactors[column] = factor;
2578
    else
2579
      qDebug() << Q_FUNC_INFO
2580
               << "Invalid stretch factor, must be positive:" << factor;
2581
  } else
2582
    qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
2583
}
2584
2585
/*!
2586
  Sets the stretch \a factors of all columns. \a factors must have the size \ref
2587
  columnCount.
2588
2589
  Stretch factors control the relative sizes of rows and columns. Cells will not
2590
  be resized beyond their minimum and maximum widths/heights (\ref
2591
  QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize),
2592
  regardless of the stretch factor.
2593
2594
  The default stretch factor of newly created rows/columns is 1.
2595
2596
  \see setColumnStretchFactor, setRowStretchFactors
2597
*/
2598
void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors) {
2599
  if (factors.size() == mColumnStretchFactors.size()) {
2600
    mColumnStretchFactors = factors;
2601
    for (int i = 0; i < mColumnStretchFactors.size(); ++i) {
2602
      if (mColumnStretchFactors.at(i) <= 0) {
2603
        qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:"
2604
                 << mColumnStretchFactors.at(i);
2605
        mColumnStretchFactors[i] = 1;
2606
      }
2607
    }
2608
  } else
2609
    qDebug() << Q_FUNC_INFO
2610
             << "Column count not equal to passed stretch factor count:"
2611
             << factors;
2612
}
2613
2614
/*!
2615
  Sets the stretch \a factor of \a row.
2616
2617
  Stretch factors control the relative sizes of rows and columns. Cells will not
2618
  be resized beyond their minimum and maximum widths/heights (\ref
2619
  QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize),
2620
  regardless of the stretch factor.
2621
2622
  The default stretch factor of newly created rows/columns is 1.
2623
2624
  \see setColumnStretchFactors, setRowStretchFactor
2625
*/
2626
void QCPLayoutGrid::setRowStretchFactor(int row, double factor) {
2627
  if (row >= 0 && row < rowCount()) {
2628
    if (factor > 0)
2629
      mRowStretchFactors[row] = factor;
2630
    else
2631
      qDebug() << Q_FUNC_INFO
2632
               << "Invalid stretch factor, must be positive:" << factor;
2633
  } else
2634
    qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
2635
}
2636
2637
/*!
2638
  Sets the stretch \a factors of all rows. \a factors must have the size \ref
2639
  rowCount.
2640
2641
  Stretch factors control the relative sizes of rows and columns. Cells will not
2642
  be resized beyond their minimum and maximum widths/heights (\ref
2643
  QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize),
2644
  regardless of the stretch factor.
2645
2646
  The default stretch factor of newly created rows/columns is 1.
2647
2648
  \see setRowStretchFactor, setColumnStretchFactors
2649
*/
2650
void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors) {
2651
  if (factors.size() == mRowStretchFactors.size()) {
2652
    mRowStretchFactors = factors;
2653
    for (int i = 0; i < mRowStretchFactors.size(); ++i) {
2654
      if (mRowStretchFactors.at(i) <= 0) {
2655
        qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:"
2656
                 << mRowStretchFactors.at(i);
2657
        mRowStretchFactors[i] = 1;
2658
      }
2659
    }
2660
  } else
2661
    qDebug() << Q_FUNC_INFO
2662
             << "Row count not equal to passed stretch factor count:"
2663
             << factors;
2664
}
2665
2666
/*!
2667
  Sets the gap that is left blank between columns to \a pixels.
2668
2669
  \see setRowSpacing
2670
*/
2671
void QCPLayoutGrid::setColumnSpacing(int pixels) { mColumnSpacing = pixels; }
2672
2673
/*!
2674
  Sets the gap that is left blank between rows to \a pixels.
2675
2676
  \see setColumnSpacing
2677
*/
2678
void QCPLayoutGrid::setRowSpacing(int pixels) { mRowSpacing = pixels; }
2679
2680
/*!
2681
  Expands the layout to have \a newRowCount rows and \a newColumnCount columns.
2682
  So the last valid row index will be \a newRowCount-1, the last valid column
2683
  index will be \a newColumnCount-1.
2684
2685
  If the current column/row count is already larger or equal to \a
2686
  newColumnCount/\a newRowCount, this function does nothing in that dimension.
2687
2688
  Newly created cells are empty, new rows and columns have the stretch factor 1.
2689
2690
  Note that upon a call to \ref addElement, the layout is expanded automatically
2691
  to contain the specified row and column, using this function.
2692
2693
  \see simplify
2694
*/
2695
void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount) {
2696
  // add rows as necessary:
2697
  while (rowCount() < newRowCount) {
2698
    mElements.append(QList<QCPLayoutElement *>());
2699
    mRowStretchFactors.append(1);
2700
  }
2701
  // go through rows and expand columns as necessary:
2702
  int newColCount = qMax(columnCount(), newColumnCount);
2703
  for (int i = 0; i < rowCount(); ++i) {
2704
    while (mElements.at(i).size() < newColCount) mElements[i].append(0);
2705
  }
2706
  while (mColumnStretchFactors.size() < newColCount)
2707
    mColumnStretchFactors.append(1);
2708
}
2709
2710
/*!
2711
  Inserts a new row with empty cells at the row index \a newIndex. Valid values
2712
  for \a newIndex range from 0 (inserts a row at the top) to \a rowCount
2713
  (appends a row at the bottom).
2714
2715
  \see insertColumn
2716
*/
2717
void QCPLayoutGrid::insertRow(int newIndex) {
2718
  if (mElements.isEmpty() ||
2719
      mElements.first()
2720
          .isEmpty())  // if grid is completely empty, add first cell
2721
  {
2722
    expandTo(1, 1);
2723
    return;
2724
  }
2725
2726
  if (newIndex < 0) newIndex = 0;
2727
  if (newIndex > rowCount()) newIndex = rowCount();
2728
2729
  mRowStretchFactors.insert(newIndex, 1);
2730
  QList<QCPLayoutElement *> newRow;
2731
  for (int col = 0; col < columnCount(); ++col)
2732
    newRow.append((QCPLayoutElement *)0);
2733
  mElements.insert(newIndex, newRow);
2734
}
2735
2736
/*!
2737
  Inserts a new column with empty cells at the column index \a newIndex. Valid
2738
  values for \a newIndex range from 0 (inserts a row at the left) to \a rowCount
2739
  (appends a row at the right).
2740
2741
  \see insertRow
2742
*/
2743
void QCPLayoutGrid::insertColumn(int newIndex) {
2744
  if (mElements.isEmpty() ||
2745
      mElements.first()
2746
          .isEmpty())  // if grid is completely empty, add first cell
2747
  {
2748
    expandTo(1, 1);
2749
    return;
2750
  }
2751
2752
  if (newIndex < 0) newIndex = 0;
2753
  if (newIndex > columnCount()) newIndex = columnCount();
2754
2755
  mColumnStretchFactors.insert(newIndex, 1);
2756
  for (int row = 0; row < rowCount(); ++row)
2757
    mElements[row].insert(newIndex, (QCPLayoutElement *)0);
2758
}
2759
2760
/* inherits documentation from base class */
2761
void QCPLayoutGrid::updateLayout() {
2762
  QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
2763
  getMinimumRowColSizes(&minColWidths, &minRowHeights);
2764
  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2765
2766
  int totalRowSpacing = (rowCount() - 1) * mRowSpacing;
2767
  int totalColSpacing = (columnCount() - 1) * mColumnSpacing;
2768
  QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths,
2769
                                           mColumnStretchFactors.toVector(),
2770
                                           mRect.width() - totalColSpacing);
2771
  QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights,
2772
                                            mRowStretchFactors.toVector(),
2773
                                            mRect.height() - totalRowSpacing);
2774
2775
  // go through cells and set rects accordingly:
2776
  int yOffset = mRect.top();
2777
  for (int row = 0; row < rowCount(); ++row) {
2778
    if (row > 0) yOffset += rowHeights.at(row - 1) + mRowSpacing;
2779
    int xOffset = mRect.left();
2780
    for (int col = 0; col < columnCount(); ++col) {
2781
      if (col > 0) xOffset += colWidths.at(col - 1) + mColumnSpacing;
2782
      if (mElements.at(row).at(col))
2783
        mElements.at(row).at(col)->setOuterRect(
2784
            QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row)));
2785
    }
2786
  }
2787
}
2788
2789
/* inherits documentation from base class */
2790
int QCPLayoutGrid::elementCount() const { return rowCount() * columnCount(); }
2791
2792
/* inherits documentation from base class */
2793
QCPLayoutElement *QCPLayoutGrid::elementAt(int index) const {
2794
  if (index >= 0 && index < elementCount())
2795
    return mElements.at(index / columnCount()).at(index % columnCount());
2796
  else
2797
    return 0;
2798
}
2799
2800
/* inherits documentation from base class */
2801
QCPLayoutElement *QCPLayoutGrid::takeAt(int index) {
2802
  if (QCPLayoutElement *el = elementAt(index)) {
2803
    releaseElement(el);
2804
    mElements[index / columnCount()][index % columnCount()] = 0;
2805
    return el;
2806
  } else {
2807
    qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
2808
    return 0;
2809
  }
2810
}
2811
2812
/* inherits documentation from base class */
2813
bool QCPLayoutGrid::take(QCPLayoutElement *element) {
2814
  if (element) {
2815
    for (int i = 0; i < elementCount(); ++i) {
2816
      if (elementAt(i) == element) {
2817
        takeAt(i);
2818
        return true;
2819
      }
2820
    }
2821
    qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
2822
  } else
2823
    qDebug() << Q_FUNC_INFO << "Can't take null element";
2824
  return false;
2825
}
2826
2827
/* inherits documentation from base class */
2828
QList<QCPLayoutElement *> QCPLayoutGrid::elements(bool recursive) const {
2829
  QList<QCPLayoutElement *> result;
2830
  int colC = columnCount();
2831
  int rowC = rowCount();
2832
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2833
  result.reserve(colC * rowC);
2834
#endif
2835
  for (int row = 0; row < rowC; ++row) {
2836
    for (int col = 0; col < colC; ++col) {
2837
      result.append(mElements.at(row).at(col));
2838
    }
2839
  }
2840
  if (recursive) {
2841
    int c = result.size();
2842
    for (int i = 0; i < c; ++i) {
2843
      if (result.at(i)) result << result.at(i)->elements(recursive);
2844
    }
2845
  }
2846
  return result;
2847
}
2848
2849
/*!
2850
  Simplifies the layout by collapsing rows and columns which only contain empty
2851
  cells.
2852
*/
2853
void QCPLayoutGrid::simplify() {
2854
  // remove rows with only empty cells:
2855
  for (int row = rowCount() - 1; row >= 0; --row) {
2856
    bool hasElements = false;
2857
    for (int col = 0; col < columnCount(); ++col) {
2858
      if (mElements.at(row).at(col)) {
2859
        hasElements = true;
2860
        break;
2861
      }
2862
    }
2863
    if (!hasElements) {
2864
      mRowStretchFactors.removeAt(row);
2865
      mElements.removeAt(row);
2866
      if (mElements.isEmpty())  // removed last element, also remove stretch
2867
                                // factor (wouldn't happen below because also
2868
                                // columnCount changed to 0 now)
2869
        mColumnStretchFactors.clear();
2870
    }
2871
  }
2872
2873
  // remove columns with only empty cells:
2874
  for (int col = columnCount() - 1; col >= 0; --col) {
2875
    bool hasElements = false;
2876
    for (int row = 0; row < rowCount(); ++row) {
2877
      if (mElements.at(row).at(col)) {
2878
        hasElements = true;
2879
        break;
2880
      }
2881
    }
2882
    if (!hasElements) {
2883
      mColumnStretchFactors.removeAt(col);
2884
      for (int row = 0; row < rowCount(); ++row) mElements[row].removeAt(col);
2885
    }
2886
  }
2887
}
2888
2889
/* inherits documentation from base class */
2890
QSize QCPLayoutGrid::minimumSizeHint() const {
2891
  QVector<int> minColWidths, minRowHeights;
2892
  getMinimumRowColSizes(&minColWidths, &minRowHeights);
2893
  QSize result(0, 0);
2894
  for (int i = 0; i < minColWidths.size(); ++i)
2895
    result.rwidth() += minColWidths.at(i);
2896
  for (int i = 0; i < minRowHeights.size(); ++i)
2897
    result.rheight() += minRowHeights.at(i);
2898
  result.rwidth() += qMax(0, columnCount() - 1) * mColumnSpacing +
2899
                     mMargins.left() + mMargins.right();
2900
  result.rheight() += qMax(0, rowCount() - 1) * mRowSpacing + mMargins.top() +
2901
                      mMargins.bottom();
2902
  return result;
2903
}
2904
2905
/* inherits documentation from base class */
2906
QSize QCPLayoutGrid::maximumSizeHint() const {
2907
  QVector<int> maxColWidths, maxRowHeights;
2908
  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2909
2910
  QSize result(0, 0);
2911
  for (int i = 0; i < maxColWidths.size(); ++i)
2912
    result.setWidth(qMin(result.width() + maxColWidths.at(i), QWIDGETSIZE_MAX));
2913
  for (int i = 0; i < maxRowHeights.size(); ++i)
2914
    result.setHeight(
2915
        qMin(result.height() + maxRowHeights.at(i), QWIDGETSIZE_MAX));
2916
  result.rwidth() += qMax(0, columnCount() - 1) * mColumnSpacing +
2917
                     mMargins.left() + mMargins.right();
2918
  result.rheight() += qMax(0, rowCount() - 1) * mRowSpacing + mMargins.top() +
2919
                      mMargins.bottom();
2920
  return result;
2921
}
2922
2923
/*! \internal
2924
2925
  Places the minimum column widths and row heights into \a minColWidths and \a
2926
  minRowHeights respectively.
2927
2928
  The minimum height of a row is the largest minimum height of any element in
2929
  that row. The minimum width of a column is the largest minimum width of any
2930
  element in that column.
2931
2932
  This is a helper function for \ref updateLayout.
2933
2934
  \see getMaximumRowColSizes
2935
*/
2936
void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths,
2937
                                          QVector<int> *minRowHeights) const {
2938
  *minColWidths = QVector<int>(columnCount(), 0);
2939
  *minRowHeights = QVector<int>(rowCount(), 0);
2940
  for (int row = 0; row < rowCount(); ++row) {
2941
    for (int col = 0; col < columnCount(); ++col) {
2942
      if (mElements.at(row).at(col)) {
2943
        QSize minHint = mElements.at(row).at(col)->minimumSizeHint();
2944
        QSize min = mElements.at(row).at(col)->minimumSize();
2945
        QSize final(min.width() > 0 ? min.width() : minHint.width(),
2946
                    min.height() > 0 ? min.height() : minHint.height());
2947
        if (minColWidths->at(col) < final.width())
2948
          (*minColWidths)[col] = final.width();
2949
        if (minRowHeights->at(row) < final.height())
2950
          (*minRowHeights)[row] = final.height();
2951
      }
2952
    }
2953
  }
2954
}
2955
2956
/*! \internal
2957
2958
  Places the maximum column widths and row heights into \a maxColWidths and \a
2959
  maxRowHeights respectively.
2960
2961
  The maximum height of a row is the smallest maximum height of any element in
2962
  that row. The maximum width of a column is the smallest maximum width of any
2963
  element in that column.
2964
2965
  This is a helper function for \ref updateLayout.
2966
2967
  \see getMinimumRowColSizes
2968
*/
2969
void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths,
2970
                                          QVector<int> *maxRowHeights) const {
2971
  *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);
2972
  *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);
2973
  for (int row = 0; row < rowCount(); ++row) {
2974
    for (int col = 0; col < columnCount(); ++col) {
2975
      if (mElements.at(row).at(col)) {
2976
        QSize maxHint = mElements.at(row).at(col)->maximumSizeHint();
2977
        QSize max = mElements.at(row).at(col)->maximumSize();
2978
        QSize final(
2979
            max.width() < QWIDGETSIZE_MAX ? max.width() : maxHint.width(),
2980
            max.height() < QWIDGETSIZE_MAX ? max.height() : maxHint.height());
2981
        if (maxColWidths->at(col) > final.width())
2982
          (*maxColWidths)[col] = final.width();
2983
        if (maxRowHeights->at(row) > final.height())
2984
          (*maxRowHeights)[row] = final.height();
2985
      }
2986
    }
2987
  }
2988
}
2989
2990
////////////////////////////////////////////////////////////////////////////////////////////////////
2991
//////////////////// QCPLayoutInset
2992
////////////////////////////////////////////////////////////////////////////////////////////////////
2993
/*! \class QCPLayoutInset
2994
  \brief A layout that places child elements aligned to the border or
2995
  arbitrarily positioned
2996
2997
  Elements are placed either aligned to the border or at arbitrary position in
2998
  the area of the layout. Which placement applies is controlled with the \ref
2999
  InsetPlacement (\ref setInsetPlacement).
3000
3001
  Elements are added via \ref addElement(QCPLayoutElement *element,
3002
  Qt::Alignment alignment) or addElement(QCPLayoutElement *element, const QRectF
3003
  &rect). If the first method is used, the inset placement will default to \ref
3004
  ipBorderAligned and the element will be aligned according to the \a alignment
3005
  parameter. The second method defaults to \ref ipFree and allows placing
3006
  elements at arbitrary position and size, defined by \a rect.
3007
3008
  The alignment or rect can be set via \ref setInsetAlignment or \ref
3009
  setInsetRect, respectively.
3010
3011
  This is the layout that every QCPAxisRect has as \ref
3012
  QCPAxisRect::insetLayout.
3013
*/
3014
3015
/* start documentation of inline functions */
3016
3017
/*! \fn virtual void QCPLayoutInset::simplify()
3018
3019
  The QCPInsetLayout does not need simplification since it can never have empty
3020
  cells due to its linear index structure. This method does nothing.
3021
*/
3022
3023
/* end documentation of inline functions */
3024
3025
/*!
3026
  Creates an instance of QCPLayoutInset and sets default values.
3027
*/
3028
QCPLayoutInset::QCPLayoutInset() {}
3029
3030
QCPLayoutInset::~QCPLayoutInset() {
3031
  // clear all child layout elements. This is important because only the
3032
  // specific layouts know how to handle removing elements (clear calls virtual
3033
  // removeAt method to do that).
3034
  clear();
3035
}
3036
3037
/*!
3038
  Returns the placement type of the element with the specified \a index.
3039
*/
3040
QCPLayoutInset::InsetPlacement QCPLayoutInset::insetPlacement(int index) const {
3041
  if (elementAt(index))
3042
    return mInsetPlacement.at(index);
3043
  else {
3044
    qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3045
    return ipFree;
3046
  }
3047
}
3048
3049
/*!
3050
  Returns the alignment of the element with the specified \a index. The
3051
  alignment only has a meaning, if the inset placement (\ref setInsetPlacement)
3052
  is \ref ipBorderAligned.
3053
*/
3054
Qt::Alignment QCPLayoutInset::insetAlignment(int index) const {
3055
  if (elementAt(index))
3056
    return mInsetAlignment.at(index);
3057
  else {
3058
    qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3059
    return 0;
3060
  }
3061
}
3062
3063
/*!
3064
  Returns the rect of the element with the specified \a index. The rect only has
3065
  a meaning, if the inset placement (\ref setInsetPlacement) is \ref ipFree.
3066
*/
3067
QRectF QCPLayoutInset::insetRect(int index) const {
3068
  if (elementAt(index))
3069
    return mInsetRect.at(index);
3070
  else {
3071
    qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3072
    return QRectF();
3073
  }
3074
}
3075
3076
/*!
3077
  Sets the inset placement type of the element with the specified \a index to \a
3078
  placement.
3079
3080
  \see InsetPlacement
3081
*/
3082
void QCPLayoutInset::setInsetPlacement(
3083
    int index, QCPLayoutInset::InsetPlacement placement) {
3084
  if (elementAt(index))
3085
    mInsetPlacement[index] = placement;
3086
  else
3087
    qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3088
}
3089
3090
/*!
3091
  If the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned, this
3092
  function is used to set the alignment of the element with the specified \a
3093
  index to \a alignment.
3094
3095
  \a alignment is an or combination of the following alignment flags:
3096
  Qt::AlignLeft, Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop,
3097
  Qt::AlignVCenter, Qt::AlignBottom. Any other alignment flags will be ignored.
3098
*/
3099
void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment) {
3100
  if (elementAt(index))
3101
    mInsetAlignment[index] = alignment;
3102
  else
3103
    qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3104
}
3105
3106
/*!
3107
  If the inset placement (\ref setInsetPlacement) is \ref ipFree, this function
3108
  is used to set the position and size of the element with the specified \a
3109
  index to \a rect.
3110
3111
  \a rect is given in fractions of the whole inset layout rect. So an inset with
3112
  rect (0, 0, 1, 1) will span the entire layout. An inset with rect (0.6, 0.1,
3113
  0.35, 0.35) will be in the top right corner of the layout, with 35% width and
3114
  height of the parent layout.
3115
3116
  Note that the minimum and maximum sizes of the embedded element (\ref
3117
  QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize) are
3118
  enforced.
3119
*/
3120
void QCPLayoutInset::setInsetRect(int index, const QRectF &rect) {
3121
  if (elementAt(index))
3122
    mInsetRect[index] = rect;
3123
  else
3124
    qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3125
}
3126
3127
/* inherits documentation from base class */
3128
void QCPLayoutInset::updateLayout() {
3129
  for (int i = 0; i < mElements.size(); ++i) {
3130
    QRect insetRect;
3131
    QSize finalMinSize, finalMaxSize;
3132
    QSize minSizeHint = mElements.at(i)->minimumSizeHint();
3133
    QSize maxSizeHint = mElements.at(i)->maximumSizeHint();
3134
    finalMinSize.setWidth(mElements.at(i)->minimumSize().width() > 0
3135
                              ? mElements.at(i)->minimumSize().width()
3136
                              : minSizeHint.width());
3137
    finalMinSize.setHeight(mElements.at(i)->minimumSize().height() > 0
3138
                               ? mElements.at(i)->minimumSize().height()
3139
                               : minSizeHint.height());
3140
    finalMaxSize.setWidth(mElements.at(i)->maximumSize().width() <
3141
                                  QWIDGETSIZE_MAX
3142
                              ? mElements.at(i)->maximumSize().width()
3143
                              : maxSizeHint.width());
3144
    finalMaxSize.setHeight(mElements.at(i)->maximumSize().height() <
3145
                                   QWIDGETSIZE_MAX
3146
                               ? mElements.at(i)->maximumSize().height()
3147
                               : maxSizeHint.height());
3148
    if (mInsetPlacement.at(i) == ipFree) {
3149
      insetRect = QRect(rect().x() + rect().width() * mInsetRect.at(i).x(),
3150
                        rect().y() + rect().height() * mInsetRect.at(i).y(),
3151
                        rect().width() * mInsetRect.at(i).width(),
3152
                        rect().height() * mInsetRect.at(i).height());
3153
      if (insetRect.size().width() < finalMinSize.width())
3154
        insetRect.setWidth(finalMinSize.width());
3155
      if (insetRect.size().height() < finalMinSize.height())
3156
        insetRect.setHeight(finalMinSize.height());
3157
      if (insetRect.size().width() > finalMaxSize.width())
3158
        insetRect.setWidth(finalMaxSize.width());
3159
      if (insetRect.size().height() > finalMaxSize.height())
3160
        insetRect.setHeight(finalMaxSize.height());
3161
    } else if (mInsetPlacement.at(i) == ipBorderAligned) {
3162
      insetRect.setSize(finalMinSize);
3163
      Qt::Alignment al = mInsetAlignment.at(i);
3164
      if (al.testFlag(Qt::AlignLeft))
3165
        insetRect.moveLeft(rect().x());
3166
      else if (al.testFlag(Qt::AlignRight))
3167
        insetRect.moveRight(rect().x() + rect().width());
3168
      else
3169
        insetRect.moveLeft(rect().x() + rect().width() * 0.5 -
3170
                           finalMinSize.width() *
3171
                               0.5);  // default to Qt::AlignHCenter
3172
      if (al.testFlag(Qt::AlignTop))
3173
        insetRect.moveTop(rect().y());
3174
      else if (al.testFlag(Qt::AlignBottom))
3175
        insetRect.moveBottom(rect().y() + rect().height());
3176
      else
3177
        insetRect.moveTop(rect().y() + rect().height() * 0.5 -
3178
                          finalMinSize.height() *
3179
                              0.5);  // default to Qt::AlignVCenter
3180
    }
3181
    mElements.at(i)->setOuterRect(insetRect);
3182
  }
3183
}
3184
3185
/* inherits documentation from base class */
3186
int QCPLayoutInset::elementCount() const { return mElements.size(); }
3187
3188
/* inherits documentation from base class */
3189
QCPLayoutElement *QCPLayoutInset::elementAt(int index) const {
3190
  if (index >= 0 && index < mElements.size())
3191
    return mElements.at(index);
3192
  else
3193
    return 0;
3194
}
3195
3196
/* inherits documentation from base class */
3197
QCPLayoutElement *QCPLayoutInset::takeAt(int index) {
3198
  if (QCPLayoutElement *el = elementAt(index)) {
3199
    releaseElement(el);
3200
    mElements.removeAt(index);
3201
    mInsetPlacement.removeAt(index);
3202
    mInsetAlignment.removeAt(index);
3203
    mInsetRect.removeAt(index);
3204
    return el;
3205
  } else {
3206
    qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
3207
    return 0;
3208
  }
3209
}
3210
3211
/* inherits documentation from base class */
3212
bool QCPLayoutInset::take(QCPLayoutElement *element) {
3213
  if (element) {
3214
    for (int i = 0; i < elementCount(); ++i) {
3215
      if (elementAt(i) == element) {
3216
        takeAt(i);
3217
        return true;
3218
      }
3219
    }
3220
    qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
3221
  } else
3222
    qDebug() << Q_FUNC_INFO << "Can't take null element";
3223
  return false;
3224
}
3225
3226
/*!
3227
  The inset layout is sensitive to events only at areas where its (visible)
3228
  child elements are sensitive. If the selectTest method of any of the child
3229
  elements returns a positive number for \a pos, this method returns a value
3230
  corresponding to 0.99 times the parent plot's selection tolerance. The inset
3231
  layout is not selectable itself by default. So if \a onlySelectable is true,
3232
  -1.0 is returned.
3233
3234
  See \ref QCPLayerable::selectTest for a general explanation of this virtual
3235
  method.
3236
*/
3237
double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable,
3238
                                  QVariant *details) const {
3239
  Q_UNUSED(details)
3240
  if (onlySelectable) return -1;
3241
3242
  for (int i = 0; i < mElements.size(); ++i) {
3243
    // inset layout shall only return positive selectTest, if actually an inset
3244
    // object is at pos else it would block the entire underlying QCPAxisRect
3245
    // with its surface.
3246
    if (mElements.at(i)->realVisibility() &&
3247
        mElements.at(i)->selectTest(pos, onlySelectable) >= 0)
3248
      return mParentPlot->selectionTolerance() * 0.99;
3249
  }
3250
  return -1;
3251
}
3252
3253
/*!
3254
  Adds the specified \a element to the layout as an inset aligned at the border
3255
  (\ref setInsetAlignment is initialized with \ref ipBorderAligned). The
3256
  alignment is set to \a alignment.
3257
3258
  \a alignment is an or combination of the following alignment flags:
3259
  Qt::AlignLeft, Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop,
3260
  Qt::AlignVCenter, Qt::AlignBottom. Any other alignment flags will be ignored.
3261
3262
  \see addElement(QCPLayoutElement *element, const QRectF &rect)
3263
*/
3264
void QCPLayoutInset::addElement(QCPLayoutElement *element,
3265
                                Qt::Alignment alignment) {
3266
  if (element) {
3267
    if (element->layout())  // remove from old layout first
3268
      element->layout()->take(element);
3269
    mElements.append(element);
3270
    mInsetPlacement.append(ipBorderAligned);
3271
    mInsetAlignment.append(alignment);
3272
    mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
3273
    adoptElement(element);
3274
  } else
3275
    qDebug() << Q_FUNC_INFO << "Can't add null element";
3276
}
3277
3278
/*!
3279
  Adds the specified \a element to the layout as an inset with free
3280
  positioning/sizing (\ref setInsetAlignment is initialized with \ref ipFree).
3281
  The position and size is set to \a rect.
3282
3283
  \a rect is given in fractions of the whole inset layout rect. So an inset with
3284
  rect (0, 0, 1, 1) will span the entire layout. An inset with rect (0.6, 0.1,
3285
  0.35, 0.35) will be in the top right corner of the layout, with 35% width and
3286
  height of the parent layout.
3287
3288
  \see addElement(QCPLayoutElement *element, Qt::Alignment alignment)
3289
*/
3290
void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect) {
3291
  if (element) {
3292
    if (element->layout())  // remove from old layout first
3293
      element->layout()->take(element);
3294
    mElements.append(element);
3295
    mInsetPlacement.append(ipFree);
3296
    mInsetAlignment.append(Qt::AlignRight | Qt::AlignTop);
3297
    mInsetRect.append(rect);
3298
    adoptElement(element);
3299
  } else
3300
    qDebug() << Q_FUNC_INFO << "Can't add null element";
3301
}
3302
3303
////////////////////////////////////////////////////////////////////////////////////////////////////
3304
//////////////////// QCPLineEnding
3305
////////////////////////////////////////////////////////////////////////////////////////////////////
3306
3307
/*! \class QCPLineEnding
3308
  \brief Handles the different ending decorations for line-like items
3309
3310
  \image html QCPLineEnding.png "The various ending styles currently supported"
3311
3312
  For every ending a line-like item has, an instance of this class exists. For
3313
  example, QCPItemLine has two endings which can be set with
3314
  QCPItemLine::setHead and QCPItemLine::setTail.
3315
3316
  The styles themselves are defined via the enum QCPLineEnding::EndingStyle.
3317
  Most decorations can be modified regarding width and length, see \ref setWidth
3318
  and \ref setLength. The direction of the ending decoration (e.g. direction an
3319
  arrow is pointing) is controlled by the line-like item. For example, when both
3320
  endings of a QCPItemLine are set to be arrows, they will point to opposite
3321
  directions, e.g. "outward". This can be changed by \ref setInverted, which
3322
  would make the respective arrow point inward.
3323
3324
  Note that due to the overloaded QCPLineEnding constructor, you may directly
3325
  specify a QCPLineEnding::EndingStyle where actually a QCPLineEnding is
3326
  expected, e.g. \snippet documentation/doc-code-snippets/mainwindow.cpp
3327
  qcplineending-sethead
3328
*/
3329
3330
/*!
3331
  Creates a QCPLineEnding instance with default values (style \ref esNone).
3332
*/
3333
QCPLineEnding::QCPLineEnding()
3334
    : mStyle(esNone), mWidth(8), mLength(10), mInverted(false) {}
3335
3336
/*!
3337
  Creates a QCPLineEnding instance with the specified values.
3338
*/
3339
QCPLineEnding::QCPLineEnding(QCPLineEnding::EndingStyle style, double width,
3340
                             double length, bool inverted)
3341
    : mStyle(style), mWidth(width), mLength(length), mInverted(inverted) {}
3342
3343
/*!
3344
  Sets the style of the ending decoration.
3345
*/
3346
void QCPLineEnding::setStyle(QCPLineEnding::EndingStyle style) {
3347
  mStyle = style;
3348
}
3349
3350
/*!
3351
  Sets the width of the ending decoration, if the style supports it. On arrows,
3352
  for example, the width defines the size perpendicular to the arrow's pointing
3353
  direction.
3354
3355
  \see setLength
3356
*/
3357
void QCPLineEnding::setWidth(double width) { mWidth = width; }
3358
3359
/*!
3360
  Sets the length of the ending decoration, if the style supports it. On arrows,
3361
  for example, the length defines the size in pointing direction.
3362
3363
  \see setWidth
3364
*/
3365
void QCPLineEnding::setLength(double length) { mLength = length; }
3366
3367
/*!
3368
  Sets whether the ending decoration shall be inverted. For example, an arrow
3369
  decoration will point inward when \a inverted is set to true.
3370
3371
  Note that also the \a width direction is inverted. For symmetrical ending
3372
  styles like arrows or discs, this doesn't make a difference. However,
3373
  asymmetric styles like \ref esHalfBar are affected by it, which can be used to
3374
  control to which side the half bar points to.
3375
*/
3376
void QCPLineEnding::setInverted(bool inverted) { mInverted = inverted; }
3377
3378
/*! \internal
3379
3380
  Returns the maximum pixel radius the ending decoration might cover, starting
3381
  from the position the decoration is drawn at (typically a line ending/\ref
3382
  QCPItemPosition of an item).
3383
3384
  This is relevant for clipping. Only omit painting of the decoration when the
3385
  position where the decoration is supposed to be drawn is farther away from the
3386
  clipping rect than the returned distance.
3387
*/
3388
double QCPLineEnding::boundingDistance() const {
3389
  switch (mStyle) {
3390
    case esNone:
3391
      return 0;
3392
3393
    case esFlatArrow:
3394
    case esSpikeArrow:
3395
    case esLineArrow:
3396
    case esSkewedBar:
3397
      return qSqrt(mWidth * mWidth +
3398
                   mLength * mLength);  // items that have width and length
3399
3400
    case esDisc:
3401
    case esSquare:
3402
    case esDiamond:
3403
    case esBar:
3404
    case esHalfBar:
3405
      return mWidth * 1.42;  // items that only have a width -> width*sqrt(2)
3406
  }
3407
  return 0;
3408
}
3409
3410
/*!
3411
  Starting from the origin of this line ending (which is style specific),
3412
  returns the length covered by the line ending symbol, in backward direction.
3413
3414
  For example, the \ref esSpikeArrow has a shorter real length than a \ref
3415
  esFlatArrow, even if both have the same \ref setLength value, because the
3416
  spike arrow has an inward curved back, which reduces the length along its
3417
  center axis (the drawing origin for arrows is at the tip).
3418
3419
  This function is used for precise, style specific placement of line endings,
3420
  for example in QCPAxes.
3421
*/
3422
double QCPLineEnding::realLength() const {
3423
  switch (mStyle) {
3424
    case esNone:
3425
    case esLineArrow:
3426
    case esSkewedBar:
3427
    case esBar:
3428
    case esHalfBar:
3429
      return 0;
3430
3431
    case esFlatArrow:
3432
      return mLength;
3433
3434
    case esDisc:
3435
    case esSquare:
3436
    case esDiamond:
3437
      return mWidth * 0.5;
3438
3439
    case esSpikeArrow:
3440
      return mLength * 0.8;
3441
  }
3442
  return 0;
3443
}
3444
3445
/*! \internal
3446
3447
  Draws the line ending with the specified \a painter at the position \a pos.
3448
  The direction of the line ending is controlled with \a dir.
3449
*/
3450
void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos,
3451
                         const QVector2D &dir) const {
3452
  if (mStyle == esNone) return;
3453
3454
  QVector2D lengthVec(dir.normalized());
3455
  if (lengthVec.isNull()) lengthVec = QVector2D(1, 0);
3456
  QVector2D widthVec(-lengthVec.y(), lengthVec.x());
3457
  lengthVec *= (float)(mLength * (mInverted ? -1 : 1));
3458
  widthVec *= (float)(mWidth * 0.5 * (mInverted ? -1 : 1));
3459
3460
  QPen penBackup = painter->pen();
3461
  QBrush brushBackup = painter->brush();
3462
  QPen miterPen = penBackup;
3463
  miterPen.setJoinStyle(Qt::MiterJoin);  // to make arrow heads spikey
3464
  QBrush brush(painter->pen().color(), Qt::SolidPattern);
3465
  switch (mStyle) {
3466
    case esNone:
3467
      break;
3468
    case esFlatArrow: {
3469
      QPointF points[3] = {pos.toPointF(),
3470
                           (pos - lengthVec + widthVec).toPointF(),
3471
                           (pos - lengthVec - widthVec).toPointF()};
3472
      painter->setPen(miterPen);
3473
      painter->setBrush(brush);
3474
      painter->drawConvexPolygon(points, 3);
3475
      painter->setBrush(brushBackup);
3476
      painter->setPen(penBackup);
3477
      break;
3478
    }
3479
    case esSpikeArrow: {
3480
      QPointF points[4] = {pos.toPointF(),
3481
                           (pos - lengthVec + widthVec).toPointF(),
3482
                           (pos - lengthVec * 0.8f).toPointF(),
3483
                           (pos - lengthVec - widthVec).toPointF()};
3484
      painter->setPen(miterPen);
3485
      painter->setBrush(brush);
3486
      painter->drawConvexPolygon(points, 4);
3487
      painter->setBrush(brushBackup);
3488
      painter->setPen(penBackup);
3489
      break;
3490
    }
3491
    case esLineArrow: {
3492
      QPointF points[3] = {(pos - lengthVec + widthVec).toPointF(),
3493
                           pos.toPointF(),
3494
                           (pos - lengthVec - widthVec).toPointF()};
3495
      painter->setPen(miterPen);
3496
      painter->drawPolyline(points, 3);
3497
      painter->setPen(penBackup);
3498
      break;
3499
    }
3500
    case esDisc: {
3501
      painter->setBrush(brush);
3502
      painter->drawEllipse(pos.toPointF(), mWidth * 0.5, mWidth * 0.5);
3503
      painter->setBrush(brushBackup);
3504
      break;
3505
    }
3506
    case esSquare: {
3507
      QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3508
      QPointF points[4] = {(pos - widthVecPerp + widthVec).toPointF(),
3509
                           (pos - widthVecPerp - widthVec).toPointF(),
3510
                           (pos + widthVecPerp - widthVec).toPointF(),
3511
                           (pos + widthVecPerp + widthVec).toPointF()};
3512
      painter->setPen(miterPen);
3513
      painter->setBrush(brush);
3514
      painter->drawConvexPolygon(points, 4);
3515
      painter->setBrush(brushBackup);
3516
      painter->setPen(penBackup);
3517
      break;
3518
    }
3519
    case esDiamond: {
3520
      QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3521
      QPointF points[4] = {
3522
          (pos - widthVecPerp).toPointF(), (pos - widthVec).toPointF(),
3523
          (pos + widthVecPerp).toPointF(), (pos + widthVec).toPointF()};
3524
      painter->setPen(miterPen);
3525
      painter->setBrush(brush);
3526
      painter->drawConvexPolygon(points, 4);
3527
      painter->setBrush(brushBackup);
3528
      painter->setPen(penBackup);
3529
      break;
3530
    }
3531
    case esBar: {
3532
      painter->drawLine((pos + widthVec).toPointF(),
3533
                        (pos - widthVec).toPointF());
3534
      break;
3535
    }
3536
    case esHalfBar: {
3537
      painter->drawLine((pos + widthVec).toPointF(), pos.toPointF());
3538
      break;
3539
    }
3540
    case esSkewedBar: {
3541
      if (qFuzzyIsNull(painter->pen().widthF()) &&
3542
          !painter->modes().testFlag(QCPPainter::pmNonCosmetic)) {
3543
        // if drawing with cosmetic pen (perfectly thin stroke, happens only in
3544
        // vector exports), draw bar exactly on tip of line
3545
        painter->drawLine(
3546
            (pos + widthVec + lengthVec * 0.2f * (mInverted ? -1 : 1))
3547
                .toPointF(),
3548
            (pos - widthVec - lengthVec * 0.2f * (mInverted ? -1 : 1))
3549
                .toPointF());
3550
      } else {
3551
        // if drawing with thick (non-cosmetic) pen, shift bar a little in line
3552
        // direction to prevent line from sticking through bar slightly
3553
        painter->drawLine(
3554
            (pos + widthVec + lengthVec * 0.2f * (mInverted ? -1 : 1) +
3555
             dir.normalized() * qMax(1.0f, (float)painter->pen().widthF()) *
3556
                 0.5f)
3557
                .toPointF(),
3558
            (pos - widthVec - lengthVec * 0.2f * (mInverted ? -1 : 1) +
3559
             dir.normalized() * qMax(1.0f, (float)painter->pen().widthF()) *
3560
                 0.5f)
3561
                .toPointF());
3562
      }
3563
      break;
3564
    }
3565
  }
3566
}
3567
3568
/*! \internal
3569
  \overload
3570
3571
  Draws the line ending. The direction is controlled with the \a angle parameter
3572
  in radians.
3573
*/
3574
void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos,
3575
                         double angle) const {
3576
  draw(painter, pos, QVector2D(qCos(angle), qSin(angle)));
3577
}
3578
3579
////////////////////////////////////////////////////////////////////////////////////////////////////
3580
//////////////////// QCPGrid
3581
////////////////////////////////////////////////////////////////////////////////////////////////////
3582
3583
/*! \class QCPGrid
3584
  \brief Responsible for drawing the grid of a QCPAxis.
3585
3586
  This class is tightly bound to QCPAxis. Every axis owns a grid instance and
3587
  uses it to draw the grid lines, sub grid lines and zero-line. You can interact
3588
  with the grid of an axis via \ref QCPAxis::grid. Normally, you don't need to
3589
  create an instance of QCPGrid yourself.
3590
3591
  The axis and grid drawing was split into two classes to allow them to be
3592
  placed on different layers (both QCPAxis and QCPGrid inherit from
3593
  QCPLayerable). Thus it is possible to have the grid in the background and the
3594
  axes in the foreground, and any plottables/items in between. This described
3595
  situation is the default setup, see the QCPLayer documentation.
3596
*/
3597
3598
/*!
3599
  Creates a QCPGrid instance and sets default values.
3600
3601
  You shouldn't instantiate grids on their own, since every QCPAxis brings its
3602
  own QCPGrid.
3603
*/
3604
QCPGrid::QCPGrid(QCPAxis *parentAxis)
3605
    : QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
3606
      mParentAxis(parentAxis) {
3607
  // warning: this is called in QCPAxis constructor, so parentAxis members
3608
  // should not be accessed/called
3609
  setParent(parentAxis);
3610
  setPen(QPen(QColor(200, 200, 200), 0, Qt::DotLine));
3611
  setSubGridPen(QPen(QColor(220, 220, 220), 0, Qt::DotLine));
3612
  setZeroLinePen(QPen(QColor(200, 200, 200), 0, Qt::SolidLine));
3613
  setSubGridVisible(false);
3614
  setAntialiased(false);
3615
  setAntialiasedSubGrid(false);
3616
  setAntialiasedZeroLine(false);
3617
}
3618
3619
/*!
3620
  Sets whether grid lines at sub tick marks are drawn.
3621
3622
  \see setSubGridPen
3623
*/
3624
void QCPGrid::setSubGridVisible(bool visible) { mSubGridVisible = visible; }
3625
3626
/*!
3627
  Sets whether sub grid lines are drawn antialiased.
3628
*/
3629
void QCPGrid::setAntialiasedSubGrid(bool enabled) {
3630
  mAntialiasedSubGrid = enabled;
3631
}
3632
3633
/*!
3634
  Sets whether zero lines are drawn antialiased.
3635
*/
3636
void QCPGrid::setAntialiasedZeroLine(bool enabled) {
3637
  mAntialiasedZeroLine = enabled;
3638
}
3639
3640
/*!
3641
  Sets the pen with which (major) grid lines are drawn.
3642
*/
3643
void QCPGrid::setPen(const QPen &pen) { mPen = pen; }
3644
3645
/*!
3646
  Sets the pen with which sub grid lines are drawn.
3647
*/
3648
void QCPGrid::setSubGridPen(const QPen &pen) { mSubGridPen = pen; }
3649
3650
/*!
3651
  Sets the pen with which zero lines are drawn.
3652
3653
  Zero lines are lines at value coordinate 0 which may be drawn with a different
3654
  pen than other grid lines. To disable zero lines and just draw normal grid
3655
  lines at zero, set \a pen to Qt::NoPen.
3656
*/
3657
void QCPGrid::setZeroLinePen(const QPen &pen) { mZeroLinePen = pen; }
3658
3659
/*! \internal
3660
3661
  A convenience function to easily set the QPainter::Antialiased hint on the
3662
  provided \a painter before drawing the major grid lines.
3663
3664
  This is the antialiasing state the painter passed to the \ref draw method is
3665
  in by default.
3666
3667
  This function takes into account the local setting of the antialiasing flag as
3668
  well as the overrides set with \ref QCustomPlot::setAntialiasedElements and
3669
  \ref QCustomPlot::setNotAntialiasedElements.
3670
3671
  \see setAntialiased
3672
*/
3673
void QCPGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const {
3674
  applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid);
3675
}
3676
3677
/*! \internal
3678
3679
  Draws grid lines and sub grid lines at the positions of (sub) ticks of the
3680
  parent axis, spanning over the complete axis rect. Also draws the zero line,
3681
  if appropriate (\ref setZeroLinePen).
3682
*/
3683
void QCPGrid::draw(QCPPainter *painter) {
3684
  if (!mParentAxis) {
3685
    qDebug() << Q_FUNC_INFO << "invalid parent axis";
3686
    return;
3687
  }
3688
3689
  if (mSubGridVisible) drawSubGridLines(painter);
3690
  drawGridLines(painter);
3691
}
3692
3693
/*! \internal
3694
3695
  Draws the main grid lines and possibly a zero line with the specified painter.
3696
3697
  This is a helper function called by \ref draw.
3698
*/
3699
void QCPGrid::drawGridLines(QCPPainter *painter) const {
3700
  if (!mParentAxis) {
3701
    qDebug() << Q_FUNC_INFO << "invalid parent axis";
3702
    return;
3703
  }
3704
3705
  int lowTick = mParentAxis->mLowestVisibleTick;
3706
  int highTick = mParentAxis->mHighestVisibleTick;
3707
  double t;  // helper variable, result of coordinate-to-pixel transforms
3708
  if (mParentAxis->orientation() == Qt::Horizontal) {
3709
    // draw zeroline:
3710
    int zeroLineIndex = -1;
3711
    if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 &&
3712
        mParentAxis->mRange.upper > 0) {
3713
      applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
3714
      painter->setPen(mZeroLinePen);
3715
      double epsilon =
3716
          mParentAxis->range().size() * 1E-6;  // for comparing double to zero
3717
      for (int i = lowTick; i <= highTick; ++i) {
3718
        if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon) {
3719
          zeroLineIndex = i;
3720
          t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i));  // x
3721
          painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t,
3722
                                   mParentAxis->mAxisRect->top()));
3723
          break;
3724
        }
3725
      }
3726
    }
3727
    // draw grid lines:
3728
    applyDefaultAntialiasingHint(painter);
3729
    painter->setPen(mPen);
3730
    for (int i = lowTick; i <= highTick; ++i) {
3731
      if (i == zeroLineIndex)
3732
        continue;  // don't draw a gridline on top of the zeroline
3733
      t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i));  // x
3734
      painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t,
3735
                               mParentAxis->mAxisRect->top()));
3736
    }
3737
  } else {
3738
    // draw zeroline:
3739
    int zeroLineIndex = -1;
3740
    if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 &&
3741
        mParentAxis->mRange.upper > 0) {
3742
      applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
3743