KChart

KChartAbstractCoordinatePlane.cpp
1/*
2 * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
3 *
4 * This file is part of the KD Chart library.
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9#include "KChartAbstractCoordinatePlane.h"
10#include "KChartAbstractCoordinatePlane_p.h"
11
12#include "KChartChart.h"
13#include "KChartGridAttributes.h"
14
15#include <QGridLayout>
16#include <QRubberBand>
17#include <QMouseEvent>
18#include <qmath.h>
19
20using namespace KChart;
21
22#define d d_func()
23
24AbstractCoordinatePlane::Private::Private()
25 : AbstractArea::Private()
26 , parent( nullptr )
27 , grid( nullptr )
28 , referenceCoordinatePlane( nullptr )
29 , enableCornerSpacers( true )
30 , enableRubberBandZooming( false )
31 , rubberBand( nullptr )
32{
33 // this block left empty intentionally
34}
35
36
37AbstractCoordinatePlane::AbstractCoordinatePlane ( KChart::Chart* parent )
38 : AbstractArea ( new Private() )
39{
40 d->parent = parent;
41 d->init();
42}
43
44AbstractCoordinatePlane::~AbstractCoordinatePlane()
45{
47}
48
49void AbstractCoordinatePlane::init()
50{
51 d->initialize(); // virtual method to init the correct grid: cartesian, polar, ...
52 connect( this, SIGNAL(internal_geometryChanged(QRect,QRect)),
55}
56
58{
59 // diagrams are invisible and paint through their paint() method
60 diagram->hide();
61
62 d->diagrams.append( diagram );
63 diagram->setParent( d->parent );
66 layoutPlanes(); // there might be new axes, etc
67 connect( diagram, SIGNAL(modelsChanged()), this, SLOT(layoutPlanes()) );
68 connect( diagram, SIGNAL(modelDataChanged()), this, SLOT(update()) );
69 connect( diagram, SIGNAL(modelDataChanged()), this, SLOT(relayout()) );
70 connect( this, SIGNAL(boundariesChanged()), diagram, SIGNAL(boundariesChanged()) );
71
72 update();
73 Q_EMIT boundariesChanged();
74}
75
76/*virtual*/
78{
79 if ( diagram && oldDiagram_ != diagram ) {
81 if ( d->diagrams.count() ) {
82 if ( ! oldDiagram ) {
83 oldDiagram = d->diagrams.first();
84 if ( oldDiagram == diagram )
85 return;
86 }
88 }
89 delete oldDiagram;
92 layoutPlanes(); // there might be new axes, etc
93 update();
94 }
95}
96
97/*virtual*/
99{
100 const int idx = d->diagrams.indexOf( diagram );
101 if ( idx != -1 ) {
102 d->diagrams.removeAt( idx );
103 diagram->setParent( nullptr );
104 diagram->setCoordinatePlane( nullptr );
105 disconnect( diagram, SIGNAL(modelsChanged()), this, SLOT(layoutPlanes()) );
106 disconnect( diagram, SIGNAL(modelDataChanged()), this, SLOT(update()) );
107 disconnect( diagram, SIGNAL(modelDataChanged()), this, SLOT(relayout()) );
109 update();
110 }
111}
112
113
115{
116 if ( d->diagrams.isEmpty() )
117 {
118 return nullptr;
119 } else {
120 return d->diagrams.first();
121 }
122}
123
125{
126 return d->diagrams;
127}
128
130{
132#ifndef QT_NO_STL
133 qCopy( d->diagrams.begin(), d->diagrams.end(), std::back_inserter( list ) );
134#else
135 for ( AbstractDiagram * a : d->diagrams )
136 list.push_back( a );
137#endif
138 return list;
139}
140
142{
143 d->gridAttributes = a;
144 update();
145}
146
148{
149 return d->gridAttributes;
150}
151
153{
154 return d->grid->updateData( this );
155}
156
158{
159 d->grid->setNeedRecalculate();
160}
161
163{
164 d->referenceCoordinatePlane = plane;
165}
166
168{
169 return d->referenceCoordinatePlane;
170}
171
173{
174 d->parent = parent;
175}
176
177const KChart::Chart* KChart::AbstractCoordinatePlane::parent() const
178{
179 return d->parent;
180}
181
182KChart::Chart* KChart::AbstractCoordinatePlane::parent()
183{
184 return d->parent;
185}
186
187/* pure virtual in QLayoutItem */
189{
190 return false; // never empty!
191 // coordinate planes with no associated diagrams
192 // are showing a default grid of ()1..10, 1..10) stepWidth 1
193}
194/* pure virtual in QLayoutItem */
199/* pure virtual in QLayoutItem */
201{
202 // No maximum size set. Especially not parent()->size(), we are not layouting
203 // to the parent widget's size when using Chart::paint()!
204 return QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX);
205}
206/* pure virtual in QLayoutItem */
208{
209 return QSize(60, 60); // this default can be overwritten by derived classes
210}
211/* pure virtual in QLayoutItem */
213{
214 // we return our maxiumu (which is the full size of the Chart)
215 // even if we know the plane will be smaller
216 return maximumSize();
217}
218/* pure virtual in QLayoutItem */
220{
221 if ( d->geometry != r ) {
222 // inform the outside word by Signal geometryChanged()
223 // via a queued connection to internal_geometryChanged()
224 Q_EMIT internal_geometryChanged( d->geometry, r );
225
226 d->geometry = r;
227 // Note: We do *not* call update() here
228 // because it would invoke KChart::update() recursively.
229 }
230}
231/* pure virtual in QLayoutItem */
233{
234 return d->geometry;
235}
236
238{
239 //qDebug("KChart::AbstractCoordinatePlane::update() called");
240 Q_EMIT needUpdate();
241}
242
244{
245 //qDebug("KChart::AbstractCoordinatePlane::relayout() called");
246 Q_EMIT needRelayout();
247}
248
250{
251 //qDebug("KChart::AbstractCoordinatePlane::relayout() called");
252 Q_EMIT needLayoutPlanes();
253}
254
256{
257 d->enableRubberBandZooming = enable;
258
259 if ( !enable && d->rubberBand != nullptr )
260 {
261 delete d->rubberBand;
262 d->rubberBand = nullptr;
263 }
264}
265
267{
268 return d->enableRubberBandZooming;
269}
270
272{
273 if ( d->enableCornerSpacers == enable ) return;
274
275 d->enableCornerSpacers = enable;
276 Q_EMIT needRelayout();
277}
278
280{
281 return d->enableCornerSpacers;
282}
283
284void KChart::AbstractCoordinatePlane::mousePressEvent( QMouseEvent* event )
285{
286 if ( event->button() == Qt::LeftButton )
287 {
288 if ( d->enableRubberBandZooming && d->rubberBand == nullptr )
289 d->rubberBand = new QRubberBand( QRubberBand::Rectangle, qobject_cast< QWidget* >( parent() ) );
290
291 if ( d->rubberBand != nullptr )
292 {
293 d->rubberBandOrigin = event->pos();
294 d->rubberBand->setGeometry( QRect( event->pos(), QSize() ) );
295 d->rubberBand->show();
296
297 event->accept();
298 }
299 }
300 else if ( event->button() == Qt::RightButton )
301 {
302 if ( d->enableRubberBandZooming && !d->rubberBandZoomConfigHistory.isEmpty() )
303 {
304 // restore the last config from the stack
305 ZoomParameters config = d->rubberBandZoomConfigHistory.pop();
306 setZoomFactorX( config.xFactor );
307 setZoomFactorY( config.yFactor );
308 setZoomCenter( config.center() );
309
310 QWidget* const p = qobject_cast< QWidget* >( parent() );
311 if ( p != nullptr )
312 p->update();
313
314 event->accept();
315 }
316 }
317
318 for ( AbstractDiagram * a : qAsConst(d->diagrams) )
319 {
320 a->mousePressEvent( event );
321 }
322}
323
324void KChart::AbstractCoordinatePlane::mouseDoubleClickEvent( QMouseEvent* event )
325{
326 if ( event->button() == Qt::RightButton )
327 {
328 // otherwise the second click gets lost
329 // which is pretty annoying when zooming out fast
330 mousePressEvent( event );
331 }
332 for ( AbstractDiagram * a : qAsConst(d->diagrams) )
333 {
334 a->mouseDoubleClickEvent( event );
335 }
336}
337
338void KChart::AbstractCoordinatePlane::mouseReleaseEvent( QMouseEvent* event )
339{
340 if ( d->rubberBand != nullptr )
341 {
342 // save the old config on the stack
343 d->rubberBandZoomConfigHistory.push( ZoomParameters( zoomFactorX(), zoomFactorY(), zoomCenter() ) );
344
345 // this is the height/width of the rubber band in pixel space
346 const qreal rubberWidth = static_cast< qreal >( d->rubberBand->width() );
347 const qreal rubberHeight = static_cast< qreal >( d->rubberBand->height() );
348
349 if ( rubberWidth > 0.0 && rubberHeight > 0.0 )
350 {
351 // this is the center of the rubber band in pixel space
352 const qreal centerX = qFloor( d->rubberBand->geometry().width() / 2.0 + d->rubberBand->geometry().x() );
353 const qreal centerY = qCeil( d->rubberBand->geometry().height() / 2.0 + d->rubberBand->geometry().y() );
354
355 const qreal rubberCenterX = static_cast< qreal >( centerX - geometry().x() );
356 const qreal rubberCenterY = static_cast< qreal >( centerY - geometry().y() );
357
358 // this is the height/width of the plane in pixel space
359 const qreal myWidth = static_cast< qreal >( geometry().width() );
360 const qreal myHeight = static_cast< qreal >( geometry().height() );
361
362 // this describes the new center of zooming, relative to the plane pixel space
363 const qreal newCenterX = rubberCenterX / myWidth / zoomFactorX() + zoomCenter().x() - 0.5 / zoomFactorX();
364 const qreal newCenterY = rubberCenterY / myHeight / zoomFactorY() + zoomCenter().y() - 0.5 / zoomFactorY();
365
366 // this will be the new zoom factor
367 const qreal newZoomFactorX = zoomFactorX() * myWidth / rubberWidth;
368 const qreal newZoomFactorY = zoomFactorY() * myHeight / rubberHeight;
369
370 // and this the new center
371 const QPointF newZoomCenter( newCenterX, newCenterY );
372
373 setZoomFactorX( newZoomFactorX );
374 setZoomFactorY( newZoomFactorY );
375 setZoomCenter( newZoomCenter );
376 }
377
378 d->rubberBand->parentWidget()->update();
379 delete d->rubberBand;
380 d->rubberBand = nullptr;
381
382 event->accept();
383 }
384
385 for ( AbstractDiagram * a : qAsConst(d->diagrams) )
386 {
387 a->mouseReleaseEvent( event );
388 }
389}
390
391void KChart::AbstractCoordinatePlane::mouseMoveEvent( QMouseEvent* event )
392{
393 if ( d->rubberBand != nullptr )
394 {
395 const QRect normalized = QRect( d->rubberBandOrigin, event->pos() ).normalized();
396 d->rubberBand->setGeometry( normalized & geometry() );
397
398 event->accept();
399 }
400
401 for ( AbstractDiagram * a : qAsConst(d->diagrams) )
402 {
403 a->mouseMoveEvent( event );
404 }
405}
406
407#if defined(Q_COMPILER_MANGLES_RETURN_TYPE)
408const
409#endif
411{
412 return d->isVisiblePoint( this, point );
413}
414
415AbstractCoordinatePlane* KChart::AbstractCoordinatePlane::sharedAxisMasterPlane( QPainter* p )
416{
417 Q_UNUSED( p );
418 return this;
419}
420
421#if !defined(QT_NO_DEBUG_STREAM)
422
423QDebug KChart::operator<<( QDebug stream, const DataDimension& r )
424{
425 stream << "DataDimension("
426 << " start=" << r.start
427 << " end=" << r.end
428 << " sequence=" << KChartEnums::granularitySequenceToString( r.sequence )
429 << " isCalculated=" << r.isCalculated
430 << " calcMode=" << ( r.calcMode == AbstractCoordinatePlane::Logarithmic ? "Logarithmic" : "Linear" )
431 << " stepWidth=" << r.stepWidth
432 << " subStepWidth=" << r.subStepWidth
433 << " )";
434 return stream;
435}
436#endif
437
438#undef d
static QString granularitySequenceToString(GranularitySequence sequence)
Converts the specified granularity sequence enum to a string representation.
Definition KChartEnums.h:95
An area in the chart with a background, a frame, etc.
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane,...
QSize minimumSize() const override
pure virtual in QLayoutItem
void geometryChanged(QRect, QRect)
Emitted after the geometry of the Coordinate Plane has been changed.
void layoutPlanes()
Calling layoutPlanes() on the plane triggers the global KChart::Chart::slotLayoutPlanes()
void setReferenceCoordinatePlane(AbstractCoordinatePlane *plane)
Set another coordinate plane to be used as the reference plane for this one.
virtual void addDiagram(AbstractDiagram *diagram)
Adds a diagram to this coordinate plane.
QSize sizeHint() const override
pure virtual in QLayoutItem
void setCornerSpacersEnabled(bool enable)
Enables or disables the use of spacers in the plane corners.
void setParent(Chart *parent)
Called internally by KChart::Chart.
virtual void replaceDiagram(AbstractDiagram *diagram, AbstractDiagram *oldDiagram=nullptr)
Replaces the old diagram, or appends the diagram, it there is none yet.
void setGeometry(const QRect &r) override
pure virtual in QLayoutItem
virtual void layoutDiagrams()=0
Distribute the available space among the diagrams and axes.
void setRubberBandZoomingEnabled(bool enable)
Enables or disables zooming with a rubber band using the mouse.
QRect geometry() const override
pure virtual in QLayoutItem
void update()
Calling update() on the plane triggers the global KChart::Chart::update()
void relayout()
Calling relayout() on the plane triggers the global KChart::Chart::slotRelayout()
void setGridNeedsRecalculate()
Used by the chart to clear the cached grid data.
QSize maximumSize() const override
pure virtual in QLayoutItem
void setGlobalGridAttributes(const GridAttributes &)
Set the grid attributes to be used by this coordinate plane.
Qt::Orientations expandingDirections() const override
pure virtual in QLayoutItem
bool isEmpty() const override
pure virtual in QLayoutItem
void destroyedCoordinatePlane(KChart::AbstractCoordinatePlane *)
Emitted when this coordinate plane is destroyed.
virtual void takeDiagram(AbstractDiagram *diagram)
Removes the diagram from the plane, without deleting it.
AbstractCoordinatePlane * referenceCoordinatePlane() const
There are two ways, in which planes can be caused to interact, in where they are put layouting wise: ...
bool isVisiblePoint(const QPointF &point) const
Tests, if a point is visible on the coordinate plane.
DataDimensionsList gridDimensionsList()
Returns the dimensions used for drawing the grid lines.
AbstractDiagram defines the interface for diagram classes.
virtual void setCoordinatePlane(AbstractCoordinatePlane *plane)
Set the coordinate plane associated with the diagram.
A chart with one or more diagrams.
Definition KChartChart.h:85
Helper class for one dimension of data, e.g.
A set of attributes controlling the appearance of grids.
ZoomParameters stores the center and the factor of zooming internally.
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
void push_back(parameter_type value)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
T qobject_cast(QObject *object)
QRect normalized() const const
QueuedConnection
LeftButton
typedef Orientations
void hide()
void setParent(QWidget *parent)
void update()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:14:24 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.