Kstars

qcustomplot.cpp
1/*
2 QCustomPlot, an easy to use, modern plotting widget for Qt
3 SPDX-FileCopyrightText: 2011-2021 Emanuel Eichhammer <http://www.qcustomplot.com/>
4
5 SPDX-License-Identifier: GPL-3.0-or-later
6*/
7
8#include "qcustomplot.h"
9
10
11/* including file 'src/vector2d.cpp' */
12/* modified 2021-03-29T02:30:44, size 7973 */
13
14////////////////////////////////////////////////////////////////////////////////////////////////////
15//////////////////// QCPVector2D
16////////////////////////////////////////////////////////////////////////////////////////////////////
17
18/*! \class QCPVector2D
19 \brief Represents two doubles as a mathematical 2D vector
20
21 This class acts as a replacement for QVector2D with the advantage of double precision instead of
22 single, and some convenience methods tailored for the QCustomPlot library.
23*/
24
25/* start documentation of inline functions */
26
27/*! \fn void QCPVector2D::setX(double x)
28
29 Sets the x coordinate of this vector to \a x.
30
31 \see setY
32*/
33
34/*! \fn void QCPVector2D::setY(double y)
35
36 Sets the y coordinate of this vector to \a y.
37
38 \see setX
39*/
40
41/*! \fn double QCPVector2D::length() const
42
43 Returns the length of this vector.
44
45 \see lengthSquared
46*/
47
48/*! \fn double QCPVector2D::lengthSquared() const
49
50 Returns the squared length of this vector. In some situations, e.g. when just trying to find the
51 shortest vector of a group, this is faster than calculating \ref length, because it avoids
52 calculation of a square root.
53
54 \see length
55*/
56
57/*! \fn double QCPVector2D::angle() const
58
59 Returns the angle of the vector in radians. The angle is measured between the positive x line and
60 the vector, counter-clockwise in a mathematical coordinate system (y axis upwards positive). In
61 screen/widget coordinates where the y axis is inverted, the angle appears clockwise.
62*/
63
64/*! \fn QPoint QCPVector2D::toPoint() const
65
66 Returns a QPoint which has the x and y coordinates of this vector, truncating any floating point
67 information.
68
69 \see toPointF
70*/
71
72/*! \fn QPointF QCPVector2D::toPointF() const
73
74 Returns a QPointF which has the x and y coordinates of this vector.
75
76 \see toPoint
77*/
78
79/*! \fn bool QCPVector2D::isNull() const
80
81 Returns whether this vector is null. A vector is null if \c qIsNull returns true for both x and y
82 coordinates, i.e. if both are binary equal to 0.
83*/
84
85/*! \fn QCPVector2D QCPVector2D::perpendicular() const
86
87 Returns a vector perpendicular to this vector, with the same length.
88*/
89
90/*! \fn double QCPVector2D::dot() const
91
92 Returns the dot/scalar product of this vector with the specified vector \a vec.
93*/
94
95/* end documentation of inline functions */
96
97/*!
98 Creates a QCPVector2D object and initializes the x and y coordinates to 0.
99*/
101 mX(0),
102 mY(0)
103{
104}
105
106/*!
107 Creates a QCPVector2D object and initializes the \a x and \a y coordinates with the specified
108 values.
109*/
110QCPVector2D::QCPVector2D(double x, double y) :
111 mX(x),
112 mY(y)
113{
114}
115
116/*!
117 Creates a QCPVector2D object and initializes the x and y coordinates respective coordinates of
118 the specified \a point.
119*/
121 mX(point.x()),
122 mY(point.y())
123{
124}
125
126/*!
127 Creates a QCPVector2D object and initializes the x and y coordinates respective coordinates of
128 the specified \a point.
129*/
131 mX(point.x()),
132 mY(point.y())
133{
134}
135
136/*!
137 Normalizes this vector. After this operation, the length of the vector is equal to 1.
138
139 If the vector has both entries set to zero, this method does nothing.
140
141 \see normalized, length, lengthSquared
142*/
144{
145 if (mX == 0.0 && mY == 0.0) return;
146 const double lenInv = 1.0/length();
147 mX *= lenInv;
148 mY *= lenInv;
149}
150
151/*!
152 Returns a normalized version of this vector. The length of the returned vector is equal to 1.
153
154 If the vector has both entries set to zero, this method returns the vector unmodified.
155
156 \see normalize, length, lengthSquared
157*/
159{
160 if (mX == 0.0 && mY == 0.0) return *this;
161 const double lenInv = 1.0/length();
162 return QCPVector2D(mX*lenInv, mY*lenInv);
163}
164
165/*! \overload
166
167 Returns the squared shortest distance of this vector (interpreted as a point) to the finite line
168 segment given by \a start and \a end.
169
170 \see distanceToStraightLine
171*/
172double QCPVector2D::distanceSquaredToLine(const QCPVector2D &start, const QCPVector2D &end) const
173{
174 const QCPVector2D v(end-start);
175 const double vLengthSqr = v.lengthSquared();
176 if (!qFuzzyIsNull(vLengthSqr))
177 {
178 const double mu = v.dot(*this-start)/vLengthSqr;
179 if (mu < 0)
180 return (*this-start).lengthSquared();
181 else if (mu > 1)
182 return (*this-end).lengthSquared();
183 else
184 return ((start + mu*v)-*this).lengthSquared();
185 } else
186 return (*this-start).lengthSquared();
187}
188
189/*! \overload
190
191 Returns the squared shortest distance of this vector (interpreted as a point) to the finite line
192 segment given by \a line.
193
194 \see distanceToStraightLine
195*/
197{
198 return distanceSquaredToLine(QCPVector2D(line.p1()), QCPVector2D(line.p2()));
199}
200
201/*!
202 Returns the shortest distance of this vector (interpreted as a point) to the infinite straight
203 line given by a \a base point and a \a direction vector.
204
205 \see distanceSquaredToLine
206*/
207double QCPVector2D::distanceToStraightLine(const QCPVector2D &base, const QCPVector2D &direction) const
208{
209 return qAbs((*this-base).dot(direction.perpendicular()))/direction.length();
210}
211
212/*!
213 Scales this vector by the given \a factor, i.e. the x and y components are multiplied by \a
214 factor.
215*/
217{
218 mX *= factor;
219 mY *= factor;
220 return *this;
221}
222
223/*!
224 Scales this vector by the given \a divisor, i.e. the x and y components are divided by \a
225 divisor.
226*/
228{
229 mX /= divisor;
230 mY /= divisor;
231 return *this;
232}
233
234/*!
235 Adds the given \a vector to this vector component-wise.
236*/
238{
239 mX += vector.mX;
240 mY += vector.mY;
241 return *this;
242}
243
244/*!
245 subtracts the given \a vector from this vector component-wise.
246*/
248{
249 mX -= vector.mX;
250 mY -= vector.mY;
251 return *this;
252}
253/* end of 'src/vector2d.cpp' */
254
255
256/* including file 'src/painter.cpp' */
257/* modified 2021-03-29T02:30:44, size 8656 */
258
259////////////////////////////////////////////////////////////////////////////////////////////////////
260//////////////////// QCPPainter
261////////////////////////////////////////////////////////////////////////////////////////////////////
262
263/*! \class QCPPainter
264 \brief QPainter subclass used internally
265
266 This QPainter subclass is used to provide some extended functionality e.g. for tweaking position
267 consistency between antialiased and non-antialiased painting. Further it provides workarounds
268 for QPainter quirks.
269
270 \warning This class intentionally hides non-virtual functions of QPainter, e.g. setPen, save and
271 restore. So while it is possible to pass a QCPPainter instance to a function that expects a
272 QPainter pointer, some of the workarounds and tweaks will be unavailable to the function (because
273 it will call the base class implementations of the functions actually hidden by QCPPainter).
274*/
275
276/*!
277 Creates a new QCPPainter instance and sets default values
278*/
280 mModes(pmDefault),
281 mIsAntialiasing(false)
282{
283 // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter isn't active yet and
284 // a call to begin() will follow
285}
286
287/*!
288 Creates a new QCPPainter instance on the specified paint \a device and sets default values. Just
289 like the analogous QPainter constructor, begins painting on \a device immediately.
290
291 Like \ref begin, this method sets QPainter::NonCosmeticDefaultPen in Qt versions before Qt5.
292*/
294 QPainter(device),
295 mModes(pmDefault),
296 mIsAntialiasing(false)
297{
298#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
299 if (isActive())
300 setRenderHint(QPainter::NonCosmeticDefaultPen);
301#endif
302}
303
304/*!
305 Sets the pen of the painter and applies certain fixes to it, depending on the mode of this
306 QCPPainter.
307
308 \note this function hides the non-virtual base class implementation.
309*/
310void QCPPainter::setPen(const QPen &pen)
311{
313 if (mModes.testFlag(pmNonCosmetic))
315}
316
317/*! \overload
318
319 Sets the pen (by color) of the painter and applies certain fixes to it, depending on the mode of
320 this QCPPainter.
321
322 \note this function hides the non-virtual base class implementation.
323*/
324void QCPPainter::setPen(const QColor &color)
325{
326 QPainter::setPen(color);
327 if (mModes.testFlag(pmNonCosmetic))
329}
330
331/*! \overload
332
333 Sets the pen (by style) of the painter and applies certain fixes to it, depending on the mode of
334 this QCPPainter.
335
336 \note this function hides the non-virtual base class implementation.
337*/
339{
340 QPainter::setPen(penStyle);
341 if (mModes.testFlag(pmNonCosmetic))
343}
344
345/*! \overload
346
347 Works around a Qt bug introduced with Qt 4.8 which makes drawing QLineF unpredictable when
348 antialiasing is disabled. Thus when antialiasing is disabled, it rounds the \a line to
349 integer coordinates and then passes it to the original drawLine.
350
351 \note this function hides the non-virtual base class implementation.
352*/
354{
355 if (mIsAntialiasing || mModes.testFlag(pmVectorized))
356 QPainter::drawLine(line);
357 else
359}
360
361/*!
362 Sets whether painting uses antialiasing or not. Use this method instead of using setRenderHint
363 with QPainter::Antialiasing directly, as it allows QCPPainter to regain pixel exactness between
364 antialiased and non-antialiased painting (Since Qt < 5.0 uses slightly different coordinate systems for
365 AA/Non-AA painting).
366*/
368{
370 if (mIsAntialiasing != enabled)
371 {
372 mIsAntialiasing = enabled;
373 if (!mModes.testFlag(pmVectorized)) // antialiasing half-pixel shift only needed for rasterized outputs
374 {
375 if (mIsAntialiasing)
376 translate(0.5, 0.5);
377 else
378 translate(-0.5, -0.5);
379 }
380 }
381}
382
383/*!
384 Sets the mode of the painter. This controls whether the painter shall adjust its
385 fixes/workarounds optimized for certain output devices.
386*/
388{
389 mModes = modes;
390}
391
392/*!
393 Sets the QPainter::NonCosmeticDefaultPen in Qt versions before Qt5 after beginning painting on \a
394 device. This is necessary to get cosmetic pen consistency across Qt versions, because since Qt5,
395 all pens are non-cosmetic by default, and in Qt4 this render hint must be set to get that
396 behaviour.
397
398 The Constructor \ref QCPPainter(QPaintDevice *device) which directly starts painting also sets
399 the render hint as appropriate.
400
401 \note this function hides the non-virtual base class implementation.
402*/
404{
405 bool result = QPainter::begin(device);
406#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
407 if (result)
408 setRenderHint(QPainter::NonCosmeticDefaultPen);
409#endif
410 return result;
411}
412
413/*! \overload
414
415 Sets the mode of the painter. This controls whether the painter shall adjust its
416 fixes/workarounds optimized for certain output devices.
417*/
419{
420 if (!enabled && mModes.testFlag(mode))
421 mModes &= ~mode;
422 else if (enabled && !mModes.testFlag(mode))
423 mModes |= mode;
424}
425
426/*!
427 Saves the painter (see QPainter::save). Since QCPPainter adds some new internal state to
428 QPainter, the save/restore functions are reimplemented to also save/restore those members.
429
430 \note this function hides the non-virtual base class implementation.
431
432 \see restore
433*/
435{
436 mAntialiasingStack.push(mIsAntialiasing);
438}
439
440/*!
441 Restores the painter (see QPainter::restore). Since QCPPainter adds some new internal state to
442 QPainter, the save/restore functions are reimplemented to also save/restore those members.
443
444 \note this function hides the non-virtual base class implementation.
445
446 \see save
447*/
449{
450 if (!mAntialiasingStack.isEmpty())
451 mIsAntialiasing = mAntialiasingStack.pop();
452 else
453 qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
455}
456
457/*!
458 Changes the pen width to 1 if it currently is 0. This function is called in the \ref setPen
459 overrides when the \ref pmNonCosmetic mode is set.
460*/
462{
463 if (qFuzzyIsNull(pen().widthF()))
464 {
465 QPen p = pen();
466 p.setWidth(1);
468 }
469}
470/* end of 'src/painter.cpp' */
471
472
473/* including file 'src/paintbuffer.cpp' */
474/* modified 2021-03-29T02:30:44, size 18915 */
475
476////////////////////////////////////////////////////////////////////////////////////////////////////
477//////////////////// QCPAbstractPaintBuffer
478////////////////////////////////////////////////////////////////////////////////////////////////////
479
480/*! \class QCPAbstractPaintBuffer
481 \brief The abstract base class for paint buffers, which define the rendering backend
482
483 This abstract base class defines the basic interface that a paint buffer needs to provide in
484 order to be usable by QCustomPlot.
485
486 A paint buffer manages both a surface to draw onto, and the matching paint device. The size of
487 the surface can be changed via \ref setSize. External classes (\ref QCustomPlot and \ref
488 QCPLayer) request a painter via \ref startPainting and then perform the draw calls. Once the
489 painting is complete, \ref donePainting is called, so the paint buffer implementation can do
490 clean up if necessary. Before rendering a frame, each paint buffer is usually filled with a color
491 using \ref clear (usually the color is \c Qt::transparent), to remove the contents of the
492 previous frame.
493
494 The simplest paint buffer implementation is \ref QCPPaintBufferPixmap which allows regular
495 software rendering via the raster engine. Hardware accelerated rendering via pixel buffers and
496 frame buffer objects is provided by \ref QCPPaintBufferGlPbuffer and \ref QCPPaintBufferGlFbo.
497 They are used automatically if \ref QCustomPlot::setOpenGl is enabled.
498*/
499
500/* start documentation of pure virtual functions */
501
502/*! \fn virtual QCPPainter *QCPAbstractPaintBuffer::startPainting() = 0
503
504 Returns a \ref QCPPainter which is ready to draw to this buffer. The ownership and thus the
505 responsibility to delete the painter after the painting operations are complete is given to the
506 caller of this method.
507
508 Once you are done using the painter, delete the painter and call \ref donePainting.
509
510 While a painter generated with this method is active, you must not call \ref setSize, \ref
511 setDevicePixelRatio or \ref clear.
512
513 This method may return 0, if a painter couldn't be activated on the buffer. This usually
514 indicates a problem with the respective painting backend.
515*/
516
517/*! \fn virtual void QCPAbstractPaintBuffer::draw(QCPPainter *painter) const = 0
518
519 Draws the contents of this buffer with the provided \a painter. This is the method that is used
520 to finally join all paint buffers and draw them onto the screen.
521*/
522
523/*! \fn virtual void QCPAbstractPaintBuffer::clear(const QColor &color) = 0
524
525 Fills the entire buffer with the provided \a color. To have an empty transparent buffer, use the
526 named color \c Qt::transparent.
527
528 This method must not be called if there is currently a painter (acquired with \ref startPainting)
529 active.
530*/
531
532/*! \fn virtual void QCPAbstractPaintBuffer::reallocateBuffer() = 0
533
534 Reallocates the internal buffer with the currently configured size (\ref setSize) and device
535 pixel ratio, if applicable (\ref setDevicePixelRatio). It is called as soon as any of those
536 properties are changed on this paint buffer.
537
538 \note Subclasses of \ref QCPAbstractPaintBuffer must call their reimplementation of this method
539 in their constructor, to perform the first allocation (this can not be done by the base class
540 because calling pure virtual methods in base class constructors is not possible).
541*/
542
543/* end documentation of pure virtual functions */
544/* start documentation of inline functions */
545
546/*! \fn virtual void QCPAbstractPaintBuffer::donePainting()
547
548 If you have acquired a \ref QCPPainter to paint onto this paint buffer via \ref startPainting,
549 call this method as soon as you are done with the painting operations and have deleted the
550 painter.
551
552 paint buffer subclasses may use this method to perform any type of cleanup that is necessary. The
553 default implementation does nothing.
554*/
555
556/* end documentation of inline functions */
557
558/*!
559 Creates a paint buffer and initializes it with the provided \a size and \a devicePixelRatio.
560
561 Subclasses must call their \ref reallocateBuffer implementation in their respective constructors.
562*/
563QCPAbstractPaintBuffer::QCPAbstractPaintBuffer(const QSize &size, double devicePixelRatio) :
564 mSize(size),
565 mDevicePixelRatio(devicePixelRatio),
566 mInvalidated(true)
567{
568}
569
570QCPAbstractPaintBuffer::~QCPAbstractPaintBuffer()
571{
572}
573
574/*!
575 Sets the paint buffer size.
576
577 The buffer is reallocated (by calling \ref reallocateBuffer), so any painters that were obtained
578 by \ref startPainting are invalidated and must not be used after calling this method.
579
580 If \a size is already the current buffer size, this method does nothing.
581*/
583{
584 if (mSize != size)
585 {
586 mSize = size;
588 }
589}
590
591/*!
592 Sets the invalidated flag to \a invalidated.
593
594 This mechanism is used internally in conjunction with isolated replotting of \ref QCPLayer
595 instances (in \ref QCPLayer::lmBuffered mode). If \ref QCPLayer::replot is called on a buffered
596 layer, i.e. an isolated repaint of only that layer (and its dedicated paint buffer) is requested,
597 QCustomPlot will decide depending on the invalidated flags of other paint buffers whether it also
598 replots them, instead of only the layer on which the replot was called.
599
600 The invalidated flag is set to true when \ref QCPLayer association has changed, i.e. if layers
601 were added or removed from this buffer, or if they were reordered. It is set to false as soon as
602 all associated \ref QCPLayer instances are drawn onto the buffer.
603
604 Under normal circumstances, it is not necessary to manually call this method.
605*/
607{
608 mInvalidated = invalidated;
609}
610
611/*!
612 Sets the device pixel ratio to \a ratio. This is useful to render on high-DPI output devices.
613 The ratio is automatically set to the device pixel ratio used by the parent QCustomPlot instance.
614
615 The buffer is reallocated (by calling \ref reallocateBuffer), so any painters that were obtained
616 by \ref startPainting are invalidated and must not be used after calling this method.
617
618 \note This method is only available for Qt versions 5.4 and higher.
619*/
621{
622 if (!qFuzzyCompare(ratio, mDevicePixelRatio))
623 {
624#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
625 mDevicePixelRatio = ratio;
627#else
628 qDebug() << Q_FUNC_INFO << "Device pixel ratios not supported for Qt versions before 5.4";
629 mDevicePixelRatio = 1.0;
630#endif
631 }
632}
633
634////////////////////////////////////////////////////////////////////////////////////////////////////
635//////////////////// QCPPaintBufferPixmap
636////////////////////////////////////////////////////////////////////////////////////////////////////
637
638/*! \class QCPPaintBufferPixmap
639 \brief A paint buffer based on QPixmap, using software raster rendering
640
641 This paint buffer is the default and fall-back paint buffer which uses software rendering and
642 QPixmap as internal buffer. It is used if \ref QCustomPlot::setOpenGl is false.
643*/
644
645/*!
646 Creates a pixmap paint buffer instancen with the specified \a size and \a devicePixelRatio, if
647 applicable.
648*/
649QCPPaintBufferPixmap::QCPPaintBufferPixmap(const QSize &size, double devicePixelRatio) :
650 QCPAbstractPaintBuffer(size, devicePixelRatio)
651{
653}
654
655QCPPaintBufferPixmap::~QCPPaintBufferPixmap()
656{
657}
658
659/* inherits documentation from base class */
661{
662 QCPPainter *result = new QCPPainter(&mBuffer);
663#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
665#endif
666 return result;
667}
668
669/* inherits documentation from base class */
671{
672 if (painter && painter->isActive())
673 painter->drawPixmap(0, 0, mBuffer);
674 else
675 qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed";
676}
677
678/* inherits documentation from base class */
680{
681 mBuffer.fill(color);
682}
683
684/* inherits documentation from base class */
686{
688 if (!qFuzzyCompare(1.0, mDevicePixelRatio))
689 {
690#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
691 mBuffer = QPixmap(mSize*mDevicePixelRatio);
692 mBuffer.setDevicePixelRatio(mDevicePixelRatio);
693#else
694 qDebug() << Q_FUNC_INFO << "Device pixel ratios not supported for Qt versions before 5.4";
695 mDevicePixelRatio = 1.0;
696 mBuffer = QPixmap(mSize);
697#endif
698 } else
699 {
700 mBuffer = QPixmap(mSize);
701 }
702}
703
704
705#ifdef QCP_OPENGL_PBUFFER
706////////////////////////////////////////////////////////////////////////////////////////////////////
707//////////////////// QCPPaintBufferGlPbuffer
708////////////////////////////////////////////////////////////////////////////////////////////////////
709
710/*! \class QCPPaintBufferGlPbuffer
711 \brief A paint buffer based on OpenGL pixel buffers, using hardware accelerated rendering
712
713 This paint buffer is one of the OpenGL paint buffers which facilitate hardware accelerated plot
714 rendering. It is based on OpenGL pixel buffers (pbuffer) and is used in Qt versions before 5.0.
715 (See \ref QCPPaintBufferGlFbo used in newer Qt versions.)
716
717 The OpenGL paint buffers are used if \ref QCustomPlot::setOpenGl is set to true, and if they are
718 supported by the system.
719*/
720
721/*!
722 Creates a \ref QCPPaintBufferGlPbuffer instance with the specified \a size and \a
723 devicePixelRatio, if applicable.
724
725 The parameter \a multisamples defines how many samples are used per pixel. Higher values thus
726 result in higher quality antialiasing. If the specified \a multisamples value exceeds the
727 capability of the graphics hardware, the highest supported multisampling is used.
728*/
729QCPPaintBufferGlPbuffer::QCPPaintBufferGlPbuffer(const QSize &size, double devicePixelRatio, int multisamples) :
730 QCPAbstractPaintBuffer(size, devicePixelRatio),
731 mGlPBuffer(0),
732 mMultisamples(qMax(0, multisamples))
733{
734 QCPPaintBufferGlPbuffer::reallocateBuffer();
735}
736
737QCPPaintBufferGlPbuffer::~QCPPaintBufferGlPbuffer()
738{
739 if (mGlPBuffer)
740 delete mGlPBuffer;
741}
742
743/* inherits documentation from base class */
744QCPPainter *QCPPaintBufferGlPbuffer::startPainting()
745{
746 if (!mGlPBuffer->isValid())
747 {
748 qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?";
749 return 0;
750 }
751
752 QCPPainter *result = new QCPPainter(mGlPBuffer);
754 return result;
755}
756
757/* inherits documentation from base class */
758void QCPPaintBufferGlPbuffer::draw(QCPPainter *painter) const
759{
760 if (!painter || !painter->isActive())
761 {
762 qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed";
763 return;
764 }
765 if (!mGlPBuffer->isValid())
766 {
767 qDebug() << Q_FUNC_INFO << "OpenGL pbuffer isn't valid, reallocateBuffer was not called?";
768 return;
769 }
770 painter->drawImage(0, 0, mGlPBuffer->toImage());
771}
772
773/* inherits documentation from base class */
774void QCPPaintBufferGlPbuffer::clear(const QColor &color)
775{
776 if (mGlPBuffer->isValid())
777 {
778 mGlPBuffer->makeCurrent();
779 glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
780 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
781 mGlPBuffer->doneCurrent();
782 } else
783 qDebug() << Q_FUNC_INFO << "OpenGL pbuffer invalid or context not current";
784}
785
786/* inherits documentation from base class */
787void QCPPaintBufferGlPbuffer::reallocateBuffer()
788{
789 if (mGlPBuffer)
790 delete mGlPBuffer;
791
792 QGLFormat format;
793 format.setAlpha(true);
794 format.setSamples(mMultisamples);
795 mGlPBuffer = new QGLPixelBuffer(mSize, format);
796}
797#endif // QCP_OPENGL_PBUFFER
798
799
800#ifdef QCP_OPENGL_FBO
801////////////////////////////////////////////////////////////////////////////////////////////////////
802//////////////////// QCPPaintBufferGlFbo
803////////////////////////////////////////////////////////////////////////////////////////////////////
804
805/*! \class QCPPaintBufferGlFbo
806 \brief A paint buffer based on OpenGL frame buffers objects, using hardware accelerated rendering
807
808 This paint buffer is one of the OpenGL paint buffers which facilitate hardware accelerated plot
809 rendering. It is based on OpenGL frame buffer objects (fbo) and is used in Qt versions 5.0 and
810 higher. (See \ref QCPPaintBufferGlPbuffer used in older Qt versions.)
811
812 The OpenGL paint buffers are used if \ref QCustomPlot::setOpenGl is set to true, and if they are
813 supported by the system.
814*/
815
816/*!
817 Creates a \ref QCPPaintBufferGlFbo instance with the specified \a size and \a devicePixelRatio,
818 if applicable.
819
820 All frame buffer objects shall share one OpenGL context and paint device, which need to be set up
821 externally and passed via \a glContext and \a glPaintDevice. The set-up is done in \ref
822 QCustomPlot::setupOpenGl and the context and paint device are managed by the parent QCustomPlot
823 instance.
824*/
825QCPPaintBufferGlFbo::QCPPaintBufferGlFbo(const QSize &size, double devicePixelRatio, QWeakPointer<QOpenGLContext> glContext, QWeakPointer<QOpenGLPaintDevice> glPaintDevice) :
826 QCPAbstractPaintBuffer(size, devicePixelRatio),
827 mGlContext(glContext),
828 mGlPaintDevice(glPaintDevice),
829 mGlFrameBuffer(0)
830{
831 QCPPaintBufferGlFbo::reallocateBuffer();
832}
833
834QCPPaintBufferGlFbo::~QCPPaintBufferGlFbo()
835{
836 if (mGlFrameBuffer)
837 delete mGlFrameBuffer;
838}
839
840/* inherits documentation from base class */
841QCPPainter *QCPPaintBufferGlFbo::startPainting()
842{
843 QSharedPointer<QOpenGLPaintDevice> paintDevice = mGlPaintDevice.toStrongRef();
844 QSharedPointer<QOpenGLContext> context = mGlContext.toStrongRef();
845 if (!paintDevice)
846 {
847 qDebug() << Q_FUNC_INFO << "OpenGL paint device doesn't exist";
848 return 0;
849 }
850 if (!context)
851 {
852 qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist";
853 return 0;
854 }
855 if (!mGlFrameBuffer)
856 {
857 qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?";
858 return 0;
859 }
860
861 if (QOpenGLContext::currentContext() != context.data())
862 context->makeCurrent(context->surface());
863 mGlFrameBuffer->bind();
864 QCPPainter *result = new QCPPainter(paintDevice.data());
865#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
867#endif
868 return result;
869}
870
871/* inherits documentation from base class */
872void QCPPaintBufferGlFbo::donePainting()
873{
874 if (mGlFrameBuffer && mGlFrameBuffer->isBound())
875 mGlFrameBuffer->release();
876 else
877 qDebug() << Q_FUNC_INFO << "Either OpenGL frame buffer not valid or was not bound";
878}
879
880/* inherits documentation from base class */
881void QCPPaintBufferGlFbo::draw(QCPPainter *painter) const
882{
883 if (!painter || !painter->isActive())
884 {
885 qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed";
886 return;
887 }
888 if (!mGlFrameBuffer)
889 {
890 qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?";
891 return;
892 }
893 painter->drawImage(0, 0, mGlFrameBuffer->toImage());
894}
895
896/* inherits documentation from base class */
897void QCPPaintBufferGlFbo::clear(const QColor &color)
898{
899 QSharedPointer<QOpenGLContext> context = mGlContext.toStrongRef();
900 if (!context)
901 {
902 qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist";
903 return;
904 }
905 if (!mGlFrameBuffer)
906 {
907 qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?";
908 return;
909 }
910
911 if (QOpenGLContext::currentContext() != context.data())
912 context->makeCurrent(context->surface());
913 mGlFrameBuffer->bind();
914 glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
915 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
916 mGlFrameBuffer->release();
917}
918
919/* inherits documentation from base class */
920void QCPPaintBufferGlFbo::reallocateBuffer()
921{
922 // release and delete possibly existing framebuffer:
923 if (mGlFrameBuffer)
924 {
925 if (mGlFrameBuffer->isBound())
926 mGlFrameBuffer->release();
927 delete mGlFrameBuffer;
928 mGlFrameBuffer = 0;
929 }
930
931 QSharedPointer<QOpenGLPaintDevice> paintDevice = mGlPaintDevice.toStrongRef();
932 QSharedPointer<QOpenGLContext> context = mGlContext.toStrongRef();
933 if (!paintDevice)
934 {
935 qDebug() << Q_FUNC_INFO << "OpenGL paint device doesn't exist";
936 return;
937 }
938 if (!context)
939 {
940 qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist";
941 return;
942 }
943
944 // create new fbo with appropriate size:
945 context->makeCurrent(context->surface());
946 QOpenGLFramebufferObjectFormat frameBufferFormat;
947 frameBufferFormat.setSamples(context->format().samples());
949 mGlFrameBuffer = new QOpenGLFramebufferObject(mSize*mDevicePixelRatio, frameBufferFormat);
950 if (paintDevice->size() != mSize*mDevicePixelRatio)
951 paintDevice->setSize(mSize*mDevicePixelRatio);
952#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
953 paintDevice->setDevicePixelRatio(mDevicePixelRatio);
954#endif
955}
956#endif // QCP_OPENGL_FBO
957/* end of 'src/paintbuffer.cpp' */
958
959
960/* including file 'src/layer.cpp' */
961/* modified 2021-03-29T02:30:44, size 37615 */
962
963////////////////////////////////////////////////////////////////////////////////////////////////////
964//////////////////// QCPLayer
965////////////////////////////////////////////////////////////////////////////////////////////////////
966
967/*! \class QCPLayer
968 \brief A layer that may contain objects, to control the rendering order
969
970 The Layering system of QCustomPlot is the mechanism to control the rendering order of the
971 elements inside the plot.
972
973 It is based on the two classes QCPLayer and QCPLayerable. QCustomPlot holds an ordered list of
974 one or more instances of QCPLayer (see QCustomPlot::addLayer, QCustomPlot::layer,
975 QCustomPlot::moveLayer, etc.). When replotting, QCustomPlot goes through the list of layers
976 bottom to top and successively draws the layerables of the layers into the paint buffer(s).
977
978 A QCPLayer contains an ordered list of QCPLayerable instances. QCPLayerable is an abstract base
979 class from which almost all visible objects derive, like axes, grids, graphs, items, etc.
980
981 \section qcplayer-defaultlayers Default layers
982
983 Initially, QCustomPlot has six layers: "background", "grid", "main", "axes", "legend" and
984 "overlay" (in that order). On top is the "overlay" layer, which only contains the QCustomPlot's
985 selection rect (\ref QCustomPlot::selectionRect). The next two layers "axes" and "legend" contain
986 the default axes and legend, so they will be drawn above plottables. In the middle, there is the
987 "main" layer. It is initially empty and set as the current layer (see
988 QCustomPlot::setCurrentLayer). This means, all new plottables, items etc. are created on this
989 layer by default. Then comes the "grid" layer which contains the QCPGrid instances (which belong
990 tightly to QCPAxis, see \ref QCPAxis::grid). The Axis rect background shall be drawn behind
991 everything else, thus the default QCPAxisRect instance is placed on the "background" layer. Of
992 course, the layer affiliation of the individual objects can be changed as required (\ref
993 QCPLayerable::setLayer).
994
995 \section qcplayer-ordering Controlling the rendering order via layers
996
997 Controlling the ordering of layerables in the plot is easy: Create a new layer in the position
998 you want the layerable to be in, e.g. above "main", with \ref QCustomPlot::addLayer. Then set the
999 current layer with \ref QCustomPlot::setCurrentLayer to that new layer and finally create the
1000 objects normally. They will be placed on the new layer automatically, due to the current layer
1001 setting. Alternatively you could have also ignored the current layer setting and just moved the
1002 objects with \ref QCPLayerable::setLayer to the desired layer after creating them.
1003
1004 It is also possible to move whole layers. For example, If you want the grid to be shown in front
1005 of all plottables/items on the "main" layer, just move it above "main" with
1006 QCustomPlot::moveLayer.
1007
1008 The rendering order within one layer is simply by order of creation or insertion. The item
1009 created last (or added last to the layer), is drawn on top of all other objects on that layer.
1010
1011 When a layer is deleted, the objects on it are not deleted with it, but fall on the layer below
1012 the deleted layer, see QCustomPlot::removeLayer.
1013
1014 \section qcplayer-buffering Replotting only a specific layer
1015
1016 If the layer mode (\ref setMode) is set to \ref lmBuffered, you can replot only this specific
1017 layer by calling \ref replot. In certain situations this can provide better replot performance,
1018 compared with a full replot of all layers. Upon creation of a new layer, the layer mode is
1019 initialized to \ref lmLogical. The only layer that is set to \ref lmBuffered in a new \ref
1020 QCustomPlot instance is the "overlay" layer, containing the selection rect.
1021*/
1022
1023/* start documentation of inline functions */
1024
1025/*! \fn QList<QCPLayerable*> QCPLayer::children() const
1026
1027 Returns a list of all layerables on this layer. The order corresponds to the rendering order:
1028 layerables with higher indices are drawn above layerables with lower indices.
1029*/
1030
1031/*! \fn int QCPLayer::index() const
1032
1033 Returns the index this layer has in the QCustomPlot. The index is the integer number by which this layer can be
1034 accessed via \ref QCustomPlot::layer.
1035
1036 Layers with higher indices will be drawn above layers with lower indices.
1037*/
1038
1039/* end documentation of inline functions */
1040
1041/*!
1042 Creates a new QCPLayer instance.
1043
1044 Normally you shouldn't directly instantiate layers, use \ref QCustomPlot::addLayer instead.
1045
1046 \warning It is not checked that \a layerName is actually a unique layer name in \a parentPlot.
1047 This check is only performed by \ref QCustomPlot::addLayer.
1048*/
1049QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) :
1050 QObject(parentPlot),
1051 mParentPlot(parentPlot),
1052 mName(layerName),
1053 mIndex(-1), // will be set to a proper value by the QCustomPlot layer creation function
1054 mVisible(true),
1055 mMode(lmLogical)
1056{
1057 // Note: no need to make sure layerName is unique, because layer
1058 // management is done with QCustomPlot functions.
1059}
1060
1061QCPLayer::~QCPLayer()
1062{
1063 // If child layerables are still on this layer, detach them, so they don't try to reach back to this
1064 // then invalid layer once they get deleted/moved themselves. This only happens when layers are deleted
1065 // directly, like in the QCustomPlot destructor. (The regular layer removal procedure for the user is to
1066 // call QCustomPlot::removeLayer, which moves all layerables off this layer before deleting it.)
1067
1068 while (!mChildren.isEmpty())
1069 mChildren.last()->setLayer(nullptr); // removes itself from mChildren via removeChild()
1070
1071 if (mParentPlot->currentLayer() == this)
1072 qDebug() << Q_FUNC_INFO << "The parent plot's mCurrentLayer will be a dangling pointer. Should have been set to a valid layer or nullptr beforehand.";
1073}
1074
1075/*!
1076 Sets whether this layer is visible or not. If \a visible is set to false, all layerables on this
1077 layer will be invisible.
1078
1079 This function doesn't change the visibility property of the layerables (\ref
1080 QCPLayerable::setVisible), but the \ref QCPLayerable::realVisibility of each layerable takes the
1081 visibility of the parent layer into account.
1082*/
1083void QCPLayer::setVisible(bool visible)
1084{
1085 mVisible = visible;
1086}
1087
1088/*!
1089 Sets the rendering mode of this layer.
1090
1091 If \a mode is set to \ref lmBuffered for a layer, it will be given a dedicated paint buffer by
1092 the parent QCustomPlot instance. This means it may be replotted individually by calling \ref
1093 QCPLayer::replot, without needing to replot all other layers.
1094
1095 Layers which are set to \ref lmLogical (the default) are used only to define the rendering order
1096 and can't be replotted individually.
1097
1098 Note that each layer which is set to \ref lmBuffered requires additional paint buffers for the
1099 layers below, above and for the layer itself. This increases the memory consumption and
1100 (slightly) decreases the repainting speed because multiple paint buffers need to be joined. So
1101 you should carefully choose which layers benefit from having their own paint buffer. A typical
1102 example would be a layer which contains certain layerables (e.g. items) that need to be changed
1103 and thus replotted regularly, while all other layerables on other layers stay static. By default,
1104 only the topmost layer called "overlay" is in mode \ref lmBuffered, and contains the selection
1105 rect.
1106
1107 \see replot
1108*/
1110{
1111 if (mMode != mode)
1112 {
1113 mMode = mode;
1115 pb->setInvalidated();
1116 }
1117}
1118
1119/*! \internal
1120
1121 Draws the contents of this layer with the provided \a painter.
1122
1123 \see replot, drawToPaintBuffer
1124*/
1126{
1127 foreach (QCPLayerable *child, mChildren)
1128 {
1129 if (child->realVisibility())
1130 {
1131 painter->save();
1132 painter->setClipRect(child->clipRect().translated(0, -1));
1133 child->applyDefaultAntialiasingHint(painter);
1134 child->draw(painter);
1135 painter->restore();
1136 }
1137 }
1138}
1139
1140/*! \internal
1141
1142 Draws the contents of this layer into the paint buffer which is associated with this layer. The
1143 association is established by the parent QCustomPlot, which manages all paint buffers (see \ref
1144 QCustomPlot::setupPaintBuffers).
1145
1146 \see draw
1147*/
1149{
1151 {
1152 if (QCPPainter *painter = pb->startPainting())
1153 {
1154 if (painter->isActive())
1155 draw(painter);
1156 else
1157 qDebug() << Q_FUNC_INFO << "paint buffer returned inactive painter";
1158 delete painter;
1159 pb->donePainting();
1160 } else
1161 qDebug() << Q_FUNC_INFO << "paint buffer returned nullptr painter";
1162 } else
1163 qDebug() << Q_FUNC_INFO << "no valid paint buffer associated with this layer";
1164}
1165
1166/*!
1167 If the layer mode (\ref setMode) is set to \ref lmBuffered, this method allows replotting only
1168 the layerables on this specific layer, without the need to replot all other layers (as a call to
1169 \ref QCustomPlot::replot would do).
1170
1171 QCustomPlot also makes sure to replot all layers instead of only this one, if the layer ordering
1172 or any layerable-layer-association has changed since the last full replot and any other paint
1173 buffers were thus invalidated.
1174
1175 If the layer mode is \ref lmLogical however, this method simply calls \ref QCustomPlot::replot on
1176 the parent QCustomPlot instance.
1177
1178 \see draw
1179*/
1181{
1182 if (mMode == lmBuffered && !mParentPlot->hasInvalidatedPaintBuffers())
1183 {
1185 {
1186 pb->clear(Qt::transparent);
1188 pb->setInvalidated(false); // since layer is lmBuffered, we know only this layer is on buffer and we can reset invalidated flag
1189 mParentPlot->update();
1190 } else
1191 qDebug() << Q_FUNC_INFO << "no valid paint buffer associated with this layer";
1192 } else
1193 mParentPlot->replot();
1194}
1195
1196/*! \internal
1197
1198 Adds the \a layerable to the list of this layer. If \a prepend is set to true, the layerable will
1199 be prepended to the list, i.e. be drawn beneath the other layerables already in the list.
1200
1201 This function does not change the \a mLayer member of \a layerable to this layer. (Use
1202 QCPLayerable::setLayer to change the layer of an object, not this function.)
1203
1204 \see removeChild
1205*/
1206void QCPLayer::addChild(QCPLayerable *layerable, bool prepend)
1207{
1208 if (!mChildren.contains(layerable))
1209 {
1210 if (prepend)
1211 mChildren.prepend(layerable);
1212 else
1213 mChildren.append(layerable);
1215 pb->setInvalidated();
1216 } else
1217 qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast<quintptr>(layerable);
1218}
1219
1220/*! \internal
1221
1222 Removes the \a layerable from the list of this layer.
1223
1224 This function does not change the \a mLayer member of \a layerable. (Use QCPLayerable::setLayer
1225 to change the layer of an object, not this function.)
1226
1227 \see addChild
1228*/
1230{
1231 if (mChildren.removeOne(layerable))
1232 {
1234 pb->setInvalidated();
1235 } else
1236 qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast<quintptr>(layerable);
1237}
1238
1239
1240////////////////////////////////////////////////////////////////////////////////////////////////////
1241//////////////////// QCPLayerable
1242////////////////////////////////////////////////////////////////////////////////////////////////////
1243
1244/*! \class QCPLayerable
1245 \brief Base class for all drawable objects
1246
1247 This is the abstract base class most visible objects derive from, e.g. plottables, axes, grid
1248 etc.
1249
1250 Every layerable is on a layer (QCPLayer) which allows controlling the rendering order by stacking
1251 the layers accordingly.
1252
1253 For details about the layering mechanism, see the QCPLayer documentation.
1254*/
1255
1256/* start documentation of inline functions */
1257
1258/*! \fn QCPLayerable *QCPLayerable::parentLayerable() const
1259
1260 Returns the parent layerable of this layerable. The parent layerable is used to provide
1261 visibility hierarchies in conjunction with the method \ref realVisibility. This way, layerables
1262 only get drawn if their parent layerables are visible, too.
1263
1264 Note that a parent layerable is not necessarily also the QObject parent for memory management.
1265 Further, a layerable doesn't always have a parent layerable, so this function may return \c
1266 nullptr.
1267
1268 A parent layerable is set implicitly when placed inside layout elements and doesn't need to be
1269 set manually by the user.
1270*/
1271
1272/* end documentation of inline functions */
1273/* start documentation of pure virtual functions */
1274
1275/*! \fn virtual void QCPLayerable::applyDefaultAntialiasingHint(QCPPainter *painter) const = 0
1276 \internal
1277
1278 This function applies the default antialiasing setting to the specified \a painter, using the
1279 function \ref applyAntialiasingHint. It is the antialiasing state the painter is put in, when
1280 \ref draw is called on the layerable. If the layerable has multiple entities whose antialiasing
1281 setting may be specified individually, this function should set the antialiasing state of the
1282 most prominent entity. In this case however, the \ref draw function usually calls the specialized
1283 versions of this function before drawing each entity, effectively overriding the setting of the
1284 default antialiasing hint.
1285
1286 <b>First example:</b> QCPGraph has multiple entities that have an antialiasing setting: The graph
1287 line, fills and scatters. Those can be configured via QCPGraph::setAntialiased,
1288 QCPGraph::setAntialiasedFill and QCPGraph::setAntialiasedScatters. Consequently, there isn't only
1289 the QCPGraph::applyDefaultAntialiasingHint function (which corresponds to the graph line's
1290 antialiasing), but specialized ones like QCPGraph::applyFillAntialiasingHint and
1291 QCPGraph::applyScattersAntialiasingHint. So before drawing one of those entities, QCPGraph::draw
1292 calls the respective specialized applyAntialiasingHint function.
1293
1294 <b>Second example:</b> QCPItemLine consists only of a line so there is only one antialiasing
1295 setting which can be controlled with QCPItemLine::setAntialiased. (This function is inherited by
1296 all layerables. The specialized functions, as seen on QCPGraph, must be added explicitly to the
1297 respective layerable subclass.) Consequently it only has the normal
1298 QCPItemLine::applyDefaultAntialiasingHint. The \ref QCPItemLine::draw function doesn't need to
1299 care about setting any antialiasing states, because the default antialiasing hint is already set
1300 on the painter when the \ref draw function is called, and that's the state it wants to draw the
1301 line with.
1302*/
1303
1304/*! \fn virtual void QCPLayerable::draw(QCPPainter *painter) const = 0
1305 \internal
1306
1307 This function draws the layerable with the specified \a painter. It is only called by
1308 QCustomPlot, if the layerable is visible (\ref setVisible).
1309
1310 Before this function is called, the painter's antialiasing state is set via \ref
1311 applyDefaultAntialiasingHint, see the documentation there. Further, the clipping rectangle was
1312 set to \ref clipRect.
1313*/
1314
1315/* end documentation of pure virtual functions */
1316/* start documentation of signals */
1317
1318/*! \fn void QCPLayerable::layerChanged(QCPLayer *newLayer);
1319
1320 This signal is emitted when the layer of this layerable changes, i.e. this layerable is moved to
1321 a different layer.
1322
1323 \see setLayer
1324*/
1325
1326/* end documentation of signals */
1327
1328/*!
1329 Creates a new QCPLayerable instance.
1330
1331 Since QCPLayerable is an abstract base class, it can't be instantiated directly. Use one of the
1332 derived classes.
1333
1334 If \a plot is provided, it automatically places itself on the layer named \a targetLayer. If \a
1335 targetLayer is an empty string, it places itself on the current layer of the plot (see \ref
1336 QCustomPlot::setCurrentLayer).
1337
1338 It is possible to provide \c nullptr as \a plot. In that case, you should assign a parent plot at
1339 a later time with \ref initializeParentPlot.
1340
1341 The layerable's parent layerable is set to \a parentLayerable, if provided. Direct layerable
1342 parents are mainly used to control visibility in a hierarchy of layerables. This means a
1343 layerable is only drawn, if all its ancestor layerables are also visible. Note that \a
1344 parentLayerable does not become the QObject-parent (for memory management) of this layerable, \a
1345 plot does. It is not uncommon to set the QObject-parent to something else in the constructors of
1346 QCPLayerable subclasses, to guarantee a working destruction hierarchy.
1347*/
1348QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable *parentLayerable) :
1349 QObject(plot),
1350 mVisible(true),
1351 mParentPlot(plot),
1352 mParentLayerable(parentLayerable),
1353 mLayer(nullptr),
1354 mAntialiased(true)
1355{
1356 if (mParentPlot)
1357 {
1358 if (targetLayer.isEmpty())
1359 setLayer(mParentPlot->currentLayer());
1360 else if (!setLayer(targetLayer))
1361 qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to" << targetLayer << "failed.";
1362 }
1363}
1364
1365QCPLayerable::~QCPLayerable()
1366{
1367 if (mLayer)
1368 {
1369 mLayer->removeChild(this);
1370 mLayer = nullptr;
1371 }
1372}
1373
1374/*!
1375 Sets the visibility of this layerable object. If an object is not visible, it will not be drawn
1376 on the QCustomPlot surface, and user interaction with it (e.g. click and selection) is not
1377 possible.
1378*/
1380{
1381 mVisible = on;
1382}
1383
1384/*!
1385 Sets the \a layer of this layerable object. The object will be placed on top of the other objects
1386 already on \a layer.
1387
1388 If \a layer is 0, this layerable will not be on any layer and thus not appear in the plot (or
1389 interact/receive events).
1390
1391 Returns true if the layer of this layerable was successfully changed to \a layer.
1392*/
1394{
1395 return moveToLayer(layer, false);
1396}
1397
1398/*! \overload
1399 Sets the layer of this layerable object by name
1400
1401 Returns true on success, i.e. if \a layerName is a valid layer name.
1402*/
1403bool QCPLayerable::setLayer(const QString &layerName)
1404{
1405 if (!mParentPlot)
1406 {
1407 qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1408 return false;
1409 }
1410 if (QCPLayer *layer = mParentPlot->layer(layerName))
1411 {
1412 return setLayer(layer);
1413 } else
1414 {
1415 qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
1416 return false;
1417 }
1418}
1419
1420/*!
1421 Sets whether this object will be drawn antialiased or not.
1422
1423 Note that antialiasing settings may be overridden by QCustomPlot::setAntialiasedElements and
1424 QCustomPlot::setNotAntialiasedElements.
1425*/
1427{
1428 mAntialiased = enabled;
1429}
1430
1431/*!
1432 Returns whether this layerable is visible, taking the visibility of the layerable parent and the
1433 visibility of this layerable's layer into account. This is the method that is consulted to decide
1434 whether a layerable shall be drawn or not.
1435
1436 If this layerable has a direct layerable parent (usually set via hierarchies implemented in
1437 subclasses, like in the case of \ref QCPLayoutElement), this function returns true only if this
1438 layerable has its visibility set to true and the parent layerable's \ref realVisibility returns
1439 true.
1440*/
1442{
1443 return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable.data()->realVisibility());
1444}
1445
1446/*!
1447 This function is used to decide whether a click hits a layerable object or not.
1448
1449 \a pos is a point in pixel coordinates on the QCustomPlot surface. This function returns the
1450 shortest pixel distance of this point to the object. If the object is either invisible or the
1451 distance couldn't be determined, -1.0 is returned. Further, if \a onlySelectable is true and the
1452 object is not selectable, -1.0 is returned, too.
1453
1454 If the object is represented not by single lines but by an area like a \ref QCPItemText or the
1455 bars of a \ref QCPBars plottable, a click inside the area should also be considered a hit. In
1456 these cases this function thus returns a constant value greater zero but still below the parent
1457 plot's selection tolerance. (typically the selectionTolerance multiplied by 0.99).
1458
1459 Providing a constant value for area objects allows selecting line objects even when they are
1460 obscured by such area objects, by clicking close to the lines (i.e. closer than
1461 0.99*selectionTolerance).
1462
1463 The actual setting of the selection state is not done by this function. This is handled by the
1464 parent QCustomPlot when the mouseReleaseEvent occurs, and the finally selected object is notified
1465 via the \ref selectEvent/\ref deselectEvent methods.
1466
1467 \a details is an optional output parameter. Every layerable subclass may place any information
1468 in \a details. This information will be passed to \ref selectEvent when the parent QCustomPlot
1469 decides on the basis of this selectTest call, that the object was successfully selected. The
1470 subsequent call to \ref selectEvent will carry the \a details. This is useful for multi-part
1471 objects (like QCPAxis). This way, a possibly complex calculation to decide which part was clicked
1472 is only done once in \ref selectTest. The result (i.e. the actually clicked part) can then be
1473 placed in \a details. So in the subsequent \ref selectEvent, the decision which part was
1474 selected doesn't have to be done a second time for a single selection operation.
1475
1476 In the case of 1D Plottables (\ref QCPAbstractPlottable1D, like \ref QCPGraph or \ref QCPBars) \a
1477 details will be set to a \ref QCPDataSelection, describing the closest data point to \a pos.
1478
1479 You may pass \c nullptr as \a details to indicate that you are not interested in those selection
1480 details.
1481
1482 \see selectEvent, deselectEvent, mousePressEvent, wheelEvent, QCustomPlot::setInteractions,
1483 QCPAbstractPlottable1D::selectTestRect
1484*/
1485double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
1486{
1487 Q_UNUSED(pos)
1488 Q_UNUSED(onlySelectable)
1489 Q_UNUSED(details)
1490 return -1.0;
1491}
1492
1493/*! \internal
1494
1495 Sets the parent plot of this layerable. Use this function once to set the parent plot if you have
1496 passed \c nullptr in the constructor. It can not be used to move a layerable from one QCustomPlot
1497 to another one.
1498
1499 Note that, unlike when passing a non \c nullptr parent plot in the constructor, this function
1500 does not make \a parentPlot the QObject-parent of this layerable. If you want this, call
1501 QObject::setParent(\a parentPlot) in addition to this function.
1502
1503 Further, you will probably want to set a layer (\ref setLayer) after calling this function, to
1504 make the layerable appear on the QCustomPlot.
1505
1506 The parent plot change will be propagated to subclasses via a call to \ref parentPlotInitialized
1507 so they can react accordingly (e.g. also initialize the parent plot of child layerables, like
1508 QCPLayout does).
1509*/
1511{
1512 if (mParentPlot)
1513 {
1514 qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized";
1515 return;
1516 }
1517
1518 if (!parentPlot)
1519 qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
1520
1521 mParentPlot = parentPlot;
1522 parentPlotInitialized(mParentPlot);
1523}
1524
1525/*! \internal
1526
1527 Sets the parent layerable of this layerable to \a parentLayerable. Note that \a parentLayerable does not
1528 become the QObject-parent (for memory management) of this layerable.
1529
1530 The parent layerable has influence on the return value of the \ref realVisibility method. Only
1531 layerables with a fully visible parent tree will return true for \ref realVisibility, and thus be
1532 drawn.
1533
1534 \see realVisibility
1535*/
1537{
1538 mParentLayerable = parentLayerable;
1539}
1540
1541/*! \internal
1542
1543 Moves this layerable object to \a layer. If \a prepend is true, this object will be prepended to
1544 the new layer's list, i.e. it will be drawn below the objects already on the layer. If it is
1545 false, the object will be appended.
1546
1547 Returns true on success, i.e. if \a layer is a valid layer.
1548*/
1549bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend)
1550{
1551 if (layer && !mParentPlot)
1552 {
1553 qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1554 return false;
1555 }
1556 if (layer && layer->parentPlot() != mParentPlot)
1557 {
1558 qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable";
1559 return false;
1560 }
1561
1562 QCPLayer *oldLayer = mLayer;
1563 if (mLayer)
1564 mLayer->removeChild(this);
1565 mLayer = layer;
1566 if (mLayer)
1567 mLayer->addChild(this, prepend);
1568 if (mLayer != oldLayer)
1569 emit layerChanged(mLayer);
1570 return true;
1571}
1572
1573/*! \internal
1574
1575 Sets the QCPainter::setAntialiasing state on the provided \a painter, depending on the \a
1576 localAntialiased value as well as the overrides \ref QCustomPlot::setAntialiasedElements and \ref
1577 QCustomPlot::setNotAntialiasedElements. Which override enum this function takes into account is
1578 controlled via \a overrideElement.
1579*/
1580void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
1581{
1582 if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement))
1583 painter->setAntialiasing(false);
1584 else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement))
1585 painter->setAntialiasing(true);
1586 else
1587 painter->setAntialiasing(localAntialiased);
1588}
1589
1590/*! \internal
1591
1592 This function is called by \ref initializeParentPlot, to allow subclasses to react on the setting
1593 of a parent plot. This is the case when \c nullptr was passed as parent plot in the constructor,
1594 and the parent plot is set at a later time.
1595
1596 For example, QCPLayoutElement/QCPLayout hierarchies may be created independently of any
1597 QCustomPlot at first. When they are then added to a layout inside the QCustomPlot, the top level
1598 element of the hierarchy gets its parent plot initialized with \ref initializeParentPlot. To
1599 propagate the parent plot to all the children of the hierarchy, the top level element then uses
1600 this function to pass the parent plot on to its child elements.
1601
1602 The default implementation does nothing.
1603
1604 \see initializeParentPlot
1605*/
1607{
1608 Q_UNUSED(parentPlot)
1609}
1610
1611/*! \internal
1612
1613 Returns the selection category this layerable shall belong to. The selection category is used in
1614 conjunction with \ref QCustomPlot::setInteractions to control which objects are selectable and
1615 which aren't.
1616
1617 Subclasses that don't fit any of the normal \ref QCP::Interaction values can use \ref
1618 QCP::iSelectOther. This is what the default implementation returns.
1619
1620 \see QCustomPlot::setInteractions
1621*/
1626
1627/*! \internal
1628
1629 Returns the clipping rectangle of this layerable object. By default, this is the viewport of the
1630 parent QCustomPlot. Specific subclasses may reimplement this function to provide different
1631 clipping rects.
1632
1633 The returned clipping rect is set on the painter before the draw function of the respective
1634 object is called.
1635*/
1637{
1638 if (mParentPlot)
1639 return mParentPlot->viewport();
1640 else
1641 return {};
1642}
1643
1644/*! \internal
1645
1646 This event is called when the layerable shall be selected, as a consequence of a click by the
1647 user. Subclasses should react to it by setting their selection state appropriately. The default
1648 implementation does nothing.
1649
1650 \a event is the mouse event that caused the selection. \a additive indicates, whether the user
1651 was holding the multi-select-modifier while performing the selection (see \ref
1652 QCustomPlot::setMultiSelectModifier). if \a additive is true, the selection state must be toggled
1653 (i.e. become selected when unselected and unselected when selected).
1654
1655 Every selectEvent is preceded by a call to \ref selectTest, which has returned positively (i.e.
1656 returned a value greater than 0 and less than the selection tolerance of the parent QCustomPlot).
1657 The \a details data you output from \ref selectTest is fed back via \a details here. You may
1658 use it to transport any kind of information from the selectTest to the possibly subsequent
1659 selectEvent. Usually \a details is used to transfer which part was clicked, if it is a layerable
1660 that has multiple individually selectable parts (like QCPAxis). This way selectEvent doesn't need
1661 to do the calculation again to find out which part was actually clicked.
1662
1663 \a selectionStateChanged is an output parameter. If the pointer is non-null, this function must
1664 set the value either to true or false, depending on whether the selection state of this layerable
1665 was actually changed. For layerables that only are selectable as a whole and not in parts, this
1666 is simple: if \a additive is true, \a selectionStateChanged must also be set to true, because the
1667 selection toggles. If \a additive is false, \a selectionStateChanged is only set to true, if the
1668 layerable was previously unselected and now is switched to the selected state.
1669
1670 \see selectTest, deselectEvent
1671*/
1672void QCPLayerable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
1673{
1674 Q_UNUSED(event)
1675 Q_UNUSED(additive)
1676 Q_UNUSED(details)
1677 Q_UNUSED(selectionStateChanged)
1678}
1679
1680/*! \internal
1681
1682 This event is called when the layerable shall be deselected, either as consequence of a user
1683 interaction or a call to \ref QCustomPlot::deselectAll. Subclasses should react to it by
1684 unsetting their selection appropriately.
1685
1686 just as in \ref selectEvent, the output parameter \a selectionStateChanged (if non-null), must
1687 return true or false when the selection state of this layerable has changed or not changed,
1688 respectively.
1689
1690 \see selectTest, selectEvent
1691*/
1692void QCPLayerable::deselectEvent(bool *selectionStateChanged)
1693{
1694 Q_UNUSED(selectionStateChanged)
1695}
1696
1697/*!
1698 This event gets called when the user presses a mouse button while the cursor is over the
1699 layerable. Whether a cursor is over the layerable is decided by a preceding call to \ref
1700 selectTest.
1701
1702 The current pixel position of the cursor on the QCustomPlot widget is accessible via \c
1703 event->pos(). The parameter \a details contains layerable-specific details about the hit, which
1704 were generated in the previous call to \ref selectTest. For example, One-dimensional plottables
1705 like \ref QCPGraph or \ref QCPBars convey the clicked data point in the \a details parameter, as
1706 \ref QCPDataSelection packed as QVariant. Multi-part objects convey the specific \c
1707 SelectablePart that was hit (e.g. \ref QCPAxis::SelectablePart in the case of axes).
1708
1709 QCustomPlot uses an event propagation system that works the same as Qt's system. If your
1710 layerable doesn't reimplement the \ref mousePressEvent or explicitly calls \c event->ignore() in
1711 its reimplementation, the event will be propagated to the next layerable in the stacking order.
1712
1713 Once a layerable has accepted the \ref mousePressEvent, it is considered the mouse grabber and
1714 will receive all following calls to \ref mouseMoveEvent or \ref mouseReleaseEvent for this mouse
1715 interaction (a "mouse interaction" in this context ends with the release).
1716
1717 The default implementation does nothing except explicitly ignoring the event with \c
1718 event->ignore().
1719
1720 \see mouseMoveEvent, mouseReleaseEvent, mouseDoubleClickEvent, wheelEvent
1721*/
1723{
1724 Q_UNUSED(details)
1725 event->ignore();
1726}
1727
1728/*!
1729 This event gets called when the user moves the mouse while holding a mouse button, after this
1730 layerable has become the mouse grabber by accepting the preceding \ref mousePressEvent.
1731
1732 The current pixel position of the cursor on the QCustomPlot widget is accessible via \c
1733 event->pos(). The parameter \a startPos indicates the position where the initial \ref
1734 mousePressEvent occurred, that started the mouse interaction.
1735
1736 The default implementation does nothing.
1737
1738 \see mousePressEvent, mouseReleaseEvent, mouseDoubleClickEvent, wheelEvent
1739*/
1741{
1742 Q_UNUSED(startPos)
1743 event->ignore();
1744}
1745
1746/*!
1747 This event gets called when the user releases the mouse button, after this layerable has become
1748 the mouse grabber by accepting the preceding \ref mousePressEvent.
1749
1750 The current pixel position of the cursor on the QCustomPlot widget is accessible via \c
1751 event->pos(). The parameter \a startPos indicates the position where the initial \ref
1752 mousePressEvent occurred, that started the mouse interaction.
1753
1754 The default implementation does nothing.
1755
1756 \see mousePressEvent, mouseMoveEvent, mouseDoubleClickEvent, wheelEvent
1757*/
1759{
1760 Q_UNUSED(startPos)
1761 event->ignore();
1762}
1763
1764/*!
1765 This event gets called when the user presses the mouse button a second time in a double-click,
1766 while the cursor is over the layerable. Whether a cursor is over the layerable is decided by a
1767 preceding call to \ref selectTest.
1768
1769 The \ref mouseDoubleClickEvent is called instead of the second \ref mousePressEvent. So in the
1770 case of a double-click, the event succession is
1771 <i>pressEvent &ndash; releaseEvent &ndash; doubleClickEvent &ndash; releaseEvent</i>.
1772
1773 The current pixel position of the cursor on the QCustomPlot widget is accessible via \c
1774 event->pos(). The parameter \a details contains layerable-specific details about the hit, which
1775 were generated in the previous call to \ref selectTest. For example, One-dimensional plottables
1776 like \ref QCPGraph or \ref QCPBars convey the clicked data point in the \a details parameter, as
1777 \ref QCPDataSelection packed as QVariant. Multi-part objects convey the specific \c
1778 SelectablePart that was hit (e.g. \ref QCPAxis::SelectablePart in the case of axes).
1779
1780 Similarly to \ref mousePressEvent, once a layerable has accepted the \ref mouseDoubleClickEvent,
1781 it is considered the mouse grabber and will receive all following calls to \ref mouseMoveEvent
1782 and \ref mouseReleaseEvent for this mouse interaction (a "mouse interaction" in this context ends
1783 with the release).
1784
1785 The default implementation does nothing except explicitly ignoring the event with \c
1786 event->ignore().
1787
1788 \see mousePressEvent, mouseMoveEvent, mouseReleaseEvent, wheelEvent
1789*/
1791{
1792 Q_UNUSED(details)
1793 event->ignore();
1794}
1795
1796/*!
1797 This event gets called when the user turns the mouse scroll wheel while the cursor is over the
1798 layerable. Whether a cursor is over the layerable is decided by a preceding call to \ref
1799 selectTest.
1800
1801 The current pixel position of the cursor on the QCustomPlot widget is accessible via \c
1802 event->pos().
1803
1804 The \c event->angleDelta() indicates how far the mouse wheel was turned, which is usually +/- 120
1805 for single rotation steps. However, if the mouse wheel is turned rapidly, multiple steps may
1806 accumulate to one event, making the delta larger. On the other hand, if the wheel has very smooth
1807 steps or none at all, the delta may be smaller.
1808
1809 The default implementation does nothing.
1810
1811 \see mousePressEvent, mouseMoveEvent, mouseReleaseEvent, mouseDoubleClickEvent
1812*/
1814{
1815 event->ignore();
1816}
1817/* end of 'src/layer.cpp' */
1818
1819
1820/* including file 'src/axis/range.cpp' */
1821/* modified 2021-03-29T02:30:44, size 12221 */
1822
1823////////////////////////////////////////////////////////////////////////////////////////////////////
1824//////////////////// QCPRange
1825////////////////////////////////////////////////////////////////////////////////////////////////////
1826/*! \class QCPRange
1827 \brief Represents the range an axis is encompassing.
1828
1829 contains a \a lower and \a upper double value and provides convenience input, output and
1830 modification functions.
1831
1832 \see QCPAxis::setRange
1833*/
1834
1835/* start of documentation of inline functions */
1836
1837/*! \fn double QCPRange::size() const
1838
1839 Returns the size of the range, i.e. \a upper-\a lower
1840*/
1841
1842/*! \fn double QCPRange::center() const
1843
1844 Returns the center of the range, i.e. (\a upper+\a lower)*0.5
1845*/
1846
1847/*! \fn void QCPRange::normalize()
1848
1849 Makes sure \a lower is numerically smaller than \a upper. If this is not the case, the values are
1850 swapped.
1851*/
1852
1853/*! \fn bool QCPRange::contains(double value) const
1854
1855 Returns true when \a value lies within or exactly on the borders of the range.
1856*/
1857
1858/*! \fn QCPRange &QCPRange::operator+=(const double& value)
1859
1860 Adds \a value to both boundaries of the range.
1861*/
1862
1863/*! \fn QCPRange &QCPRange::operator-=(const double& value)
1864
1865 Subtracts \a value from both boundaries of the range.
1866*/
1867
1868/*! \fn QCPRange &QCPRange::operator*=(const double& value)
1869
1870 Multiplies both boundaries of the range by \a value.
1871*/
1872
1873/*! \fn QCPRange &QCPRange::operator/=(const double& value)
1874
1875 Divides both boundaries of the range by \a value.
1876*/
1877
1878/* end of documentation of inline functions */
1879
1880/*!
1881 Minimum range size (\a upper - \a lower) the range changing functions will accept. Smaller
1882 intervals would cause errors due to the 11-bit exponent of double precision numbers,
1883 corresponding to a minimum magnitude of roughly 1e-308.
1884
1885 \warning Do not use this constant to indicate "arbitrarily small" values in plotting logic (as
1886 values that will appear in the plot)! It is intended only as a bound to compare against, e.g. to
1887 prevent axis ranges from obtaining underflowing ranges.
1888
1889 \see validRange, maxRange
1890*/
1891const double QCPRange::minRange = 1e-280;
1892
1893/*!
1894 Maximum values (negative and positive) the range will accept in range-changing functions.
1895 Larger absolute values would cause errors due to the 11-bit exponent of double precision numbers,
1896 corresponding to a maximum magnitude of roughly 1e308.
1897
1898 \warning Do not use this constant to indicate "arbitrarily large" values in plotting logic (as
1899 values that will appear in the plot)! It is intended only as a bound to compare against, e.g. to
1900 prevent axis ranges from obtaining overflowing ranges.
1901
1902 \see validRange, minRange
1903*/
1904const double QCPRange::maxRange = 1e250;
1905
1906/*!
1907 Constructs a range with \a lower and \a upper set to zero.
1908*/
1910 lower(0),
1911 upper(0)
1912{
1913}
1914
1915/*! \overload
1916
1917 Constructs a range with the specified \a lower and \a upper values.
1918
1919 The resulting range will be normalized (see \ref normalize), so if \a lower is not numerically
1920 smaller than \a upper, they will be swapped.
1921*/
1922QCPRange::QCPRange(double lower, double upper) :
1923 lower(lower),
1924 upper(upper)
1925{
1926 normalize();
1927}
1928
1929/*! \overload
1930
1931 Expands this range such that \a otherRange is contained in the new range. It is assumed that both
1932 this range and \a otherRange are normalized (see \ref normalize).
1933
1934 If this range contains NaN as lower or upper bound, it will be replaced by the respective bound
1935 of \a otherRange.
1936
1937 If \a otherRange is already inside the current range, this function does nothing.
1938
1939 \see expanded
1940*/
1941void QCPRange::expand(const QCPRange &otherRange)
1942{
1943 if (lower > otherRange.lower || qIsNaN(lower))
1944 lower = otherRange.lower;
1945 if (upper < otherRange.upper || qIsNaN(upper))
1946 upper = otherRange.upper;
1947}
1948
1949/*! \overload
1950
1951 Expands this range such that \a includeCoord is contained in the new range. It is assumed that
1952 this range is normalized (see \ref normalize).
1953
1954 If this range contains NaN as lower or upper bound, the respective bound will be set to \a
1955 includeCoord.
1956
1957 If \a includeCoord is already inside the current range, this function does nothing.
1958
1959 \see expand
1960*/
1961void QCPRange::expand(double includeCoord)
1962{
1963 if (lower > includeCoord || qIsNaN(lower))
1964 lower = includeCoord;
1965 if (upper < includeCoord || qIsNaN(upper))
1966 upper = includeCoord;
1967}
1968
1969
1970/*! \overload
1971
1972 Returns an expanded range that contains this and \a otherRange. It is assumed that both this
1973 range and \a otherRange are normalized (see \ref normalize).
1974
1975 If this range contains NaN as lower or upper bound, the returned range's bound will be taken from
1976 \a otherRange.
1977
1978 \see expand
1979*/
1980QCPRange QCPRange::expanded(const QCPRange &otherRange) const
1981{
1982 QCPRange result = *this;
1983 result.expand(otherRange);
1984 return result;
1985}
1986
1987/*! \overload
1988
1989 Returns an expanded range that includes the specified \a includeCoord. It is assumed that this
1990 range is normalized (see \ref normalize).
1991
1992 If this range contains NaN as lower or upper bound, the returned range's bound will be set to \a
1993 includeCoord.
1994
1995 \see expand
1996*/
1997QCPRange QCPRange::expanded(double includeCoord) const
1998{
1999 QCPRange result = *this;
2000 result.expand(includeCoord);
2001 return result;
2002}
2003
2004/*!
2005 Returns this range, possibly modified to not exceed the bounds provided as \a lowerBound and \a
2006 upperBound. If possible, the size of the current range is preserved in the process.
2007
2008 If the range shall only be bounded at the lower side, you can set \a upperBound to \ref
2009 QCPRange::maxRange. If it shall only be bounded at the upper side, set \a lowerBound to -\ref
2010 QCPRange::maxRange.
2011*/
2012QCPRange QCPRange::bounded(double lowerBound, double upperBound) const
2013{
2014 if (lowerBound > upperBound)
2015 qSwap(lowerBound, upperBound);
2016
2017 QCPRange result(lower, upper);
2018 if (result.lower < lowerBound)
2019 {
2020 result.lower = lowerBound;
2021 result.upper = lowerBound + size();
2022 if (result.upper > upperBound || qFuzzyCompare(size(), upperBound-lowerBound))
2023 result.upper = upperBound;
2024 } else if (result.upper > upperBound)
2025 {
2026 result.upper = upperBound;
2027 result.lower = upperBound - size();
2028 if (result.lower < lowerBound || qFuzzyCompare(size(), upperBound-lowerBound))
2029 result.lower = lowerBound;
2030 }
2031
2032 return result;
2033}
2034
2035/*!
2036 Returns a sanitized version of the range. Sanitized means for logarithmic scales, that
2037 the range won't span the positive and negative sign domain, i.e. contain zero. Further
2038 \a lower will always be numerically smaller (or equal) to \a upper.
2039
2040 If the original range does span positive and negative sign domains or contains zero,
2041 the returned range will try to approximate the original range as good as possible.
2042 If the positive interval of the original range is wider than the negative interval, the
2043 returned range will only contain the positive interval, with lower bound set to \a rangeFac or
2044 \a rangeFac *\a upper, whichever is closer to zero. Same procedure is used if the negative interval
2045 is wider than the positive interval, this time by changing the \a upper bound.
2046*/
2048{
2049 double rangeFac = 1e-3;
2050 QCPRange sanitizedRange(lower, upper);
2051 sanitizedRange.normalize();
2052 // can't have range spanning negative and positive values in log plot, so change range to fix it
2053 //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1))
2054 if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0)
2055 {
2056 // case lower is 0
2057 if (rangeFac < sanitizedRange.upper*rangeFac)
2058 sanitizedRange.lower = rangeFac;
2059 else
2060 sanitizedRange.lower = sanitizedRange.upper*rangeFac;
2061 } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
2062 else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0)
2063 {
2064 // case upper is 0
2065 if (-rangeFac > sanitizedRange.lower*rangeFac)
2066 sanitizedRange.upper = -rangeFac;
2067 else
2068 sanitizedRange.upper = sanitizedRange.lower*rangeFac;
2069 } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0)
2070 {
2071 // find out whether negative or positive interval is wider to decide which sign domain will be chosen
2072 if (-sanitizedRange.lower > sanitizedRange.upper)
2073 {
2074 // negative is wider, do same as in case upper is 0
2075 if (-rangeFac > sanitizedRange.lower*rangeFac)
2076 sanitizedRange.upper = -rangeFac;
2077 else
2078 sanitizedRange.upper = sanitizedRange.lower*rangeFac;
2079 } else
2080 {
2081 // positive is wider, do same as in case lower is 0
2082 if (rangeFac < sanitizedRange.upper*rangeFac)
2083 sanitizedRange.lower = rangeFac;
2084 else
2085 sanitizedRange.lower = sanitizedRange.upper*rangeFac;
2086 }
2087 }
2088 // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper<lower
2089 return sanitizedRange;
2090}
2091
2092/*!
2093 Returns a sanitized version of the range. Sanitized means for linear scales, that
2094 \a lower will always be numerically smaller (or equal) to \a upper.
2095*/
2097{
2098 QCPRange sanitizedRange(lower, upper);
2099 sanitizedRange.normalize();
2100 return sanitizedRange;
2101}
2102
2103/*!
2104 Checks, whether the specified range is within valid bounds, which are defined
2105 as QCPRange::maxRange and QCPRange::minRange.
2106 A valid range means:
2107 \li range bounds within -maxRange and maxRange
2108 \li range size above minRange
2109 \li range size below maxRange
2110*/
2111bool QCPRange::validRange(double lower, double upper)
2112{
2113 return (lower > -maxRange &&
2114 upper < maxRange &&
2115 qAbs(lower-upper) > minRange &&
2116 qAbs(lower-upper) < maxRange &&
2117 !(lower > 0 && qIsInf(upper/lower)) &&
2118 !(upper < 0 && qIsInf(lower/upper)));
2119}
2120
2121/*!
2122 \overload
2123 Checks, whether the specified range is within valid bounds, which are defined
2124 as QCPRange::maxRange and QCPRange::minRange.
2125 A valid range means:
2126 \li range bounds within -maxRange and maxRange
2127 \li range size above minRange
2128 \li range size below maxRange
2129*/
2131{
2132 return (range.lower > -maxRange &&
2133 range.upper < maxRange &&
2134 qAbs(range.lower-range.upper) > minRange &&
2135 qAbs(range.lower-range.upper) < maxRange &&
2136 !(range.lower > 0 && qIsInf(range.upper/range.lower)) &&
2137 !(range.upper < 0 && qIsInf(range.lower/range.upper)));
2138}
2139/* end of 'src/axis/range.cpp' */
2140
2141
2142/* including file 'src/selection.cpp' */
2143/* modified 2021-03-29T02:30:44, size 21837 */
2144
2145////////////////////////////////////////////////////////////////////////////////////////////////////
2146//////////////////// QCPDataRange
2147////////////////////////////////////////////////////////////////////////////////////////////////////
2148
2149/*! \class QCPDataRange
2150 \brief Describes a data range given by begin and end index
2151
2152 QCPDataRange holds two integers describing the begin (\ref setBegin) and end (\ref setEnd) index
2153 of a contiguous set of data points. The \a end index corresponds to the data point just after the
2154 last data point of the data range, like in standard iterators.
2155
2156 Data Ranges are not bound to a certain plottable, thus they can be freely exchanged, created and
2157 modified. If a non-contiguous data set shall be described, the class \ref QCPDataSelection is
2158 used, which holds and manages multiple instances of \ref QCPDataRange. In most situations, \ref
2159 QCPDataSelection is thus used.
2160
2161 Both \ref QCPDataRange and \ref QCPDataSelection offer convenience methods to work with them,
2162 e.g. \ref bounded, \ref expanded, \ref intersects, \ref intersection, \ref adjusted, \ref
2163 contains. Further, addition and subtraction operators (defined in \ref QCPDataSelection) can be
2164 used to join/subtract data ranges and data selections (or mixtures), to retrieve a corresponding
2165 \ref QCPDataSelection.
2166
2167 %QCustomPlot's \ref dataselection "data selection mechanism" is based on \ref QCPDataSelection and
2168 QCPDataRange.
2169
2170 \note Do not confuse \ref QCPDataRange with \ref QCPRange. A \ref QCPRange describes an interval
2171 in floating point plot coordinates, e.g. the current axis range.
2172*/
2173
2174/* start documentation of inline functions */
2175
2176/*! \fn int QCPDataRange::size() const
2177
2178 Returns the number of data points described by this data range. This is equal to the end index
2179 minus the begin index.
2180
2181 \see length
2182*/
2183
2184/*! \fn int QCPDataRange::length() const
2185
2186 Returns the number of data points described by this data range. Equivalent to \ref size.
2187*/
2188
2189/*! \fn void QCPDataRange::setBegin(int begin)
2190
2191 Sets the begin of this data range. The \a begin index points to the first data point that is part
2192 of the data range.
2193
2194 No checks or corrections are made to ensure the resulting range is valid (\ref isValid).
2195
2196 \see setEnd
2197*/
2198
2199/*! \fn void QCPDataRange::setEnd(int end)
2200
2201 Sets the end of this data range. The \a end index points to the data point just after the last
2202 data point that is part of the data range.
2203
2204 No checks or corrections are made to ensure the resulting range is valid (\ref isValid).
2205
2206 \see setBegin
2207*/
2208
2209/*! \fn bool QCPDataRange::isValid() const
2210
2211 Returns whether this range is valid. A valid range has a begin index greater or equal to 0, and
2212 an end index greater or equal to the begin index.
2213
2214 \note Invalid ranges should be avoided and are never the result of any of QCustomPlot's methods
2215 (unless they are themselves fed with invalid ranges). Do not pass invalid ranges to QCustomPlot's
2216 methods. The invalid range is not inherently prevented in QCPDataRange, to allow temporary
2217 invalid begin/end values while manipulating the range. An invalid range is not necessarily empty
2218 (\ref isEmpty), since its \ref length can be negative and thus non-zero.
2219*/
2220
2221/*! \fn bool QCPDataRange::isEmpty() const
2222
2223 Returns whether this range is empty, i.e. whether its begin index equals its end index.
2224
2225 \see size, length
2226*/
2227
2228/*! \fn QCPDataRange QCPDataRange::adjusted(int changeBegin, int changeEnd) const
2229
2230 Returns a data range where \a changeBegin and \a changeEnd were added to the begin and end
2231 indices, respectively.
2232*/
2233
2234/* end documentation of inline functions */
2235
2236/*!
2237 Creates an empty QCPDataRange, with begin and end set to 0.
2238*/
2240 mBegin(0),
2241 mEnd(0)
2242{
2243}
2244
2245/*!
2246 Creates a QCPDataRange, initialized with the specified \a begin and \a end.
2247
2248 No checks or corrections are made to ensure the resulting range is valid (\ref isValid).
2249*/
2250QCPDataRange::QCPDataRange(int begin, int end) :
2251 mBegin(begin),
2252 mEnd(end)
2253{
2254}
2255
2256/*!
2257 Returns a data range that matches this data range, except that parts exceeding \a other are
2258 excluded.
2259
2260 This method is very similar to \ref intersection, with one distinction: If this range and the \a
2261 other range share no intersection, the returned data range will be empty with begin and end set
2262 to the respective boundary side of \a other, at which this range is residing. (\ref intersection
2263 would just return a range with begin and end set to 0.)
2264*/
2266{
2267 QCPDataRange result(intersection(other));
2268 if (result.isEmpty()) // no intersection, preserve respective bounding side of otherRange as both begin and end of return value
2269 {
2270 if (mEnd <= other.mBegin)
2271 result = QCPDataRange(other.mBegin, other.mBegin);
2272 else
2273 result = QCPDataRange(other.mEnd, other.mEnd);
2274 }
2275 return result;
2276}
2277
2278/*!
2279 Returns a data range that contains both this data range as well as \a other.
2280*/
2282{
2283 return {qMin(mBegin, other.mBegin), qMax(mEnd, other.mEnd)};
2284}
2285
2286/*!
2287 Returns the data range which is contained in both this data range and \a other.
2288
2289 This method is very similar to \ref bounded, with one distinction: If this range and the \a other
2290 range share no intersection, the returned data range will be empty with begin and end set to 0.
2291 (\ref bounded would return a range with begin and end set to one of the boundaries of \a other,
2292 depending on which side this range is on.)
2293
2294 \see QCPDataSelection::intersection
2295*/
2297{
2298 QCPDataRange result(qMax(mBegin, other.mBegin), qMin(mEnd, other.mEnd));
2299 if (result.isValid())
2300 return result;
2301 else
2302 return {};
2303}
2304
2305/*!
2306 Returns whether this data range and \a other share common data points.
2307
2308 \see intersection, contains
2309*/
2311{
2312 return !( (mBegin > other.mBegin && mBegin >= other.mEnd) ||
2313 (mEnd <= other.mBegin && mEnd < other.mEnd) );
2314}
2315
2316/*!
2317 Returns whether all data points of \a other are also contained inside this data range.
2318
2319 \see intersects
2320*/
2321bool QCPDataRange::contains(const QCPDataRange &other) const
2322{
2323 return mBegin <= other.mBegin && mEnd >= other.mEnd;
2324}
2325
2326
2327
2328////////////////////////////////////////////////////////////////////////////////////////////////////
2329//////////////////// QCPDataSelection
2330////////////////////////////////////////////////////////////////////////////////////////////////////
2331
2332/*! \class QCPDataSelection
2333 \brief Describes a data set by holding multiple QCPDataRange instances
2334
2335 QCPDataSelection manages multiple instances of QCPDataRange in order to represent any (possibly
2336 disjoint) set of data selection.
2337
2338 The data selection can be modified with addition and subtraction operators which take
2339 QCPDataSelection and QCPDataRange instances, as well as methods such as \ref addDataRange and
2340 \ref clear. Read access is provided by \ref dataRange, \ref dataRanges, \ref dataRangeCount, etc.
2341
2342 The method \ref simplify is used to join directly adjacent or even overlapping QCPDataRange
2343 instances. QCPDataSelection automatically simplifies when using the addition/subtraction
2344 operators. The only case when \ref simplify is left to the user, is when calling \ref
2345 addDataRange, with the parameter \a simplify explicitly set to false. This is useful if many data
2346 ranges will be added to the selection successively and the overhead for simplifying after each
2347 iteration shall be avoided. In this case, you should make sure to call \ref simplify after
2348 completing the operation.
2349
2350 Use \ref enforceType to bring the data selection into a state complying with the constraints for
2351 selections defined in \ref QCP::SelectionType.
2352
2353 %QCustomPlot's \ref dataselection "data selection mechanism" is based on QCPDataSelection and
2354 QCPDataRange.
2355
2356 \section qcpdataselection-iterating Iterating over a data selection
2357
2358 As an example, the following code snippet calculates the average value of a graph's data
2359 \ref QCPAbstractPlottable::selection "selection":
2360
2361 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpdataselection-iterating-1
2362
2363*/
2364
2365/* start documentation of inline functions */
2366
2367/*! \fn int QCPDataSelection::dataRangeCount() const
2368
2369 Returns the number of ranges that make up the data selection. The ranges can be accessed by \ref
2370 dataRange via their index.
2371
2372 \see dataRange, dataPointCount
2373*/
2374
2375/*! \fn QList<QCPDataRange> QCPDataSelection::dataRanges() const
2376
2377 Returns all data ranges that make up the data selection. If the data selection is simplified (the
2378 usual state of the selection, see \ref simplify), the ranges are sorted by ascending data point
2379 index.
2380
2381 \see dataRange
2382*/
2383
2384/*! \fn bool QCPDataSelection::isEmpty() const
2385
2386 Returns true if there are no data ranges, and thus no data points, in this QCPDataSelection
2387 instance.
2388
2389 \see dataRangeCount
2390*/
2391
2392/* end documentation of inline functions */
2393
2394/*!
2395 Creates an empty QCPDataSelection.
2396*/
2400
2401/*!
2402 Creates a QCPDataSelection containing the provided \a range.
2403*/
2405{
2406 mDataRanges.append(range);
2407}
2408
2409/*!
2410 Returns true if this selection is identical (contains the same data ranges with the same begin
2411 and end indices) to \a other.
2412
2413 Note that both data selections must be in simplified state (the usual state of the selection, see
2414 \ref simplify) for this operator to return correct results.
2415*/
2417{
2418 if (mDataRanges.size() != other.mDataRanges.size())
2419 return false;
2420 for (int i=0; i<mDataRanges.size(); ++i)
2421 {
2422 if (mDataRanges.at(i) != other.mDataRanges.at(i))
2423 return false;
2424 }
2425 return true;
2426}
2427
2428/*!
2429 Adds the data selection of \a other to this data selection, and then simplifies this data
2430 selection (see \ref simplify).
2431*/
2433{
2434 mDataRanges << other.mDataRanges;
2435 simplify();
2436 return *this;
2437}
2438
2439/*!
2440 Adds the data range \a other to this data selection, and then simplifies this data selection (see
2441 \ref simplify).
2442*/
2444{
2445 addDataRange(other);
2446 return *this;
2447}
2448
2449/*!
2450 Removes all data point indices that are described by \a other from this data selection.
2451*/
2453{
2454 for (int i=0; i<other.dataRangeCount(); ++i)
2455 *this -= other.dataRange(i);
2456
2457 return *this;
2458}
2459
2460/*!
2461 Removes all data point indices that are described by \a other from this data selection.
2462*/
2464{
2465 if (other.isEmpty() || isEmpty())
2466 return *this;
2467
2468 simplify();
2469 int i=0;
2470 while (i < mDataRanges.size())
2471 {
2472 const int thisBegin = mDataRanges.at(i).begin();
2473 const int thisEnd = mDataRanges.at(i).end();
2474 if (thisBegin >= other.end())
2475 break; // since data ranges are sorted after the simplify() call, no ranges which contain other will come after this
2476
2477 if (thisEnd > other.begin()) // ranges which don't fulfill this are entirely before other and can be ignored
2478 {
2479 if (thisBegin >= other.begin()) // range leading segment is encompassed
2480 {
2481 if (thisEnd <= other.end()) // range fully encompassed, remove completely
2482 {
2483 mDataRanges.removeAt(i);
2484 continue;
2485 } else // only leading segment is encompassed, trim accordingly
2486 mDataRanges[i].setBegin(other.end());
2487 } else // leading segment is not encompassed
2488 {
2489 if (thisEnd <= other.end()) // only trailing segment is encompassed, trim accordingly
2490 {
2491 mDataRanges[i].setEnd(other.begin());
2492 } else // other lies inside this range, so split range
2493 {
2494 mDataRanges[i].setEnd(other.begin());
2495 mDataRanges.insert(i+1, QCPDataRange(other.end(), thisEnd));
2496 break; // since data ranges are sorted (and don't overlap) after simplify() call, we're done here
2497 }
2498 }
2499 }
2500 ++i;
2501 }
2502
2503 return *this;
2504}
2505
2506/*!
2507 Returns the total number of data points contained in all data ranges that make up this data
2508 selection.
2509*/
2511{
2512 int result = 0;
2513 foreach (QCPDataRange dataRange, mDataRanges)
2514 result += dataRange.length();
2515 return result;
2516}
2517
2518/*!
2519 Returns the data range with the specified \a index.
2520
2521 If the data selection is simplified (the usual state of the selection, see \ref simplify), the
2522 ranges are sorted by ascending data point index.
2523
2524 \see dataRangeCount
2525*/
2527{
2528 if (index >= 0 && index < mDataRanges.size())
2529 {
2530 return mDataRanges.at(index);
2531 } else
2532 {
2533 qDebug() << Q_FUNC_INFO << "index out of range:" << index;
2534 return {};
2535 }
2536}
2537
2538/*!
2539 Returns a \ref QCPDataRange which spans the entire data selection, including possible
2540 intermediate segments which are not part of the original data selection.
2541*/
2543{
2544 if (isEmpty())
2545 return {};
2546 else
2547 return {mDataRanges.first().begin(), mDataRanges.last().end()};
2548}
2549
2550/*!
2551 Adds the given \a dataRange to this data selection. This is equivalent to the += operator but
2552 allows disabling immediate simplification by setting \a simplify to false. This can improve
2553 performance if adding a very large amount of data ranges successively. In this case, make sure to
2554 call \ref simplify manually, after the operation.
2555*/
2556void QCPDataSelection::addDataRange(const QCPDataRange &dataRange, bool simplify)
2557{
2558 mDataRanges.append(dataRange);
2559 if (simplify)
2560 this->simplify();
2561}
2562
2563/*!
2564 Removes all data ranges. The data selection then contains no data points.
2565
2566 \ref isEmpty
2567*/
2569{
2570 mDataRanges.clear();
2571}
2572
2573/*!
2574 Sorts all data ranges by range begin index in ascending order, and then joins directly adjacent
2575 or overlapping ranges. This can reduce the number of individual data ranges in the selection, and
2576 prevents possible double-counting when iterating over the data points held by the data ranges.
2577
2578 This method is automatically called when using the addition/subtraction operators. The only case
2579 when \ref simplify is left to the user, is when calling \ref addDataRange, with the parameter \a
2580 simplify explicitly set to false.
2581*/
2583{
2584 // remove any empty ranges:
2585 for (int i=mDataRanges.size()-1; i>=0; --i)
2586 {
2587 if (mDataRanges.at(i).isEmpty())
2588 mDataRanges.removeAt(i);
2589 }
2590 if (mDataRanges.isEmpty())
2591 return;
2592
2593 // sort ranges by starting value, ascending:
2594 std::sort(mDataRanges.begin(), mDataRanges.end(), lessThanDataRangeBegin);
2595
2596 // join overlapping/contiguous ranges:
2597 int i = 1;
2598 while (i < mDataRanges.size())
2599 {
2600 if (mDataRanges.at(i-1).end() >= mDataRanges.at(i).begin()) // range i overlaps/joins with i-1, so expand range i-1 appropriately and remove range i from list
2601 {
2602 mDataRanges[i-1].setEnd(qMax(mDataRanges.at(i-1).end(), mDataRanges.at(i).end()));
2603 mDataRanges.removeAt(i);
2604 } else
2605 ++i;
2606 }
2607}
2608
2609/*!
2610 Makes sure this data selection conforms to the specified \a type selection type. Before the type
2611 is enforced, \ref simplify is called.
2612
2613 Depending on \a type, enforcing means adding new data points that were previously not part of the
2614 selection, or removing data points from the selection. If the current selection already conforms
2615 to \a type, the data selection is not changed.
2616
2617 \see QCP::SelectionType
2618*/
2620{
2621 simplify();
2622 switch (type)
2623 {
2624 case QCP::stNone:
2625 {
2626 mDataRanges.clear();
2627 break;
2628 }
2629 case QCP::stWhole:
2630 {
2631 // whole selection isn't defined by data range, so don't change anything (is handled in plottable methods)
2632 break;
2633 }
2634 case QCP::stSingleData:
2635 {
2636 // reduce all data ranges to the single first data point:
2637 if (!mDataRanges.isEmpty())
2638 {
2639 if (mDataRanges.size() > 1)
2640 mDataRanges = QList<QCPDataRange>() << mDataRanges.first();
2641 if (mDataRanges.first().length() > 1)
2642 mDataRanges.first().setEnd(mDataRanges.first().begin()+1);
2643 }
2644 break;
2645 }
2646 case QCP::stDataRange:
2647 {
2648 if (!isEmpty())
2649 mDataRanges = QList<QCPDataRange>() << span();
2650 break;
2651 }
2653 {
2654 // this is the selection type that allows all concievable combinations of ranges, so do nothing
2655 break;
2656 }
2657 }
2658}
2659
2660/*!
2661 Returns true if the data selection \a other is contained entirely in this data selection, i.e.
2662 all data point indices that are in \a other are also in this data selection.
2663
2664 \see QCPDataRange::contains
2665*/
2667{
2668 if (other.isEmpty()) return false;
2669
2670 int otherIndex = 0;
2671 int thisIndex = 0;
2672 while (thisIndex < mDataRanges.size() && otherIndex < other.mDataRanges.size())
2673 {
2674 if (mDataRanges.at(thisIndex).contains(other.mDataRanges.at(otherIndex)))
2675 ++otherIndex;
2676 else
2677 ++thisIndex;
2678 }
2679 return thisIndex < mDataRanges.size(); // if thisIndex ran all the way to the end to find a containing range for the current otherIndex, other is not contained in this
2680}
2681
2682/*!
2683 Returns a data selection containing the points which are both in this data selection and in the
2684 data range \a other.
2685
2686 A common use case is to limit an unknown data selection to the valid range of a data container,
2687 using \ref QCPDataContainer::dataRange as \a other. One can then safely iterate over the returned
2688 data selection without exceeding the data container's bounds.
2689*/
2691{
2692 QCPDataSelection result;
2693 foreach (QCPDataRange dataRange, mDataRanges)
2694 result.addDataRange(dataRange.intersection(other), false);
2695 result.simplify();
2696 return result;
2697}
2698
2699/*!
2700 Returns a data selection containing the points which are both in this data selection and in the
2701 data selection \a other.
2702*/
2704{
2705 QCPDataSelection result;
2706 for (int i=0; i<other.dataRangeCount(); ++i)
2707 result += intersection(other.dataRange(i));
2708 result.simplify();
2709 return result;
2710}
2711
2712/*!
2713 Returns a data selection which is the exact inverse of this data selection, with \a outerRange
2714 defining the base range on which to invert. If \a outerRange is smaller than the \ref span of
2715 this data selection, it is expanded accordingly.
2716
2717 For example, this method can be used to retrieve all unselected segments by setting \a outerRange
2718 to the full data range of the plottable, and calling this method on a data selection holding the
2719 selected segments.
2720*/
2722{
2723 if (isEmpty())
2724 return QCPDataSelection(outerRange);
2725 QCPDataRange fullRange = outerRange.expanded(span());
2726
2727 QCPDataSelection result;
2728 // first unselected segment:
2729 if (mDataRanges.first().begin() != fullRange.begin())
2730 result.addDataRange(QCPDataRange(fullRange.begin(), mDataRanges.first().begin()), false);
2731 // intermediate unselected segments:
2732 for (int i=1; i<mDataRanges.size(); ++i)
2733 result.addDataRange(QCPDataRange(mDataRanges.at(i-1).end(), mDataRanges.at(i).begin()), false);
2734 // last unselected segment:
2735 if (mDataRanges.last().end() != fullRange.end())
2736 result.addDataRange(QCPDataRange(mDataRanges.last().end(), fullRange.end()), false);
2737 result.simplify();
2738 return result;
2739}
2740/* end of 'src/selection.cpp' */
2741
2742
2743/* including file 'src/selectionrect.cpp' */
2744/* modified 2021-03-29T02:30:44, size 9215 */
2745
2746////////////////////////////////////////////////////////////////////////////////////////////////////
2747//////////////////// QCPSelectionRect
2748////////////////////////////////////////////////////////////////////////////////////////////////////
2749
2750/*! \class QCPSelectionRect
2751 \brief Provides rect/rubber-band data selection and range zoom interaction
2752
2753 QCPSelectionRect is used by QCustomPlot when the \ref QCustomPlot::setSelectionRectMode is not
2754 \ref QCP::srmNone. When the user drags the mouse across the plot, the current selection rect
2755 instance (\ref QCustomPlot::setSelectionRect) is forwarded these events and makes sure an
2756 according rect shape is drawn. At the begin, during, and after completion of the interaction, it
2757 emits the corresponding signals \ref started, \ref changed, \ref canceled, and \ref accepted.
2758
2759 The QCustomPlot instance connects own slots to the current selection rect instance, in order to
2760 react to an accepted selection rect interaction accordingly.
2761
2762 \ref isActive can be used to check whether the selection rect is currently active. An ongoing
2763 selection interaction can be cancelled programmatically via calling \ref cancel at any time.
2764
2765 The appearance of the selection rect can be controlled via \ref setPen and \ref setBrush.
2766
2767 If you wish to provide custom behaviour, e.g. a different visual representation of the selection
2768 rect (\ref QCPSelectionRect::draw), you can subclass QCPSelectionRect and pass an instance of
2769 your subclass to \ref QCustomPlot::setSelectionRect.
2770*/
2771
2772/* start of documentation of inline functions */
2773
2774/*! \fn bool QCPSelectionRect::isActive() const
2775
2776 Returns true if there is currently a selection going on, i.e. the user has started dragging a
2777 selection rect, but hasn't released the mouse button yet.
2778
2779 \see cancel
2780*/
2781
2782/* end of documentation of inline functions */
2783/* start documentation of signals */
2784
2785/*! \fn void QCPSelectionRect::started(QMouseEvent *event);
2786
2787 This signal is emitted when a selection rect interaction was initiated, i.e. the user just
2788 started dragging the selection rect with the mouse.
2789*/
2790
2791/*! \fn void QCPSelectionRect::changed(const QRect &rect, QMouseEvent *event);
2792
2793 This signal is emitted while the selection rect interaction is ongoing and the \a rect has
2794 changed its size due to the user moving the mouse.
2795
2796 Note that \a rect may have a negative width or height, if the selection is being dragged to the
2797 upper or left side of the selection rect origin.
2798*/
2799
2800/*! \fn void QCPSelectionRect::canceled(const QRect &rect, QInputEvent *event);
2801
2802 This signal is emitted when the selection interaction was cancelled. Note that \a event is \c
2803 nullptr if the selection interaction was cancelled programmatically, by a call to \ref cancel.
2804
2805 The user may cancel the selection interaction by pressing the escape key. In this case, \a event
2806 holds the respective input event.
2807
2808 Note that \a rect may have a negative width or height, if the selection is being dragged to the
2809 upper or left side of the selection rect origin.
2810*/
2811
2812/*! \fn void QCPSelectionRect::accepted(const QRect &rect, QMouseEvent *event);
2813
2814 This signal is emitted when the selection interaction was completed by the user releasing the
2815 mouse button.
2816
2817 Note that \a rect may have a negative width or height, if the selection is being dragged to the
2818 upper or left side of the selection rect origin.
2819*/
2820
2821/* end documentation of signals */
2822
2823/*!
2824 Creates a new QCPSelectionRect instance. To make QCustomPlot use the selection rect instance,
2825 pass it to \ref QCustomPlot::setSelectionRect. \a parentPlot should be set to the same
2826 QCustomPlot widget.
2827*/
2829 QCPLayerable(parentPlot),
2830 mPen(QBrush(Qt::gray), 0, Qt::DashLine),
2831 mBrush(Qt::NoBrush),
2832 mActive(false)
2833{
2834}
2835
2836QCPSelectionRect::~QCPSelectionRect()
2837{
2838 cancel();
2839}
2840
2841/*!
2842 A convenience function which returns the coordinate range of the provided \a axis, that this
2843 selection rect currently encompasses.
2844*/
2846{
2847 if (axis)
2848 {
2849 if (axis->orientation() == Qt::Horizontal)
2850 return {axis->pixelToCoord(mRect.left()), axis->pixelToCoord(mRect.left()+mRect.width())};
2851 else
2852 return {axis->pixelToCoord(mRect.top()+mRect.height()), axis->pixelToCoord(mRect.top())};
2853 } else
2854 {
2855 qDebug() << Q_FUNC_INFO << "called with axis zero";
2856 return {};
2857 }
2858}
2859
2860/*!
2861 Sets the pen that will be used to draw the selection rect outline.
2862
2863 \see setBrush
2864*/
2866{
2867 mPen = pen;
2868}
2869
2870/*!
2871 Sets the brush that will be used to fill the selection rect. By default the selection rect is not
2872 filled, i.e. \a brush is <tt>Qt::NoBrush</tt>.
2873
2874 \see setPen
2875*/
2877{
2878 mBrush = brush;
2879}
2880
2881/*!
2882 If there is currently a selection interaction going on (\ref isActive), the interaction is
2883 canceled. The selection rect will emit the \ref canceled signal.
2884*/
2886{
2887 if (mActive)
2888 {
2889 mActive = false;
2890 emit canceled(mRect, nullptr);
2891 }
2892}
2893
2894/*! \internal
2895
2896 This method is called by QCustomPlot to indicate that a selection rect interaction was initiated.
2897 The default implementation sets the selection rect to active, initializes the selection rect
2898 geometry and emits the \ref started signal.
2899*/
2901{
2902 mActive = true;
2903 mRect = QRect(event->pos(), event->pos());
2904 emit started(event);
2905}
2906
2907/*! \internal
2908
2909 This method is called by QCustomPlot to indicate that an ongoing selection rect interaction needs
2910 to update its geometry. The default implementation updates the rect and emits the \ref changed
2911 signal.
2912*/
2914{
2915 mRect.setBottomRight(event->pos());
2916 emit changed(mRect, event);
2917 layer()->replot();
2918}
2919
2920/*! \internal
2921
2922 This method is called by QCustomPlot to indicate that an ongoing selection rect interaction has
2923 finished by the user releasing the mouse button. The default implementation deactivates the
2924 selection rect and emits the \ref accepted signal.
2925*/
2927{
2928 mRect.setBottomRight(event->pos());
2929 mActive = false;
2930 emit accepted(mRect, event);
2931}
2932
2933/*! \internal
2934
2935 This method is called by QCustomPlot when a key has been pressed by the user while the selection
2936 rect interaction is active. The default implementation allows to \ref cancel the interaction by
2937 hitting the escape key.
2938*/
2940{
2941 if (event->key() == Qt::Key_Escape && mActive)
2942 {
2943 mActive = false;
2944 emit canceled(mRect, event);
2945 }
2946}
2947
2948/* inherits documentation from base class */
2950{
2951 applyAntialiasingHint(painter, mAntialiased, QCP::aeOther);
2952}
2953
2954/*! \internal
2955
2956 If the selection rect is active (\ref isActive), draws the selection rect defined by \a mRect.
2957
2958 \seebaseclassmethod
2959*/
2961{
2962 if (mActive)
2963 {
2964 painter->setPen(mPen);
2965 painter->setBrush(mBrush);
2966 painter->drawRect(mRect);
2967 }
2968}
2969/* end of 'src/selectionrect.cpp' */
2970
2971
2972/* including file 'src/layout.cpp' */
2973/* modified 2021-03-29T02:30:44, size 78863 */
2974
2975////////////////////////////////////////////////////////////////////////////////////////////////////
2976//////////////////// QCPMarginGroup
2977////////////////////////////////////////////////////////////////////////////////////////////////////
2978
2979/*! \class QCPMarginGroup
2980 \brief A margin group allows synchronization of margin sides if working with multiple layout elements.
2981
2982 QCPMarginGroup allows you to tie a margin side of two or more layout elements together, such that
2983 they will all have the same size, based on the largest required margin in the group.
2984
2985 \n
2986 \image html QCPMarginGroup.png "Demonstration of QCPMarginGroup"
2987 \n
2988
2989 In certain situations it is desirable that margins at specific sides are synchronized across
2990 layout elements. For example, if one QCPAxisRect is below another one in a grid layout, it will
2991 provide a cleaner look to the user if the left and right margins of the two axis rects are of the
2992 same size. The left axis of the top axis rect will then be at the same horizontal position as the
2993 left axis of the lower axis rect, making them appear aligned. The same applies for the right
2994 axes. This is what QCPMarginGroup makes possible.
2995
2996 To add/remove a specific side of a layout element to/from a margin group, use the \ref
2997 QCPLayoutElement::setMarginGroup method. To completely break apart the margin group, either call
2998 \ref clear, or just delete the margin group.
2999
3000 \section QCPMarginGroup-example Example
3001
3002 First create a margin group:
3003 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpmargingroup-creation-1
3004 Then set this group on the layout element sides:
3005 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpmargingroup-creation-2
3006 Here, we've used the first two axis rects of the plot and synchronized their left margins with
3007 each other and their right margins with each other.
3008*/
3009
3010/* start documentation of inline functions */
3011
3012/*! \fn QList<QCPLayoutElement*> QCPMarginGroup::elements(QCP::MarginSide side) const
3013
3014 Returns a list of all layout elements that have their margin \a side associated with this margin
3015 group.
3016*/
3017
3018/* end documentation of inline functions */
3019
3020/*!
3021 Creates a new QCPMarginGroup instance in \a parentPlot.
3022*/
3024 QObject(parentPlot),
3025 mParentPlot(parentPlot)
3026{
3031}
3032
3033QCPMarginGroup::~QCPMarginGroup()
3034{
3035 clear();
3036}
3037
3038/*!
3039 Returns whether this margin group is empty. If this function returns true, no layout elements use
3040 this margin group to synchronize margin sides.
3041*/
3043{
3045 while (it.hasNext())
3046 {
3047 it.next();
3048 if (!it.value().isEmpty())
3049 return false;
3050 }
3051 return true;
3052}
3053
3054/*!
3055 Clears this margin group. The synchronization of the margin sides that use this margin group is
3056 lifted and they will use their individual margin sizes again.
3057*/
3059{
3060 // make all children remove themselves from this margin group:
3062 while (it.hasNext())
3063 {
3064 it.next();
3066 for (int i=elements.size()-1; i>=0; --i)
3067 elements.at(i)->setMarginGroup(it.key(), nullptr); // removes itself from mChildren via removeChild
3068 }
3069}
3070
3071/*! \internal
3072
3073 Returns the synchronized common margin for \a side. This is the margin value that will be used by
3074 the layout element on the respective side, if it is part of this margin group.
3075
3076 The common margin is calculated by requesting the automatic margin (\ref
3077 QCPLayoutElement::calculateAutoMargin) of each element associated with \a side in this margin
3078 group, and choosing the largest returned value. (QCPLayoutElement::minimumMargins is taken into
3079 account, too.)
3080*/
3082{
3083 // query all automatic margins of the layout elements in this margin group side and find maximum:
3084 int result = 0;
3085 foreach (QCPLayoutElement *el, mChildren.value(side))
3086 {
3087 if (!el->autoMargins().testFlag(side))
3088 continue;
3089 int m = qMax(el->calculateAutoMargin(side), QCP::getMarginValue(el->minimumMargins(), side));
3090 if (m > result)
3091 result = m;
3092 }
3093 return result;
3094}
3095
3096/*! \internal
3097
3098 Adds \a element to the internal list of child elements, for the margin \a side.
3099
3100 This function does not modify the margin group property of \a element.
3101*/
3103{
3104 if (!mChildren[side].contains(element))
3105 mChildren[side].append(element);
3106 else
3107 qDebug() << Q_FUNC_INFO << "element is already child of this margin group side" << reinterpret_cast<quintptr>(element);
3108}
3109
3110/*! \internal
3111
3112 Removes \a element from the internal list of child elements, for the margin \a side.
3113
3114 This function does not modify the margin group property of \a element.
3115*/
3117{
3118 if (!mChildren[side].removeOne(element))
3119 qDebug() << Q_FUNC_INFO << "element is not child of this margin group side" << reinterpret_cast<quintptr>(element);
3120}
3121
3122
3123////////////////////////////////////////////////////////////////////////////////////////////////////
3124//////////////////// QCPLayoutElement
3125////////////////////////////////////////////////////////////////////////////////////////////////////
3126
3127/*! \class QCPLayoutElement
3128 \brief The abstract base class for all objects that form \ref thelayoutsystem "the layout system".
3129
3130 This is an abstract base class. As such, it can't be instantiated directly, rather use one of its subclasses.
3131
3132 A Layout element is a rectangular object which can be placed in layouts. It has an outer rect
3133 (QCPLayoutElement::outerRect) and an inner rect (\ref QCPLayoutElement::rect). The difference
3134 between outer and inner rect is called its margin. The margin can either be set to automatic or
3135 manual (\ref setAutoMargins) on a per-side basis. If a side is set to manual, that margin can be
3136 set explicitly with \ref setMargins and will stay fixed at that value. If it's set to automatic,
3137 the layout element subclass will control the value itself (via \ref calculateAutoMargin).
3138
3139 Layout elements can be placed in layouts (base class QCPLayout) like QCPLayoutGrid. The top level
3140 layout is reachable via \ref QCustomPlot::plotLayout, and is a \ref QCPLayoutGrid. Since \ref
3141 QCPLayout itself derives from \ref QCPLayoutElement, layouts can be nested.
3142
3143 Thus in QCustomPlot one can divide layout elements into two categories: The ones that are
3144 invisible by themselves, because they don't draw anything. Their only purpose is to manage the
3145 position and size of other layout elements. This category of layout elements usually use
3146 QCPLayout as base class. Then there is the category of layout elements which actually draw
3147 something. For example, QCPAxisRect, QCPLegend and QCPTextElement are of this category. This does
3148 not necessarily mean that the latter category can't have child layout elements. QCPLegend for
3149 instance, actually derives from QCPLayoutGrid and the individual legend items are child layout
3150 elements in the grid layout.
3151*/
3152
3153/* start documentation of inline functions */
3154
3155/*! \fn QCPLayout *QCPLayoutElement::layout() const
3156
3157 Returns the parent layout of this layout element.
3158*/
3159
3160/*! \fn QRect QCPLayoutElement::rect() const
3161
3162 Returns the inner rect of this layout element. The inner rect is the outer rect (\ref outerRect, \ref
3163 setOuterRect) shrinked by the margins (\ref setMargins, \ref setAutoMargins).
3164
3165 In some cases, the area between outer and inner rect is left blank. In other cases the margin
3166 area is used to display peripheral graphics while the main content is in the inner rect. This is
3167 where automatic margin calculation becomes interesting because it allows the layout element to
3168 adapt the margins to the peripheral graphics it wants to draw. For example, \ref QCPAxisRect
3169 draws the axis labels and tick labels in the margin area, thus needs to adjust the margins (if
3170 \ref setAutoMargins is enabled) according to the space required by the labels of the axes.
3171
3172 \see outerRect
3173*/
3174
3175/*! \fn QRect QCPLayoutElement::outerRect() const
3176
3177 Returns the outer rect of this layout element. The outer rect is the inner rect expanded by the
3178 margins (\ref setMargins, \ref setAutoMargins). The outer rect is used (and set via \ref
3179 setOuterRect) by the parent \ref QCPLayout to control the size of this layout element.
3180
3181 \see rect
3182*/
3183
3184/* end documentation of inline functions */
3185
3186/*!
3187 Creates an instance of QCPLayoutElement and sets default values.
3188*/
3190 QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout)
3191 mParentLayout(nullptr),
3192 mMinimumSize(),
3193 mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
3194 mSizeConstraintRect(scrInnerRect),
3195 mRect(0, 0, 0, 0),
3196 mOuterRect(0, 0, 0, 0),
3197 mMargins(0, 0, 0, 0),
3198 mMinimumMargins(0, 0, 0, 0),
3199 mAutoMargins(QCP::msAll)
3200{
3201}
3202
3203QCPLayoutElement::~QCPLayoutElement()
3204{
3205 setMarginGroup(QCP::msAll, nullptr); // unregister at margin groups, if there are any
3206 // unregister at layout:
3207 if (qobject_cast<QCPLayout*>(mParentLayout)) // the qobject_cast is just a safeguard in case the layout forgets to call clear() in its dtor and this dtor is called by QObject dtor
3208 mParentLayout->take(this);
3209}
3210
3211/*!
3212 Sets the outer rect of this layout element. If the layout element is inside a layout, the layout
3213 sets the position and size of this layout element using this function.
3214
3215 Calling this function externally has no effect, since the layout will overwrite any changes to
3216 the outer rect upon the next replot.
3217
3218 The layout element will adapt its inner \ref rect by applying the margins inward to the outer rect.
3219
3220 \see rect
3221*/
3223{
3224 if (mOuterRect != rect)
3225 {
3226 mOuterRect = rect;
3227 mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
3228 }
3229}
3230
3231/*!
3232 Sets the margins of this layout element. If \ref setAutoMargins is disabled for some or all
3233 sides, this function is used to manually set the margin on those sides. Sides that are still set
3234 to be handled automatically are ignored and may have any value in \a margins.
3235
3236 The margin is the distance between the outer rect (controlled by the parent layout via \ref
3237 setOuterRect) and the inner \ref rect (which usually contains the main content of this layout
3238 element).
3239
3240 \see setAutoMargins
3241*/
3243{
3244 if (mMargins != margins)
3245 {
3246 mMargins = margins;
3247 mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
3248 }
3249}
3250
3251/*!
3252 If \ref setAutoMargins is enabled on some or all margins, this function is used to provide
3253 minimum values for those margins.
3254
3255 The minimum values are not enforced on margin sides that were set to be under manual control via
3256 \ref setAutoMargins.
3257
3258 \see setAutoMargins
3259*/
3261{
3262 if (mMinimumMargins != margins)
3263 {
3264 mMinimumMargins = margins;
3265 }
3266}
3267
3268/*!
3269 Sets on which sides the margin shall be calculated automatically. If a side is calculated
3270 automatically, a minimum margin value may be provided with \ref setMinimumMargins. If a side is
3271 set to be controlled manually, the value may be specified with \ref setMargins.
3272
3273 Margin sides that are under automatic control may participate in a \ref QCPMarginGroup (see \ref
3274 setMarginGroup), to synchronize (align) it with other layout elements in the plot.
3275
3276 \see setMinimumMargins, setMargins, QCP::MarginSide
3277*/
3279{
3280 mAutoMargins = sides;
3281}
3282
3283/*!
3284 Sets the minimum size of this layout element. A parent layout tries to respect the \a size here
3285 by changing row/column sizes in the layout accordingly.
3286
3287 If the parent layout size is not sufficient to satisfy all minimum size constraints of its child
3288 layout elements, the layout may set a size that is actually smaller than \a size. QCustomPlot
3289 propagates the layout's size constraints to the outside by setting its own minimum QWidget size
3290 accordingly, so violations of \a size should be exceptions.
3291
3292 Whether this constraint applies to the inner or the outer rect can be specified with \ref
3293 setSizeConstraintRect (see \ref rect and \ref outerRect).
3294*/
3296{
3297 if (mMinimumSize != size)
3298 {
3299 mMinimumSize = size;
3300 if (mParentLayout)
3301 mParentLayout->sizeConstraintsChanged();
3302 }
3303}
3304
3305/*! \overload
3306
3307 Sets the minimum size of this layout element.
3308
3309 Whether this constraint applies to the inner or the outer rect can be specified with \ref
3310 setSizeConstraintRect (see \ref rect and \ref outerRect).
3311*/
3312void QCPLayoutElement::setMinimumSize(int width, int height)
3313{
3314 setMinimumSize(QSize(width, height));
3315}
3316
3317/*!
3318 Sets the maximum size of this layout element. A parent layout tries to respect the \a size here
3319 by changing row/column sizes in the layout accordingly.
3320
3321 Whether this constraint applies to the inner or the outer rect can be specified with \ref
3322 setSizeConstraintRect (see \ref rect and \ref outerRect).
3323*/
3325{
3326 if (mMaximumSize != size)
3327 {
3328 mMaximumSize = size;
3329 if (mParentLayout)
3330 mParentLayout->sizeConstraintsChanged();
3331 }
3332}
3333
3334/*! \overload
3335
3336 Sets the maximum size of this layout element.
3337
3338 Whether this constraint applies to the inner or the outer rect can be specified with \ref
3339 setSizeConstraintRect (see \ref rect and \ref outerRect).
3340*/
3341void QCPLayoutElement::setMaximumSize(int width, int height)
3342{
3343 setMaximumSize(QSize(width, height));
3344}
3345
3346/*!
3347 Sets to which rect of a layout element the size constraints apply. Size constraints can be set
3348 via \ref setMinimumSize and \ref setMaximumSize.
3349
3350 The outer rect (\ref outerRect) includes the margins (e.g. in the case of a QCPAxisRect the axis
3351 labels), whereas the inner rect (\ref rect) does not.
3352
3353 \see setMinimumSize, setMaximumSize
3354*/
3356{
3357 if (mSizeConstraintRect != constraintRect)
3358 {
3359 mSizeConstraintRect = constraintRect;
3360 if (mParentLayout)
3361 mParentLayout->sizeConstraintsChanged();
3362 }
3363}
3364
3365/*!
3366 Sets the margin \a group of the specified margin \a sides.
3367
3368 Margin groups allow synchronizing specified margins across layout elements, see the documentation
3369 of \ref QCPMarginGroup.
3370
3371 To unset the margin group of \a sides, set \a group to \c nullptr.
3372
3373 Note that margin groups only work for margin sides that are set to automatic (\ref
3374 setAutoMargins).
3375
3376 \see QCP::MarginSide
3377*/
3379{
3380 QVector<QCP::MarginSide> sideVector;
3381 if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
3382 if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
3383 if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
3384 if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
3385
3386 foreach (QCP::MarginSide side, sideVector)
3387 {
3388 if (marginGroup(side) != group)
3389 {
3390 QCPMarginGroup *oldGroup = marginGroup(side);
3391 if (oldGroup) // unregister at old group
3392 oldGroup->removeChild(side, this);
3393
3394 if (!group) // if setting to 0, remove hash entry. Else set hash entry to new group and register there
3395 {
3396 mMarginGroups.remove(side);
3397 } else // setting to a new group
3398 {
3399 mMarginGroups[side] = group;
3400 group->addChild(side, this);
3401 }
3402 }
3403 }
3404}
3405
3406/*!
3407 Updates the layout element and sub-elements. This function is automatically called before every
3408 replot by the parent layout element. It is called multiple times, once for every \ref
3409 UpdatePhase. The phases are run through in the order of the enum values. For details about what
3410 happens at the different phases, see the documentation of \ref UpdatePhase.
3411
3412 Layout elements that have child elements should call the \ref update method of their child
3413 elements, and pass the current \a phase unchanged.
3414
3415 The default implementation executes the automatic margin mechanism in the \ref upMargins phase.
3416 Subclasses should make sure to call the base class implementation.
3417*/
3419{
3420 if (phase == upMargins)
3421 {
3422 if (mAutoMargins != QCP::msNone)
3423 {
3424 // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
3425 QMargins newMargins = mMargins;
3427 foreach (QCP::MarginSide side, allMarginSides)
3428 {
3429 if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
3430 {
3431 if (mMarginGroups.contains(side))
3432 QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group
3433 else
3434 QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly
3435 // apply minimum margin restrictions:
3436 if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))
3437 QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));
3438 }
3439 }
3440 setMargins(newMargins);
3441 }
3442 }
3443}
3444
3445/*!
3446 Returns the suggested minimum size this layout element (the \ref outerRect) may be compressed to,
3447 if no manual minimum size is set.
3448
3449 if a minimum size (\ref setMinimumSize) was not set manually, parent layouts use the returned size
3450 (usually indirectly through \ref QCPLayout::getFinalMinimumOuterSize) to determine the minimum
3451 allowed size of this layout element.
3452
3453 A manual minimum size is considered set if it is non-zero.
3454
3455 The default implementation simply returns the sum of the horizontal margins for the width and the
3456 sum of the vertical margins for the height. Reimplementations may use their detailed knowledge
3457 about the layout element's content to provide size hints.
3458*/
3460{
3461 return {mMargins.left()+mMargins.right(), mMargins.top()+mMargins.bottom()};
3462}
3463
3464/*!
3465 Returns the suggested maximum size this layout element (the \ref outerRect) may be expanded to,
3466 if no manual maximum size is set.
3467
3468 if a maximum size (\ref setMaximumSize) was not set manually, parent layouts use the returned
3469 size (usually indirectly through \ref QCPLayout::getFinalMaximumOuterSize) to determine the
3470 maximum allowed size of this layout element.
3471
3472 A manual maximum size is considered set if it is smaller than Qt's \c QWIDGETSIZE_MAX.
3473
3474 The default implementation simply returns \c QWIDGETSIZE_MAX for both width and height, implying
3475 no suggested maximum size. Reimplementations may use their detailed knowledge about the layout
3476 element's content to provide size hints.
3477*/
3479{
3480 return {QWIDGETSIZE_MAX, QWIDGETSIZE_MAX};
3481}
3482
3483/*!
3484 Returns a list of all child elements in this layout element. If \a recursive is true, all
3485 sub-child elements are included in the list, too.
3486
3487 \warning There may be \c nullptr entries in the returned list. For example, QCPLayoutGrid may
3488 have empty cells which yield \c nullptr at the respective index.
3489*/
3491{
3492 Q_UNUSED(recursive)
3493 return QList<QCPLayoutElement*>();
3494}
3495
3496/*!
3497 Layout elements are sensitive to events inside their outer rect. If \a pos is within the outer
3498 rect, this method returns a value corresponding to 0.99 times the parent plot's selection
3499 tolerance. However, layout elements are not selectable by default. So if \a onlySelectable is
3500 true, -1.0 is returned.
3501
3502 See \ref QCPLayerable::selectTest for a general explanation of this virtual method.
3503
3504 QCPLayoutElement subclasses may reimplement this method to provide more specific selection test
3505 behaviour.
3506*/
3507double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
3508{
3509 Q_UNUSED(details)
3510
3511 if (onlySelectable)
3512 return -1;
3513
3514 if (QRectF(mOuterRect).contains(pos))
3515 {
3516 if (mParentPlot)
3517 return mParentPlot->selectionTolerance()*0.99;
3518 else
3519 {
3520 qDebug() << Q_FUNC_INFO << "parent plot not defined";
3521 return -1;
3522 }
3523 } else
3524 return -1;
3525}
3526
3527/*! \internal
3528
3529 propagates the parent plot initialization to all child elements, by calling \ref
3530 QCPLayerable::initializeParentPlot on them.
3531*/
3533{
3534 foreach (QCPLayoutElement *el, elements(false))
3535 {
3536 if (!el->parentPlot())
3537 el->initializeParentPlot(parentPlot);
3538 }
3539}
3540
3541/*! \internal
3542
3543 Returns the margin size for this \a side. It is used if automatic margins is enabled for this \a
3544 side (see \ref setAutoMargins). If a minimum margin was set with \ref setMinimumMargins, the
3545 returned value will not be smaller than the specified minimum margin.
3546
3547 The default implementation just returns the respective manual margin (\ref setMargins) or the
3548 minimum margin, whichever is larger.
3549*/
3551{
3552 return qMax(QCP::getMarginValue(mMargins, side), QCP::getMarginValue(mMinimumMargins, side));
3553}
3554
3555/*! \internal
3556
3557 This virtual method is called when this layout element was moved to a different QCPLayout, or
3558 when this layout element has changed its logical position (e.g. row and/or column) within the
3559 same QCPLayout. Subclasses may use this to react accordingly.
3560
3561 Since this method is called after the completion of the move, you can access the new parent
3562 layout via \ref layout().
3563
3564 The default implementation does nothing.
3565*/
3569
3570////////////////////////////////////////////////////////////////////////////////////////////////////
3571//////////////////// QCPLayout
3572////////////////////////////////////////////////////////////////////////////////////////////////////
3573
3574/*! \class QCPLayout
3575 \brief The abstract base class for layouts
3576
3577 This is an abstract base class for layout elements whose main purpose is to define the position
3578 and size of other child layout elements. In most cases, layouts don't draw anything themselves
3579 (but there are exceptions to this, e.g. QCPLegend).
3580
3581 QCPLayout derives from QCPLayoutElement, and thus can itself be nested in other layouts.
3582
3583 QCPLayout introduces a common interface for accessing and manipulating the child elements. Those
3584 functions are most notably \ref elementCount, \ref elementAt, \ref takeAt, \ref take, \ref
3585 simplify, \ref removeAt, \ref remove and \ref clear. Individual subclasses may add more functions
3586 to this interface which are more specialized to the form of the layout. For example, \ref
3587 QCPLayoutGrid adds functions that take row and column indices to access cells of the layout grid
3588 more conveniently.
3589
3590 Since this is an abstract base class, you can't instantiate it directly. Rather use one of its
3591 subclasses like QCPLayoutGrid or QCPLayoutInset.
3592
3593 For a general introduction to the layout system, see the dedicated documentation page \ref
3594 thelayoutsystem "The Layout System".
3595*/
3596
3597/* start documentation of pure virtual functions */
3598
3599/*! \fn virtual int QCPLayout::elementCount() const = 0
3600
3601 Returns the number of elements/cells in the layout.
3602
3603 \see elements, elementAt
3604*/
3605
3606/*! \fn virtual QCPLayoutElement* QCPLayout::elementAt(int index) const = 0
3607
3608 Returns the element in the cell with the given \a index. If \a index is invalid, returns \c
3609 nullptr.
3610
3611 Note that even if \a index is valid, the respective cell may be empty in some layouts (e.g.
3612 QCPLayoutGrid), so this function may return \c nullptr in those cases. You may use this function
3613 to check whether a cell is empty or not.
3614
3615 \see elements, elementCount, takeAt
3616*/
3617
3618/*! \fn virtual QCPLayoutElement* QCPLayout::takeAt(int index) = 0
3619
3620 Removes the element with the given \a index from the layout and returns it.
3621
3622 If the \a index is invalid or the cell with that index is empty, returns \c nullptr.
3623
3624 Note that some layouts don't remove the respective cell right away but leave an empty cell after
3625 successful removal of the layout element. To collapse empty cells, use \ref simplify.
3626
3627 \see elementAt, take
3628*/
3629
3630/*! \fn virtual bool QCPLayout::take(QCPLayoutElement* element) = 0
3631
3632 Removes the specified \a element from the layout and returns true on success.
3633
3634 If the \a element isn't in this layout, returns false.
3635
3636 Note that some layouts don't remove the respective cell right away but leave an empty cell after
3637 successful removal of the layout element. To collapse empty cells, use \ref simplify.
3638
3639 \see takeAt
3640*/
3641
3642/* end documentation of pure virtual functions */
3643
3644/*!
3645 Creates an instance of QCPLayout and sets default values. Note that since QCPLayout
3646 is an abstract base class, it can't be instantiated directly.
3647*/
3651
3652/*!
3653 If \a phase is \ref upLayout, calls \ref updateLayout, which subclasses may reimplement to
3654 reposition and resize their cells.
3655
3656 Finally, the call is propagated down to all child \ref QCPLayoutElement "QCPLayoutElements".
3657
3658 For details about this method and the update phases, see the documentation of \ref
3659 QCPLayoutElement::update.
3660*/
3662{
3664
3665 // set child element rects according to layout:
3666 if (phase == upLayout)
3667 updateLayout();
3668
3669 // propagate update call to child elements:
3670 const int elCount = elementCount();
3671 for (int i=0; i<elCount; ++i)
3672 {
3673 if (QCPLayoutElement *el = elementAt(i))
3674 el->update(phase);
3675 }
3676}
3677
3678/* inherits documentation from base class */
3680{
3681 const int c = elementCount();
3683#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
3684 result.reserve(c);
3685#endif
3686 for (int i=0; i<c; ++i)
3687 result.append(elementAt(i));
3688 if (recursive)
3689 {
3690 for (int i=0; i<c; ++i)
3691 {
3692 if (result.at(i))
3693 result << result.at(i)->elements(recursive);
3694 }
3695 }
3696 return result;
3697}
3698
3699/*!
3700 Simplifies the layout by collapsing empty cells. The exact behavior depends on subclasses, the
3701 default implementation does nothing.
3702
3703 Not all layouts need simplification. For example, QCPLayoutInset doesn't use explicit
3704 simplification while QCPLayoutGrid does.
3705*/
3707{
3708}
3709
3710/*!
3711 Removes and deletes the element at the provided \a index. Returns true on success. If \a index is
3712 invalid or points to an empty cell, returns false.
3713
3714 This function internally uses \ref takeAt to remove the element from the layout and then deletes
3715 the returned element. Note that some layouts don't remove the respective cell right away but leave an
3716 empty cell after successful removal of the layout element. To collapse empty cells, use \ref
3717 simplify.
3718
3719 \see remove, takeAt
3720*/
3721bool QCPLayout::removeAt(int index)
3722{
3723 if (QCPLayoutElement *el = takeAt(index))
3724 {
3725 delete el;
3726 return true;
3727 } else
3728 return false;
3729}
3730
3731/*!
3732 Removes and deletes the provided \a element. Returns true on success. If \a element is not in the
3733 layout, returns false.
3734
3735 This function internally uses \ref takeAt to remove the element from the layout and then deletes
3736 the element. Note that some layouts don't remove the respective cell right away but leave an
3737 empty cell after successful removal of the layout element. To collapse empty cells, use \ref
3738 simplify.
3739
3740 \see removeAt, take
3741*/
3743{
3744 if (take(element))
3745 {
3746 delete element;
3747 return true;
3748 } else
3749 return false;
3750}
3751
3752/*!
3753 Removes and deletes all layout elements in this layout. Finally calls \ref simplify to make sure
3754 all empty cells are collapsed.
3755
3756 \see remove, removeAt
3757*/
3759{
3760 for (int i=elementCount()-1; i>=0; --i)
3761 {
3762 if (elementAt(i))
3763 removeAt(i);
3764 }
3765 simplify();
3766}
3767
3768/*!
3769 Subclasses call this method to report changed (minimum/maximum) size constraints.
3770
3771 If the parent of this layout is again a QCPLayout, forwards the call to the parent's \ref
3772 sizeConstraintsChanged. If the parent is a QWidget (i.e. is the \ref QCustomPlot::plotLayout of
3773 QCustomPlot), calls QWidget::updateGeometry, so if the QCustomPlot widget is inside a Qt QLayout,
3774 it may update itself and resize cells accordingly.
3775*/
3777{
3779 w->updateGeometry();
3780 else if (QCPLayout *l = qobject_cast<QCPLayout*>(parent()))
3781 l->sizeConstraintsChanged();
3782}
3783
3784/*! \internal
3785
3786 Subclasses reimplement this method to update the position and sizes of the child elements/cells
3787 via calling their \ref QCPLayoutElement::setOuterRect. The default implementation does nothing.
3788
3789 The geometry used as a reference is the inner \ref rect of this layout. Child elements should stay
3790 within that rect.
3791
3792 \ref getSectionSizes may help with the reimplementation of this function.
3793
3794 \see update
3795*/
3797{
3798}
3799
3800
3801/*! \internal
3802
3803 Associates \a el with this layout. This is done by setting the \ref QCPLayoutElement::layout, the
3804 \ref QCPLayerable::parentLayerable and the QObject parent to this layout.
3805
3806 Further, if \a el didn't previously have a parent plot, calls \ref
3807 QCPLayerable::initializeParentPlot on \a el to set the paret plot.
3808
3809 This method is used by subclass specific methods that add elements to the layout. Note that this
3810 method only changes properties in \a el. The removal from the old layout and the insertion into
3811 the new layout must be done additionally.
3812*/
3814{
3815 if (el)
3816 {
3817 el->mParentLayout = this;
3818 el->setParentLayerable(this);
3819 el->setParent(this);
3820 if (!el->parentPlot())
3821 el->initializeParentPlot(mParentPlot);
3822 el->layoutChanged();
3823 } else
3824 qDebug() << Q_FUNC_INFO << "Null element passed";
3825}
3826
3827/*! \internal
3828
3829 Disassociates \a el from this layout. This is done by setting the \ref QCPLayoutElement::layout
3830 and the \ref QCPLayerable::parentLayerable to zero. The QObject parent is set to the parent
3831 QCustomPlot.
3832
3833 This method is used by subclass specific methods that remove elements from the layout (e.g. \ref
3834 take or \ref takeAt). Note that this method only changes properties in \a el. The removal from
3835 the old layout must be done additionally.
3836*/
3838{
3839 if (el)
3840 {
3841 el->mParentLayout = nullptr;
3842 el->setParentLayerable(nullptr);
3843 el->setParent(mParentPlot);
3844 // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot
3845 } else
3846 qDebug() << Q_FUNC_INFO << "Null element passed";
3847}
3848
3849/*! \internal
3850
3851 This is a helper function for the implementation of \ref updateLayout in subclasses.
3852
3853 It calculates the sizes of one-dimensional sections with provided constraints on maximum section
3854 sizes, minimum section sizes, relative stretch factors and the final total size of all sections.
3855
3856 The QVector entries refer to the sections. Thus all QVectors must have the same size.
3857
3858 \a maxSizes gives the maximum allowed size of each section. If there shall be no maximum size
3859 imposed, set all vector values to Qt's QWIDGETSIZE_MAX.
3860
3861 \a minSizes gives the minimum allowed size of each section. If there shall be no minimum size
3862 imposed, set all vector values to zero. If the \a minSizes entries add up to a value greater than
3863 \a totalSize, sections will be scaled smaller than the proposed minimum sizes. (In other words,
3864 not exceeding the allowed total size is taken to be more important than not going below minimum
3865 section sizes.)
3866
3867 \a stretchFactors give the relative proportions of the sections to each other. If all sections
3868 shall be scaled equally, set all values equal. If the first section shall be double the size of
3869 each individual other section, set the first number of \a stretchFactors to double the value of
3870 the other individual values (e.g. {2, 1, 1, 1}).
3871
3872 \a totalSize is the value that the final section sizes will add up to. Due to rounding, the
3873 actual sum may differ slightly. If you want the section sizes to sum up to exactly that value,
3874 you could distribute the remaining difference on the sections.
3875
3876 The return value is a QVector containing the section sizes.
3877*/
3878QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, QVector<int> minSizes, QVector<double> stretchFactors, int totalSize) const
3879{
3880 if (maxSizes.size() != minSizes.size() || minSizes.size() != stretchFactors.size())
3881 {
3882 qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes << minSizes << stretchFactors;
3883 return QVector<int>();
3884 }
3885 if (stretchFactors.isEmpty())
3886 return QVector<int>();
3887 int sectionCount = stretchFactors.size();
3888 QVector<double> sectionSizes(sectionCount);
3889 // if provided total size is forced smaller than total minimum size, ignore minimum sizes (squeeze sections):
3890 int minSizeSum = 0;
3891 for (int i=0; i<sectionCount; ++i)
3892 minSizeSum += minSizes.at(i);
3893 if (totalSize < minSizeSum)
3894 {
3895 // new stretch factors are minimum sizes and minimum sizes are set to zero:
3896 for (int i=0; i<sectionCount; ++i)
3897 {
3898 stretchFactors[i] = minSizes.at(i);
3899 minSizes[i] = 0;
3900 }
3901 }
3902
3903 QList<int> minimumLockedSections;
3904 QList<int> unfinishedSections;
3905 for (int i=0; i<sectionCount; ++i)
3906 unfinishedSections.append(i);
3907 double freeSize = totalSize;
3908
3909 int outerIterations = 0;
3910 while (!unfinishedSections.isEmpty() && outerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
3911 {
3912 ++outerIterations;
3913 int innerIterations = 0;
3914 while (!unfinishedSections.isEmpty() && innerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
3915 {
3916 ++innerIterations;
3917 // find section that hits its maximum next:
3918 int nextId = -1;
3919 double nextMax = 1e12;
3920 foreach (int secId, unfinishedSections)
3921 {
3922 double hitsMaxAt = (maxSizes.at(secId)-sectionSizes.at(secId))/stretchFactors.at(secId);
3923 if (hitsMaxAt < nextMax)
3924 {
3925 nextMax = hitsMaxAt;
3926 nextId = secId;
3927 }
3928 }
3929 // check if that maximum is actually within the bounds of the total size (i.e. can we stretch all remaining sections so far that the found section
3930 // actually hits its maximum, without exceeding the total size when we add up all sections)
3931 double stretchFactorSum = 0;
3932 foreach (int secId, unfinishedSections)
3933 stretchFactorSum += stretchFactors.at(secId);
3934 double nextMaxLimit = freeSize/stretchFactorSum;
3935 if (nextMax < nextMaxLimit) // next maximum is actually hit, move forward to that point and fix the size of that section
3936 {
3937 foreach (int secId, unfinishedSections)
3938 {
3939 sectionSizes[secId] += nextMax*stretchFactors.at(secId); // increment all sections
3940 freeSize -= nextMax*stretchFactors.at(secId);
3941 }
3942 unfinishedSections.removeOne(nextId); // exclude the section that is now at maximum from further changes
3943 } else // next maximum isn't hit, just distribute rest of free space on remaining sections
3944 {
3945 foreach (int secId, unfinishedSections)
3946 sectionSizes[secId] += nextMaxLimit*stretchFactors.at(secId); // increment all sections
3947 unfinishedSections.clear();
3948 }
3949 }
3950 if (innerIterations == sectionCount*2)
3951 qDebug() << Q_FUNC_INFO << "Exceeded maximum expected inner iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
3952
3953 // now check whether the resulting section sizes violate minimum restrictions:
3954 bool foundMinimumViolation = false;
3955 for (int i=0; i<sectionSizes.size(); ++i)
3956 {
3957 if (minimumLockedSections.contains(i))
3958 continue;
3959 if (sectionSizes.at(i) < minSizes.at(i)) // section violates minimum
3960 {
3961 sectionSizes[i] = minSizes.at(i); // set it to minimum
3962 foundMinimumViolation = true; // make sure we repeat the whole optimization process
3963 minimumLockedSections.append(i);
3964 }
3965 }
3966 if (foundMinimumViolation)
3967 {
3968 freeSize = totalSize;
3969 for (int i=0; i<sectionCount; ++i)
3970 {
3971 if (!minimumLockedSections.contains(i)) // only put sections that haven't hit their minimum back into the pool
3972 unfinishedSections.append(i);
3973 else
3974 freeSize -= sectionSizes.at(i); // remove size of minimum locked sections from available space in next round
3975 }
3976 // reset all section sizes to zero that are in unfinished sections (all others have been set to their minimum):
3977 foreach (int secId, unfinishedSections)
3978 sectionSizes[secId] = 0;
3979 }
3980 }
3981 if (outerIterations == sectionCount*2)
3982 qDebug() << Q_FUNC_INFO << "Exceeded maximum expected outer iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
3983
3984 QVector<int> result(sectionCount);
3985 for (int i=0; i<sectionCount; ++i)
3986 result[i] = qRound(sectionSizes.at(i));
3987 return result;
3988}
3989
3990/*! \internal
3991
3992 This is a helper function for the implementation of subclasses.
3993
3994 It returns the minimum size that should finally be used for the outer rect of the passed layout
3995 element \a el.
3996
3997 It takes into account whether a manual minimum size is set (\ref
3998 QCPLayoutElement::setMinimumSize), which size constraint is set (\ref
3999 QCPLayoutElement::setSizeConstraintRect), as well as the minimum size hint, if no manual minimum
4000 size was set (\ref QCPLayoutElement::minimumOuterSizeHint).
4001*/
4003{
4004 QSize minOuterHint = el->minimumOuterSizeHint();
4005 QSize minOuter = el->minimumSize(); // depending on sizeConstraitRect this might be with respect to inner rect, so possibly add margins in next four lines (preserving unset minimum of 0)
4006 if (minOuter.width() > 0 && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)
4007 minOuter.rwidth() += el->margins().left() + el->margins().right();
4008 if (minOuter.height() > 0 && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)
4009 minOuter.rheight() += el->margins().top() + el->margins().bottom();
4010
4011 return {minOuter.width() > 0 ? minOuter.width() : minOuterHint.width(),
4012 minOuter.height() > 0 ? minOuter.height() : minOuterHint.height()};
4013}
4014
4015/*! \internal
4016
4017 This is a helper function for the implementation of subclasses.
4018
4019 It returns the maximum size that should finally be used for the outer rect of the passed layout
4020 element \a el.
4021
4022 It takes into account whether a manual maximum size is set (\ref
4023 QCPLayoutElement::setMaximumSize), which size constraint is set (\ref
4024 QCPLayoutElement::setSizeConstraintRect), as well as the maximum size hint, if no manual maximum
4025 size was set (\ref QCPLayoutElement::maximumOuterSizeHint).
4026*/
4028{
4029 QSize maxOuterHint = el->maximumOuterSizeHint();
4030 QSize maxOuter = el->maximumSize(); // depending on sizeConstraitRect this might be with respect to inner rect, so possibly add margins in next four lines (preserving unset maximum of QWIDGETSIZE_MAX)
4031 if (maxOuter.width() < QWIDGETSIZE_MAX && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)
4032 maxOuter.rwidth() += el->margins().left() + el->margins().right();
4033 if (maxOuter.height() < QWIDGETSIZE_MAX && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)
4034 maxOuter.rheight() += el->margins().top() + el->margins().bottom();
4035
4036 return {maxOuter.width() < QWIDGETSIZE_MAX ? maxOuter.width() : maxOuterHint.width(),
4037 maxOuter.height() < QWIDGETSIZE_MAX ? maxOuter.height() : maxOuterHint.height()};
4038}
4039
4040
4041////////////////////////////////////////////////////////////////////////////////////////////////////
4042//////////////////// QCPLayoutGrid
4043////////////////////////////////////////////////////////////////////////////////////////////////////
4044
4045/*! \class QCPLayoutGrid
4046 \brief A layout that arranges child elements in a grid
4047
4048 Elements are laid out in a grid with configurable stretch factors (\ref setColumnStretchFactor,
4049 \ref setRowStretchFactor) and spacing (\ref setColumnSpacing, \ref setRowSpacing).
4050
4051 Elements can be added to cells via \ref addElement. The grid is expanded if the specified row or
4052 column doesn't exist yet. Whether a cell contains a valid layout element can be checked with \ref
4053 hasElement, that element can be retrieved with \ref element. If rows and columns that only have
4054 empty cells shall be removed, call \ref simplify. Removal of elements is either done by just
4055 adding the element to a different layout or by using the QCPLayout interface \ref take or \ref
4056 remove.
4057
4058 If you use \ref addElement(QCPLayoutElement*) without explicit parameters for \a row and \a
4059 column, the grid layout will choose the position according to the current \ref setFillOrder and
4060 the wrapping (\ref setWrap).
4061
4062 Row and column insertion can be performed with \ref insertRow and \ref insertColumn.
4063*/
4064
4065/* start documentation of inline functions */
4066
4067/*! \fn int QCPLayoutGrid::rowCount() const
4068
4069 Returns the number of rows in the layout.
4070
4071 \see columnCount
4072*/
4073
4074/*! \fn int QCPLayoutGrid::columnCount() const
4075
4076 Returns the number of columns in the layout.
4077
4078 \see rowCount
4079*/
4080
4081/* end documentation of inline functions */
4082
4083/*!
4084 Creates an instance of QCPLayoutGrid and sets default values.
4085*/
4087 mColumnSpacing(5),
4088 mRowSpacing(5),
4089 mWrap(0),
4090 mFillOrder(foColumnsFirst)
4091{
4092}
4093
4094QCPLayoutGrid::~QCPLayoutGrid()
4095{
4096 // clear all child layout elements. This is important because only the specific layouts know how
4097 // to handle removing elements (clear calls virtual removeAt method to do that).
4098 clear();
4099}
4100
4101/*!
4102 Returns the element in the cell in \a row and \a column.
4103
4104 Returns \c nullptr if either the row/column is invalid or if the cell is empty. In those cases, a
4105 qDebug message is printed. To check whether a cell exists and isn't empty, use \ref hasElement.
4106
4107 \see addElement, hasElement
4108*/
4109QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const
4110{
4111 if (row >= 0 && row < mElements.size())
4112 {
4113 if (column >= 0 && column < mElements.first().size())
4114 {
4115 if (QCPLayoutElement *result = mElements.at(row).at(column))
4116 return result;
4117 else
4118 qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row << "Column:" << column;
4119 } else
4120 qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column;
4121 } else
4122 qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column;
4123 return nullptr;
4124}
4125
4126
4127/*! \overload
4128
4129 Adds the \a element to cell with \a row and \a column. If \a element is already in a layout, it
4130 is first removed from there. If \a row or \a column don't exist yet, the layout is expanded
4131 accordingly.
4132
4133 Returns true if the element was added successfully, i.e. if the cell at \a row and \a column
4134 didn't already have an element.
4135
4136 Use the overload of this method without explicit row/column index to place the element according
4137 to the configured fill order and wrapping settings.
4138
4139 \see element, hasElement, take, remove
4140*/
4141bool QCPLayoutGrid::addElement(int row, int column, QCPLayoutElement *element)
4142{
4143 if (!hasElement(row, column))
4144 {
4145 if (element && element->layout()) // remove from old layout first
4147 expandTo(row+1, column+1);
4148 mElements[row][column] = element;
4149 if (element)
4151 return true;
4152 } else
4153 qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column;
4154 return false;
4155}
4156
4157/*! \overload
4158
4159 Adds the \a element to the next empty cell according to the current fill order (\ref
4160 setFillOrder) and wrapping (\ref setWrap). If \a element is already in a layout, it is first
4161 removed from there. If necessary, the layout is expanded to hold the new element.
4162
4163 Returns true if the element was added successfully.
4164
4165 \see setFillOrder, setWrap, element, hasElement, take, remove
4166*/
4168{
4169 int rowIndex = 0;
4170 int colIndex = 0;
4171 if (mFillOrder == foColumnsFirst)
4172 {
4173 while (hasElement(rowIndex, colIndex))
4174 {
4175 ++colIndex;
4176 if (colIndex >= mWrap && mWrap > 0)
4177 {
4178 colIndex = 0;
4179 ++rowIndex;
4180 }
4181 }
4182 } else
4183 {
4184 while (hasElement(rowIndex, colIndex))
4185 {
4186 ++rowIndex;
4187 if (rowIndex >= mWrap && mWrap > 0)
4188 {
4189 rowIndex = 0;
4190 ++colIndex;
4191 }
4192 }
4193 }
4194 return addElement(rowIndex, colIndex, element);
4195}
4196
4197/*!
4198 Returns whether the cell at \a row and \a column exists and contains a valid element, i.e. isn't
4199 empty.
4200
4201 \see element
4202*/
4203bool QCPLayoutGrid::hasElement(int row, int column)
4204{
4205 if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
4206 return mElements.at(row).at(column);
4207 else
4208 return false;
4209}
4210
4211/*!
4212 Sets the stretch \a factor of \a column.
4213
4214 Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
4215 their minimum and maximum widths/heights, regardless of the stretch factor. (see \ref
4216 QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize, \ref
4217 QCPLayoutElement::setSizeConstraintRect.)
4218
4219 The default stretch factor of newly created rows/columns is 1.
4220
4221 \see setColumnStretchFactors, setRowStretchFactor
4222*/
4223void QCPLayoutGrid::setColumnStretchFactor(int column, double factor)
4224{
4225 if (column >= 0 && column < columnCount())
4226 {
4227 if (factor > 0)
4228 mColumnStretchFactors[column] = factor;
4229 else
4230 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
4231 } else
4232 qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
4233}
4234
4235/*!
4236 Sets the stretch \a factors of all columns. \a factors must have the size \ref columnCount.
4237
4238 Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
4239 their minimum and maximum widths/heights, regardless of the stretch factor. (see \ref
4240 QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize, \ref
4241 QCPLayoutElement::setSizeConstraintRect.)
4242
4243 The default stretch factor of newly created rows/columns is 1.
4244
4245 \see setColumnStretchFactor, setRowStretchFactors
4246*/
4248{
4249 if (factors.size() == mColumnStretchFactors.size())
4250 {
4251 mColumnStretchFactors = factors;
4252 for (int i=0; i<mColumnStretchFactors.size(); ++i)
4253 {
4254 if (mColumnStretchFactors.at(i) <= 0)
4255 {
4256 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mColumnStretchFactors.at(i);
4257 mColumnStretchFactors[i] = 1;
4258 }
4259 }
4260 } else
4261 qDebug() << Q_FUNC_INFO << "Column count not equal to passed stretch factor count:" << factors;
4262}
4263
4264/*!
4265 Sets the stretch \a factor of \a row.
4266
4267 Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
4268 their minimum and maximum widths/heights, regardless of the stretch factor. (see \ref
4269 QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize, \ref
4270 QCPLayoutElement::setSizeConstraintRect.)
4271
4272 The default stretch factor of newly created rows/columns is 1.
4273
4274 \see setColumnStretchFactors, setRowStretchFactor
4275*/
4276void QCPLayoutGrid::setRowStretchFactor(int row, double factor)
4277{
4278 if (row >= 0 && row < rowCount())
4279 {
4280 if (factor > 0)
4281 mRowStretchFactors[row] = factor;
4282 else
4283 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
4284 } else
4285 qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
4286}
4287
4288/*!
4289 Sets the stretch \a factors of all rows. \a factors must have the size \ref rowCount.
4290
4291 Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
4292 their minimum and maximum widths/heights, regardless of the stretch factor. (see \ref
4293 QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize, \ref
4294 QCPLayoutElement::setSizeConstraintRect.)
4295
4296 The default stretch factor of newly created rows/columns is 1.
4297
4298 \see setRowStretchFactor, setColumnStretchFactors
4299*/
4301{
4302 if (factors.size() == mRowStretchFactors.size())
4303 {
4304 mRowStretchFactors = factors;
4305 for (int i=0; i<mRowStretchFactors.size(); ++i)
4306 {
4307 if (mRowStretchFactors.at(i) <= 0)
4308 {
4309 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mRowStretchFactors.at(i);
4310 mRowStretchFactors[i] = 1;
4311 }
4312 }
4313 } else
4314 qDebug() << Q_FUNC_INFO << "Row count not equal to passed stretch factor count:" << factors;
4315}
4316
4317/*!
4318 Sets the gap that is left blank between columns to \a pixels.
4319
4320 \see setRowSpacing
4321*/
4323{
4324 mColumnSpacing = pixels;
4325}
4326
4327/*!
4328 Sets the gap that is left blank between rows to \a pixels.
4329
4330 \see setColumnSpacing
4331*/
4333{
4334 mRowSpacing = pixels;
4335}
4336
4337/*!
4338 Sets the maximum number of columns or rows that are used, before new elements added with \ref
4339 addElement(QCPLayoutElement*) will start to fill the next row or column, respectively. It depends
4340 on \ref setFillOrder, whether rows or columns are wrapped.
4341
4342 If \a count is set to zero, no wrapping will ever occur.
4343
4344 If you wish to re-wrap the elements currently in the layout, call \ref setFillOrder with \a
4345 rearrange set to true (the actual fill order doesn't need to be changed for the rearranging to be
4346 done).
4347
4348 Note that the method \ref addElement(int row, int column, QCPLayoutElement *element) with
4349 explicitly stated row and column is not subject to wrapping and can place elements even beyond
4350 the specified wrapping point.
4351
4352 \see setFillOrder
4353*/
4355{
4356 mWrap = qMax(0, count);
4357}
4358
4359/*!
4360 Sets the filling order and wrapping behaviour that is used when adding new elements with the
4361 method \ref addElement(QCPLayoutElement*).
4362
4363 The specified \a order defines whether rows or columns are filled first. Using \ref setWrap, you
4364 can control at which row/column count wrapping into the next column/row will occur. If you set it
4365 to zero, no wrapping will ever occur. Changing the fill order also changes the meaning of the
4366 linear index used e.g. in \ref elementAt and \ref takeAt. The default fill order for \ref
4367 QCPLayoutGrid is \ref foColumnsFirst.
4368
4369 If you want to have all current elements arranged in the new order, set \a rearrange to true. The
4370 elements will be rearranged in a way that tries to preserve their linear index. However, empty
4371 cells are skipped during build-up of the new cell order, which shifts the succeeding element's
4372 index. The rearranging is performed even if the specified \a order is already the current fill
4373 order. Thus this method can be used to re-wrap the current elements.
4374
4375 If \a rearrange is false, the current element arrangement is not changed, which means the
4376 linear indexes change (because the linear index is dependent on the fill order).
4377
4378 Note that the method \ref addElement(int row, int column, QCPLayoutElement *element) with
4379 explicitly stated row and column is not subject to wrapping and can place elements even beyond
4380 the specified wrapping point.
4381
4382 \see setWrap, addElement(QCPLayoutElement*)
4383*/
4384void QCPLayoutGrid::setFillOrder(FillOrder order, bool rearrange)
4385{
4386 // if rearranging, take all elements via linear index of old fill order:
4387 const int elCount = elementCount();
4388 QVector<QCPLayoutElement*> tempElements;
4389 if (rearrange)
4390 {
4391 tempElements.reserve(elCount);
4392 for (int i=0; i<elCount; ++i)
4393 {
4394 if (elementAt(i))
4395 tempElements.append(takeAt(i));
4396 }
4397 simplify();
4398 }
4399 // change fill order as requested:
4400 mFillOrder = order;
4401 // if rearranging, re-insert via linear index according to new fill order:
4402 if (rearrange)
4403 {
4404 foreach (QCPLayoutElement *tempElement, tempElements)
4405 addElement(tempElement);
4406 }
4407}
4408
4409/*!
4410 Expands the layout to have \a newRowCount rows and \a newColumnCount columns. So the last valid
4411 row index will be \a newRowCount-1, the last valid column index will be \a newColumnCount-1.
4412
4413 If the current column/row count is already larger or equal to \a newColumnCount/\a newRowCount,
4414 this function does nothing in that dimension.
4415
4416 Newly created cells are empty, new rows and columns have the stretch factor 1.
4417
4418 Note that upon a call to \ref addElement, the layout is expanded automatically to contain the
4419 specified row and column, using this function.
4420
4421 \see simplify
4422*/
4423void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount)
4424{
4425 // add rows as necessary:
4426 while (rowCount() < newRowCount)
4427 {
4428 mElements.append(QList<QCPLayoutElement*>());
4429 mRowStretchFactors.append(1);
4430 }
4431 // go through rows and expand columns as necessary:
4432 int newColCount = qMax(columnCount(), newColumnCount);
4433 for (int i=0; i<rowCount(); ++i)
4434 {
4435 while (mElements.at(i).size() < newColCount)
4436 mElements[i].append(nullptr);
4437 }
4438 while (mColumnStretchFactors.size() < newColCount)
4439 mColumnStretchFactors.append(1);
4440}
4441
4442/*!
4443 Inserts a new row with empty cells at the row index \a newIndex. Valid values for \a newIndex
4444 range from 0 (inserts a row at the top) to \a rowCount (appends a row at the bottom).
4445
4446 \see insertColumn
4447*/
4449{
4450 if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
4451 {
4452 expandTo(1, 1);
4453 return;
4454 }
4455
4456 if (newIndex < 0)
4457 newIndex = 0;
4458 if (newIndex > rowCount())
4459 newIndex = rowCount();
4460
4461 mRowStretchFactors.insert(newIndex, 1);
4463 for (int col=0; col<columnCount(); ++col)
4464 newRow.append(nullptr);
4465 mElements.insert(newIndex, newRow);
4466}
4467
4468/*!
4469 Inserts a new column with empty cells at the column index \a newIndex. Valid values for \a
4470 newIndex range from 0 (inserts a column at the left) to \a columnCount (appends a column at the
4471 right).
4472
4473 \see insertRow
4474*/
4476{
4477 if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
4478 {
4479 expandTo(1, 1);
4480 return;
4481 }
4482
4483 if (newIndex < 0)
4484 newIndex = 0;
4485 if (newIndex > columnCount())
4486 newIndex = columnCount();
4487
4488 mColumnStretchFactors.insert(newIndex, 1);
4489 for (int row=0; row<rowCount(); ++row)
4490 mElements[row].insert(newIndex, nullptr);
4491}
4492
4493/*!
4494 Converts the given \a row and \a column to the linear index used by some methods of \ref
4495 QCPLayoutGrid and \ref QCPLayout.
4496
4497 The way the cells are indexed depends on \ref setFillOrder. If it is \ref foRowsFirst, the
4498 indices increase left to right and then top to bottom. If it is \ref foColumnsFirst, the indices
4499 increase top to bottom and then left to right.
4500
4501 For the returned index to be valid, \a row and \a column must be valid indices themselves, i.e.
4502 greater or equal to zero and smaller than the current \ref rowCount/\ref columnCount.
4503
4504 \see indexToRowCol
4505*/
4506int QCPLayoutGrid::rowColToIndex(int row, int column) const
4507{
4508 if (row >= 0 && row < rowCount())
4509 {
4510 if (column >= 0 && column < columnCount())
4511 {
4512 switch (mFillOrder)
4513 {
4514 case foRowsFirst: return column*rowCount() + row;
4515 case foColumnsFirst: return row*columnCount() + column;
4516 }
4517 } else
4518 qDebug() << Q_FUNC_INFO << "row index out of bounds:" << row;
4519 } else
4520 qDebug() << Q_FUNC_INFO << "column index out of bounds:" << column;
4521 return 0;
4522}
4523
4524/*!
4525 Converts the linear index to row and column indices and writes the result to \a row and \a
4526 column.
4527
4528 The way the cells are indexed depends on \ref setFillOrder. If it is \ref foRowsFirst, the
4529 indices increase left to right and then top to bottom. If it is \ref foColumnsFirst, the indices
4530 increase top to bottom and then left to right.
4531
4532 If there are no cells (i.e. column or row count is zero), sets \a row and \a column to -1.
4533
4534 For the retrieved \a row and \a column to be valid, the passed \a index must be valid itself,
4535 i.e. greater or equal to zero and smaller than the current \ref elementCount.
4536
4537 \see rowColToIndex
4538*/
4539void QCPLayoutGrid::indexToRowCol(int index, int &row, int &column) const
4540{
4541 row = -1;
4542 column = -1;
4543 const int nCols = columnCount();
4544 const int nRows = rowCount();
4545 if (nCols == 0 || nRows == 0)
4546 return;
4547 if (index < 0 || index >= elementCount())
4548 {
4549 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
4550 return;
4551 }
4552
4553 switch (mFillOrder)
4554 {
4555 case foRowsFirst:
4556 {
4557 column = index / nRows;
4558 row = index % nRows;
4559 break;
4560 }
4561 case foColumnsFirst:
4562 {
4563 row = index / nCols;
4564 column = index % nCols;
4565 break;
4566 }
4567 }
4568}
4569
4570/* inherits documentation from base class */
4572{
4573 QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
4574 getMinimumRowColSizes(&minColWidths, &minRowHeights);
4575 getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
4576
4577 int totalRowSpacing = (rowCount()-1) * mRowSpacing;
4578 int totalColSpacing = (columnCount()-1) * mColumnSpacing;
4579 QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths, mColumnStretchFactors.toVector(), mRect.width()-totalColSpacing);
4580 QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights, mRowStretchFactors.toVector(), mRect.height()-totalRowSpacing);
4581
4582 // go through cells and set rects accordingly:
4583 int yOffset = mRect.top();
4584 for (int row=0; row<rowCount(); ++row)
4585 {
4586 if (row > 0)
4587 yOffset += rowHeights.at(row-1)+mRowSpacing;
4588 int xOffset = mRect.left();
4589 for (int col=0; col<columnCount(); ++col)
4590 {
4591 if (col > 0)
4592 xOffset += colWidths.at(col-1)+mColumnSpacing;
4593 if (mElements.at(row).at(col))
4594 mElements.at(row).at(col)->setOuterRect(QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row)));
4595 }
4596 }
4597}
4598
4599/*!
4600 \seebaseclassmethod
4601
4602 Note that the association of the linear \a index to the row/column based cells depends on the
4603 current setting of \ref setFillOrder.
4604
4605 \see rowColToIndex
4606*/
4608{
4609 if (index >= 0 && index < elementCount())
4610 {
4611 int row, col;
4612 indexToRowCol(index, row, col);
4613 return mElements.at(row).at(col);
4614 } else
4615 return nullptr;
4616}
4617
4618/*!
4619 \seebaseclassmethod
4620
4621 Note that the association of the linear \a index to the row/column based cells depends on the
4622 current setting of \ref setFillOrder.
4623
4624 \see rowColToIndex
4625*/
4627{
4628 if (QCPLayoutElement *el = elementAt(index))
4629 {
4630 releaseElement(el);
4631 int row, col;
4632 indexToRowCol(index, row, col);
4633 mElements[row][col] = nullptr;
4634 return el;
4635 } else
4636 {
4637 qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
4638 return nullptr;
4639 }
4640}
4641
4642/* inherits documentation from base class */
4644{
4645 if (element)
4646 {
4647 for (int i=0; i<elementCount(); ++i)
4648 {
4649 if (elementAt(i) == element)
4650 {
4651 takeAt(i);
4652 return true;
4653 }
4654 }
4655 qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
4656 } else
4657 qDebug() << Q_FUNC_INFO << "Can't take nullptr element";
4658 return false;
4659}
4660
4661/* inherits documentation from base class */
4663{
4665 const int elCount = elementCount();
4666#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
4667 result.reserve(elCount);
4668#endif
4669 for (int i=0; i<elCount; ++i)
4670 result.append(elementAt(i));
4671 if (recursive)
4672 {
4673 for (int i=0; i<elCount; ++i)
4674 {
4675 if (result.at(i))
4676 result << result.at(i)->elements(recursive);
4677 }
4678 }
4679 return result;
4680}
4681
4682/*!
4683 Simplifies the layout by collapsing rows and columns which only contain empty cells.
4684*/
4686{
4687 // remove rows with only empty cells:
4688 for (int row=rowCount()-1; row>=0; --row)
4689 {
4690 bool hasElements = false;
4691 for (int col=0; col<columnCount(); ++col)
4692 {
4693 if (mElements.at(row).at(col))
4694 {
4695 hasElements = true;
4696 break;
4697 }
4698 }
4699 if (!hasElements)
4700 {
4701 mRowStretchFactors.removeAt(row);
4702 mElements.removeAt(row);
4703 if (mElements.isEmpty()) // removed last element, also remove stretch factor (wouldn't happen below because also columnCount changed to 0 now)
4704 mColumnStretchFactors.clear();
4705 }
4706 }
4707
4708 // remove columns with only empty cells:
4709 for (int col=columnCount()-1; col>=0; --col)
4710 {
4711 bool hasElements = false;
4712 for (int row=0; row<rowCount(); ++row)
4713 {
4714 if (mElements.at(row).at(col))
4715 {
4716 hasElements = true;
4717 break;
4718 }
4719 }
4720 if (!hasElements)
4721 {
4722 mColumnStretchFactors.removeAt(col);
4723 for (int row=0; row<rowCount(); ++row)
4724 mElements[row].removeAt(col);
4725 }
4726 }
4727}
4728
4729/* inherits documentation from base class */
4731{
4732 QVector<int> minColWidths, minRowHeights;
4733 getMinimumRowColSizes(&minColWidths, &minRowHeights);
4734 QSize result(0, 0);
4735 foreach (int w, minColWidths)
4736 result.rwidth() += w;
4737 foreach (int h, minRowHeights)
4738 result.rheight() += h;
4739 result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing;
4740 result.rheight() += qMax(0, rowCount()-1) * mRowSpacing;
4741 result.rwidth() += mMargins.left()+mMargins.right();
4742 result.rheight() += mMargins.top()+mMargins.bottom();
4743 return result;
4744}
4745
4746/* inherits documentation from base class */
4748{
4749 QVector<int> maxColWidths, maxRowHeights;
4750 getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
4751
4752 QSize result(0, 0);
4753 foreach (int w, maxColWidths)
4754 result.setWidth(qMin(result.width()+w, QWIDGETSIZE_MAX));
4755 foreach (int h, maxRowHeights)
4756 result.setHeight(qMin(result.height()+h, QWIDGETSIZE_MAX));
4757 result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing;
4758 result.rheight() += qMax(0, rowCount()-1) * mRowSpacing;
4759 result.rwidth() += mMargins.left()+mMargins.right();
4760 result.rheight() += mMargins.top()+mMargins.bottom();
4761 if (result.height() > QWIDGETSIZE_MAX)
4762 result.setHeight(QWIDGETSIZE_MAX);
4763 if (result.width() > QWIDGETSIZE_MAX)
4764 result.setWidth(QWIDGETSIZE_MAX);
4765 return result;
4766}
4767
4768/*! \internal
4769
4770 Places the minimum column widths and row heights into \a minColWidths and \a minRowHeights
4771 respectively.
4772
4773 The minimum height of a row is the largest minimum height of any element's outer rect in that
4774 row. The minimum width of a column is the largest minimum width of any element's outer rect in
4775 that column.
4776
4777 This is a helper function for \ref updateLayout.
4778
4779 \see getMaximumRowColSizes
4780*/
4781void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths, QVector<int> *minRowHeights) const
4782{
4783 *minColWidths = QVector<int>(columnCount(), 0);
4784 *minRowHeights = QVector<int>(rowCount(), 0);
4785 for (int row=0; row<rowCount(); ++row)
4786 {
4787 for (int col=0; col<columnCount(); ++col)
4788 {
4789 if (QCPLayoutElement *el = mElements.at(row).at(col))
4790 {
4791 QSize minSize = getFinalMinimumOuterSize(el);
4792 if (minColWidths->at(col) < minSize.width())
4793 (*minColWidths)[col] = minSize.width();
4794 if (minRowHeights->at(row) < minSize.height())
4795 (*minRowHeights)[row] = minSize.height();
4796 }
4797 }
4798 }
4799}
4800
4801/*! \internal
4802
4803 Places the maximum column widths and row heights into \a maxColWidths and \a maxRowHeights
4804 respectively.
4805
4806 The maximum height of a row is the smallest maximum height of any element's outer rect in that
4807 row. The maximum width of a column is the smallest maximum width of any element's outer rect in
4808 that column.
4809
4810 This is a helper function for \ref updateLayout.
4811
4812 \see getMinimumRowColSizes
4813*/
4814void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths, QVector<int> *maxRowHeights) const
4815{
4816 *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);
4817 *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);
4818 for (int row=0; row<rowCount(); ++row)
4819 {
4820 for (int col=0; col<columnCount(); ++col)
4821 {
4822 if (QCPLayoutElement *el = mElements.at(row).at(col))
4823 {
4824 QSize maxSize = getFinalMaximumOuterSize(el);
4825 if (maxColWidths->at(col) > maxSize.width())
4826 (*maxColWidths)[col] = maxSize.width();
4827 if (maxRowHeights->at(row) > maxSize.height())
4828 (*maxRowHeights)[row] = maxSize.height();
4829 }
4830 }
4831 }
4832}
4833
4834
4835////////////////////////////////////////////////////////////////////////////////////////////////////
4836//////////////////// QCPLayoutInset
4837////////////////////////////////////////////////////////////////////////////////////////////////////
4838/*! \class QCPLayoutInset
4839 \brief A layout that places child elements aligned to the border or arbitrarily positioned
4840
4841 Elements are placed either aligned to the border or at arbitrary position in the area of the
4842 layout. Which placement applies is controlled with the \ref InsetPlacement (\ref
4843 setInsetPlacement).
4844
4845 Elements are added via \ref addElement(QCPLayoutElement *element, Qt::Alignment alignment) or
4846 addElement(QCPLayoutElement *element, const QRectF &rect). If the first method is used, the inset
4847 placement will default to \ref ipBorderAligned and the element will be aligned according to the
4848 \a alignment parameter. The second method defaults to \ref ipFree and allows placing elements at
4849 arbitrary position and size, defined by \a rect.
4850
4851 The alignment or rect can be set via \ref setInsetAlignment or \ref setInsetRect, respectively.
4852
4853 This is the layout that every QCPAxisRect has as \ref QCPAxisRect::insetLayout.
4854*/
4855
4856/* start documentation of inline functions */
4857
4858/*! \fn virtual void QCPLayoutInset::simplify()
4859
4860 The QCPInsetLayout does not need simplification since it can never have empty cells due to its
4861 linear index structure. This method does nothing.
4862*/
4863
4864/* end documentation of inline functions */
4865
4866/*!
4867 Creates an instance of QCPLayoutInset and sets default values.
4868*/
4872
4873QCPLayoutInset::~QCPLayoutInset()
4874{
4875 // clear all child layout elements. This is important because only the specific layouts know how
4876 // to handle removing elements (clear calls virtual removeAt method to do that).
4877 clear();
4878}
4879
4880/*!
4881 Returns the placement type of the element with the specified \a index.
4882*/
4884{
4885 if (elementAt(index))
4886 return mInsetPlacement.at(index);
4887 else
4888 {
4889 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4890 return ipFree;
4891 }
4892}
4893
4894/*!
4895 Returns the alignment of the element with the specified \a index. The alignment only has a
4896 meaning, if the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned.
4897*/
4899{
4900 if (elementAt(index))
4901 return mInsetAlignment.at(index);
4902 else
4903 {
4904 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4905#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)
4906 return nullptr;
4907#else
4908 return {};
4909#endif
4910 }
4911}
4912
4913/*!
4914 Returns the rect of the element with the specified \a index. The rect only has a
4915 meaning, if the inset placement (\ref setInsetPlacement) is \ref ipFree.
4916*/
4918{
4919 if (elementAt(index))
4920 return mInsetRect.at(index);
4921 else
4922 {
4923 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4924 return {};
4925 }
4926}
4927
4928/*!
4929 Sets the inset placement type of the element with the specified \a index to \a placement.
4930
4931 \see InsetPlacement
4932*/
4934{
4935 if (elementAt(index))
4936 mInsetPlacement[index] = placement;
4937 else
4938 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4939}
4940
4941/*!
4942 If the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned, this function
4943 is used to set the alignment of the element with the specified \a index to \a alignment.
4944
4945 \a alignment is an or combination of the following alignment flags: Qt::AlignLeft,
4946 Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, Qt::AlignVCenter, Qt::AlignBottom. Any other
4947 alignment flags will be ignored.
4948*/
4950{
4951 if (elementAt(index))
4952 mInsetAlignment[index] = alignment;
4953 else
4954 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4955}
4956
4957/*!
4958 If the inset placement (\ref setInsetPlacement) is \ref ipFree, this function is used to set the
4959 position and size of the element with the specified \a index to \a rect.
4960
4961 \a rect is given in fractions of the whole inset layout rect. So an inset with rect (0, 0, 1, 1)
4962 will span the entire layout. An inset with rect (0.6, 0.1, 0.35, 0.35) will be in the top right
4963 corner of the layout, with 35% width and height of the parent layout.
4964
4965 Note that the minimum and maximum sizes of the embedded element (\ref
4966 QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize) are enforced.
4968void QCPLayoutInset::setInsetRect(int index, const QRectF &rect)
4969{
4970 if (elementAt(index))
4971 mInsetRect[index] = rect;
4972 else
4973 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4974}
4975
4976/* inherits documentation from base class */
4978{
4979 for (int i=0; i<mElements.size(); ++i)
4980 {
4981 QCPLayoutElement *el = mElements.at(i);
4983 QSize finalMinSize = getFinalMinimumOuterSize(el);
4984 QSize finalMaxSize = getFinalMaximumOuterSize(el);
4985 if (mInsetPlacement.at(i) == ipFree)
4986 {
4987 insetRect = QRect(int( rect().x()+rect().width()*mInsetRect.at(i).x() ),
4988 int( rect().y()+rect().height()*mInsetRect.at(i).y() ),
4989 int( rect().width()*mInsetRect.at(i).width() ),
4990 int( rect().height()*mInsetRect.at(i).height() ));
4991 if (insetRect.size().width() < finalMinSize.width())
4992 insetRect.setWidth(finalMinSize.width());
4993 if (insetRect.size().height() < finalMinSize.height())
4994 insetRect.setHeight(finalMinSize.height());
4995 if (insetRect.size().width() > finalMaxSize.width())
4996 insetRect.setWidth(finalMaxSize.width());
4997 if (insetRect.size().height() > finalMaxSize.height())
4998 insetRect.setHeight(finalMaxSize.height());
4999 } else if (mInsetPlacement.at(i) == ipBorderAligned)
5000 {
5001 insetRect.setSize(finalMinSize);
5002 Qt::Alignment al = mInsetAlignment.at(i);
5003 if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x());
5004 else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width());
5005 else insetRect.moveLeft(int( rect().x()+rect().width()*0.5-finalMinSize.width()*0.5 )); // default to Qt::AlignHCenter
5006 if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y());
5007 else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height());
5008 else insetRect.moveTop(int( rect().y()+rect().height()*0.5-finalMinSize.height()*0.5 )); // default to Qt::AlignVCenter
5009 }
5010 mElements.at(i)->setOuterRect(insetRect);
5011 }
5012}
5013
5014/* inherits documentation from base class */
5016{
5017 return mElements.size();
5018}
5019
5020/* inherits documentation from base class */
5022{
5023 if (index >= 0 && index < mElements.size())
5024 return mElements.at(index);
5025 else
5026 return nullptr;
5027}
5028
5029/* inherits documentation from base class */
5031{
5032 if (QCPLayoutElement *el = elementAt(index))
5033 {
5034 releaseElement(el);
5035 mElements.removeAt(index);
5036 mInsetPlacement.removeAt(index);
5037 mInsetAlignment.removeAt(index);
5038 mInsetRect.removeAt(index);
5039 return el;
5040 } else
5041 {
5042 qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
5043 return nullptr;
5044 }
5045}
5046
5047/* inherits documentation from base class */
5049{
5050 if (element)
5051 {
5052 for (int i=0; i<elementCount(); ++i)
5053 {
5054 if (elementAt(i) == element)
5055 {
5056 takeAt(i);
5057 return true;
5058 }
5059 }
5060 qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
5061 } else
5062 qDebug() << Q_FUNC_INFO << "Can't take nullptr element";
5063 return false;
5064}
5065
5066/*!
5067 The inset layout is sensitive to events only at areas where its (visible) child elements are
5068 sensitive. If the selectTest method of any of the child elements returns a positive number for \a
5069 pos, this method returns a value corresponding to 0.99 times the parent plot's selection
5070 tolerance. The inset layout is not selectable itself by default. So if \a onlySelectable is true,
5071 -1.0 is returned.
5072
5073 See \ref QCPLayerable::selectTest for a general explanation of this virtual method.
5074*/
5075double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
5076{
5077 Q_UNUSED(details)
5078 if (onlySelectable)
5079 return -1;
5080
5081 foreach (QCPLayoutElement *el, mElements)
5082 {
5083 // inset layout shall only return positive selectTest, if actually an inset object is at pos
5084 // else it would block the entire underlying QCPAxisRect with its surface.
5085 if (el->realVisibility() && el->selectTest(pos, onlySelectable) >= 0)
5086 return mParentPlot->selectionTolerance()*0.99;
5087 }
5088 return -1;
5089}
5090
5091/*!
5092 Adds the specified \a element to the layout as an inset aligned at the border (\ref
5093 setInsetAlignment is initialized with \ref ipBorderAligned). The alignment is set to \a
5094 alignment.
5095
5096 \a alignment is an or combination of the following alignment flags: Qt::AlignLeft,
5097 Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, Qt::AlignVCenter, Qt::AlignBottom. Any other
5098 alignment flags will be ignored.
5099
5100 \see addElement(QCPLayoutElement *element, const QRectF &rect)
5101*/
5103{
5104 if (element)
5105 {
5106 if (element->layout()) // remove from old layout first
5107 element->layout()->take(element);
5108 mElements.append(element);
5109 mInsetPlacement.append(ipBorderAligned);
5110 mInsetAlignment.append(alignment);
5111 mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
5112 adoptElement(element);
5113 } else
5114 qDebug() << Q_FUNC_INFO << "Can't add nullptr element";
5115}
5116
5117/*!
5118 Adds the specified \a element to the layout as an inset with free positioning/sizing (\ref
5119 setInsetAlignment is initialized with \ref ipFree). The position and size is set to \a
5120 rect.
5121
5122 \a rect is given in fractions of the whole inset layout rect. So an inset with rect (0, 0, 1, 1)
5123 will span the entire layout. An inset with rect (0.6, 0.1, 0.35, 0.35) will be in the top right
5124 corner of the layout, with 35% width and height of the parent layout.
5125
5126 \see addElement(QCPLayoutElement *element, Qt::Alignment alignment)
5127*/
5129{
5130 if (element)
5131 {
5132 if (element->layout()) // remove from old layout first
5133 element->layout()->take(element);
5134 mElements.append(element);
5135 mInsetPlacement.append(ipFree);
5136 mInsetAlignment.append(Qt::AlignRight|Qt::AlignTop);
5137 mInsetRect.append(rect);
5138 adoptElement(element);
5139 } else
5140 qDebug() << Q_FUNC_INFO << "Can't add nullptr element";
5141}
5142/* end of 'src/layout.cpp' */
5143
5144
5145/* including file 'src/lineending.cpp' */
5146/* modified 2021-03-29T02:30:44, size 11189 */
5147
5148////////////////////////////////////////////////////////////////////////////////////////////////////
5149//////////////////// QCPLineEnding
5150////////////////////////////////////////////////////////////////////////////////////////////////////
5151
5152/*! \class QCPLineEnding
5153 \brief Handles the different ending decorations for line-like items
5154
5155 \image html QCPLineEnding.png "The various ending styles currently supported"
5156
5157 For every ending a line-like item has, an instance of this class exists. For example, QCPItemLine
5158 has two endings which can be set with QCPItemLine::setHead and QCPItemLine::setTail.
5159
5160 The styles themselves are defined via the enum QCPLineEnding::EndingStyle. Most decorations can
5161 be modified regarding width and length, see \ref setWidth and \ref setLength. The direction of
5162 the ending decoration (e.g. direction an arrow is pointing) is controlled by the line-like item.
5163 For example, when both endings of a QCPItemLine are set to be arrows, they will point to opposite
5164 directions, e.g. "outward". This can be changed by \ref setInverted, which would make the
5165 respective arrow point inward.
5166
5167 Note that due to the overloaded QCPLineEnding constructor, you may directly specify a
5168 QCPLineEnding::EndingStyle where actually a QCPLineEnding is expected, e.g.
5169 \snippet documentation/doc-code-snippets/mainwindow.cpp qcplineending-sethead
5170*/
5171
5172/*!
5173 Creates a QCPLineEnding instance with default values (style \ref esNone).
5174*/
5176 mStyle(esNone),
5177 mWidth(8),
5178 mLength(10),
5179 mInverted(false)
5180{
5181}
5182
5183/*!
5184 Creates a QCPLineEnding instance with the specified values.
5185*/
5186QCPLineEnding::QCPLineEnding(QCPLineEnding::EndingStyle style, double width, double length, bool inverted) :
5187 mStyle(style),
5188 mWidth(width),
5189 mLength(length),
5190 mInverted(inverted)
5191{
5192}
5193
5194/*!
5195 Sets the style of the ending decoration.
5196*/
5198{
5199 mStyle = style;
5200}
5201
5202/*!
5203 Sets the width of the ending decoration, if the style supports it. On arrows, for example, the
5204 width defines the size perpendicular to the arrow's pointing direction.
5205
5206 \see setLength
5207*/
5208void QCPLineEnding::setWidth(double width)
5209{
5210 mWidth = width;
5211}
5212
5213/*!
5214 Sets the length of the ending decoration, if the style supports it. On arrows, for example, the
5215 length defines the size in pointing direction.
5216
5217 \see setWidth
5218*/
5219void QCPLineEnding::setLength(double length)
5220{
5221 mLength = length;
5222}
5223
5224/*!
5225 Sets whether the ending decoration shall be inverted. For example, an arrow decoration will point
5226 inward when \a inverted is set to true.
5227
5228 Note that also the \a width direction is inverted. For symmetrical ending styles like arrows or
5229 discs, this doesn't make a difference. However, asymmetric styles like \ref esHalfBar are
5230 affected by it, which can be used to control to which side the half bar points to.
5231*/
5233{
5234 mInverted = inverted;
5235}
5236
5237/*! \internal
5238
5239 Returns the maximum pixel radius the ending decoration might cover, starting from the position
5240 the decoration is drawn at (typically a line ending/\ref QCPItemPosition of an item).
5241
5242 This is relevant for clipping. Only omit painting of the decoration when the position where the
5243 decoration is supposed to be drawn is farther away from the clipping rect than the returned
5244 distance.
5245*/
5247{
5248 switch (mStyle)
5249 {
5250 case esNone:
5251 return 0;
5252
5253 case esFlatArrow:
5254 case esSpikeArrow:
5255 case esLineArrow:
5256 case esSkewedBar:
5257 return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length
5258
5259 case esDisc:
5260 case esSquare:
5261 case esDiamond:
5262 case esBar:
5263 case esHalfBar:
5264 return mWidth*1.42; // items that only have a width -> width*sqrt(2)
5265
5266 }
5267 return 0;
5268}
5269
5270/*!
5271 Starting from the origin of this line ending (which is style specific), returns the length
5272 covered by the line ending symbol, in backward direction.
5273
5274 For example, the \ref esSpikeArrow has a shorter real length than a \ref esFlatArrow, even if
5275 both have the same \ref setLength value, because the spike arrow has an inward curved back, which
5276 reduces the length along its center axis (the drawing origin for arrows is at the tip).
5277
5278 This function is used for precise, style specific placement of line endings, for example in
5279 QCPAxes.
5280*/
5282{
5283 switch (mStyle)
5284 {
5285 case esNone:
5286 case esLineArrow:
5287 case esSkewedBar:
5288 case esBar:
5289 case esHalfBar:
5290 return 0;
5291
5292 case esFlatArrow:
5293 return mLength;
5294
5295 case esDisc:
5296 case esSquare:
5297 case esDiamond:
5298 return mWidth*0.5;
5299
5300 case esSpikeArrow:
5301 return mLength*0.8;
5302 }
5303 return 0;
5304}
5305
5306/*! \internal
5307
5308 Draws the line ending with the specified \a painter at the position \a pos. The direction of the
5309 line ending is controlled with \a dir.
5310*/
5311void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, const QCPVector2D &dir) const
5312{
5313 if (mStyle == esNone)
5314 return;
5315
5316 QCPVector2D lengthVec = dir.normalized() * mLength*(mInverted ? -1 : 1);
5317 if (lengthVec.isNull())
5318 lengthVec = QCPVector2D(1, 0);
5319 QCPVector2D widthVec = dir.normalized().perpendicular() * mWidth*0.5*(mInverted ? -1 : 1);
5320
5321 QPen penBackup = painter->pen();
5322 QBrush brushBackup = painter->brush();
5323 QPen miterPen = penBackup;
5324 miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey
5325 QBrush brush(painter->pen().color(), Qt::SolidPattern);
5326 switch (mStyle)
5327 {
5328 case esNone: break;
5329 case esFlatArrow:
5330 {
5331 QPointF points[3] = {pos.toPointF(),
5332 (pos-lengthVec+widthVec).toPointF(),
5333 (pos-lengthVec-widthVec).toPointF()
5334 };
5335 painter->setPen(miterPen);
5336 painter->setBrush(brush);
5337 painter->drawConvexPolygon(points, 3);
5338 painter->setBrush(brushBackup);
5339 painter->setPen(penBackup);
5340 break;
5341 }
5342 case esSpikeArrow:
5343 {
5344 QPointF points[4] = {pos.toPointF(),
5345 (pos-lengthVec+widthVec).toPointF(),
5346 (pos-lengthVec*0.8).toPointF(),
5347 (pos-lengthVec-widthVec).toPointF()
5348 };
5349 painter->setPen(miterPen);
5350 painter->setBrush(brush);
5351 painter->drawConvexPolygon(points, 4);
5352 painter->setBrush(brushBackup);
5353 painter->setPen(penBackup);
5354 break;
5355 }
5356 case esLineArrow:
5357 {
5358 QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(),
5359 pos.toPointF(),
5360 (pos-lengthVec-widthVec).toPointF()
5361 };
5362 painter->setPen(miterPen);
5363 painter->drawPolyline(points, 3);
5364 painter->setPen(penBackup);
5365 break;
5366 }
5367 case esDisc:
5368 {
5369 painter->setBrush(brush);
5370 painter->drawEllipse(pos.toPointF(), mWidth*0.5, mWidth*0.5);
5371 painter->setBrush(brushBackup);
5372 break;
5373 }
5374 case esSquare:
5375 {
5376 QCPVector2D widthVecPerp = widthVec.perpendicular();
5377 QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(),
5378 (pos-widthVecPerp-widthVec).toPointF(),
5379 (pos+widthVecPerp-widthVec).toPointF(),
5380 (pos+widthVecPerp+widthVec).toPointF()
5381 };
5382 painter->setPen(miterPen);
5383 painter->setBrush(brush);
5384 painter->drawConvexPolygon(points, 4);
5385 painter->setBrush(brushBackup);
5386 painter->setPen(penBackup);
5387 break;
5388 }
5389 case esDiamond:
5390 {
5391 QCPVector2D widthVecPerp = widthVec.perpendicular();
5392 QPointF points[4] = {(pos-widthVecPerp).toPointF(),
5393 (pos-widthVec).toPointF(),
5394 (pos+widthVecPerp).toPointF(),
5395 (pos+widthVec).toPointF()
5396 };
5397 painter->setPen(miterPen);
5398 painter->setBrush(brush);
5399 painter->drawConvexPolygon(points, 4);
5400 painter->setBrush(brushBackup);
5401 painter->setPen(penBackup);
5402 break;
5403 }
5404 case esBar:
5405 {
5406 painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF());
5407 break;
5408 }
5409 case esHalfBar:
5410 {
5411 painter->drawLine((pos+widthVec).toPointF(), pos.toPointF());
5412 break;
5413 }
5414 case esSkewedBar:
5415 {
5416 QCPVector2D shift;
5417 if (!qFuzzyIsNull(painter->pen().widthF()) || painter->modes().testFlag(QCPPainter::pmNonCosmetic))
5418 shift = dir.normalized()*qMax(qreal(1.0), painter->pen().widthF())*qreal(0.5);
5419 // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
5420 painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)+shift).toPointF(),
5421 (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)+shift).toPointF());
5422 break;
5423 }
5424 }
5425}
5426
5427/*! \internal
5428 \overload
5429
5430 Draws the line ending. The direction is controlled with the \a angle parameter in radians.
5431*/
5432void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, double angle) const
5433{
5434 draw(painter, pos, QCPVector2D(qCos(angle), qSin(angle)));
5435}
5436/* end of 'src/lineending.cpp' */
5437
5438
5439/* including file 'src/axis/labelpainter.cpp' */
5440/* modified 2021-03-29T02:30:44, size 27296 */
5441
5442
5443////////////////////////////////////////////////////////////////////////////////////////////////////
5444//////////////////// QCPLabelPainterPrivate
5445////////////////////////////////////////////////////////////////////////////////////////////////////
5446
5447/*! \class QCPLabelPainterPrivate
5448
5449 \internal
5450 \brief (Private)
5451
5452 This is a private class and not part of the public QCustomPlot interface.
5453
5454*/
5455
5456const QChar QCPLabelPainterPrivate::SymbolDot(183);
5457const QChar QCPLabelPainterPrivate::SymbolCross(215);
5458
5459/*!
5460 Constructs a QCPLabelPainterPrivate instance. Make sure to not create a new
5461 instance on every redraw, to utilize the caching mechanisms.
5462
5463 the \a parentPlot does not take ownership of the label painter. Make sure
5464 to delete it appropriately.
5465*/
5467 mAnchorMode(amRectangular),
5468 mAnchorSide(asLeft),
5469 mAnchorReferenceType(artNormal),
5470 mColor(Qt::black),
5471 mPadding(0),
5472 mRotation(0),
5473 mSubstituteExponent(true),
5474 mMultiplicationSymbol(QChar(215)),
5475 mAbbreviateDecimalPowers(false),
5476 mParentPlot(parentPlot),
5477 mLabelCache(16)
5478{
5479 analyzeFontMetrics();
5480}
5481
5482QCPLabelPainterPrivate::~QCPLabelPainterPrivate()
5483{
5484}
5485
5486void QCPLabelPainterPrivate::setAnchorSide(AnchorSide side)
5487{
5488 mAnchorSide = side;
5489}
5490
5491void QCPLabelPainterPrivate::setAnchorMode(AnchorMode mode)
5492{
5493 mAnchorMode = mode;
5494}
5495
5496void QCPLabelPainterPrivate::setAnchorReference(const QPointF &pixelPoint)
5497{
5498 mAnchorReference = pixelPoint;
5499}
5500
5501void QCPLabelPainterPrivate::setAnchorReferenceType(AnchorReferenceType type)
5502{
5503 mAnchorReferenceType = type;
5504}
5505
5506void QCPLabelPainterPrivate::setFont(const QFont &font)
5507{
5508 if (mFont != font)
5509 {
5510 mFont = font;
5511 analyzeFontMetrics();
5512 }
5513}
5514
5515void QCPLabelPainterPrivate::setColor(const QColor &color)
5516{
5517 mColor = color;
5518}
5519
5520void QCPLabelPainterPrivate::setPadding(int padding)
5521{
5522 mPadding = padding;
5523}
5524
5525void QCPLabelPainterPrivate::setRotation(double rotation)
5526{
5527 mRotation = qBound(-90.0, rotation, 90.0);
5528}
5529
5530void QCPLabelPainterPrivate::setSubstituteExponent(bool enabled)
5531{
5532 mSubstituteExponent = enabled;
5533}
5534
5535void QCPLabelPainterPrivate::setMultiplicationSymbol(QChar symbol)
5536{
5537 mMultiplicationSymbol = symbol;
5538}
5539
5540void QCPLabelPainterPrivate::setAbbreviateDecimalPowers(bool enabled)
5541{
5542 mAbbreviateDecimalPowers = enabled;
5543}
5544
5545void QCPLabelPainterPrivate::setCacheSize(int labelCount)
5546{
5547 mLabelCache.setMaxCost(labelCount);
5548}
5549
5550int QCPLabelPainterPrivate::cacheSize() const
5551{
5552 return mLabelCache.maxCost();
5553}
5554
5555void QCPLabelPainterPrivate::drawTickLabel(QCPPainter *painter, const QPointF &tickPos, const QString &text)
5556{
5557 double realRotation = mRotation;
5558
5559 AnchorSide realSide = mAnchorSide;
5560 // for circular axes, the anchor side is determined depending on the quadrant of tickPos with respect to mCircularReference
5561 if (mAnchorMode == amSkewedUpright)
5562 {
5563 realSide = skewedAnchorSide(tickPos, 0.2, 0.3);
5564 } else if (mAnchorMode == amSkewedRotated) // in this mode every label is individually rotated to match circle tangent
5565 {
5566 realSide = skewedAnchorSide(tickPos, 0, 0);
5567 realRotation += QCPVector2D(tickPos-mAnchorReference).angle()/M_PI*180.0;
5568 if (realRotation > 90) realRotation -= 180;
5569 else if (realRotation < -90) realRotation += 180;
5570 }
5571
5572 realSide = rotationCorrectedSide(realSide, realRotation); // rotation angles may change the true anchor side of the label
5573 drawLabelMaybeCached(painter, mFont, mColor, getAnchorPos(tickPos), realSide, realRotation, text);
5574}
5575
5576/*! \internal
5577
5578 Returns the size ("margin" in QCPAxisRect context, so measured perpendicular to the axis backbone
5579 direction) needed to fit the axis.
5580*/
5581/* TODO: needed?
5582int QCPLabelPainterPrivate::size() const
5583{
5584 int result = 0;
5585 // get length of tick marks pointing outwards:
5586 if (!tickPositions.isEmpty())
5587 result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
5588
5589 // calculate size of tick labels:
5590 if (tickLabelSide == QCPAxis::lsOutside)
5591 {
5592 QSize tickLabelsSize(0, 0);
5593 if (!tickLabels.isEmpty())
5594 {
5595 for (int i=0; i<tickLabels.size(); ++i)
5596 getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);
5597 result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
5598 result += tickLabelPadding;
5599 }
5600 }
5601
5602 // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
5603 if (!label.isEmpty())
5604 {
5605 QFontMetrics fontMetrics(labelFont);
5606 QRect bounds;
5607 bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
5608 result += bounds.height() + labelPadding;
5609 }
5610
5611