Kstars

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