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 
20 using namespace KChart;
21 
22 #define d d_func()
23 
24 AbstractCoordinatePlane::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 
37 AbstractCoordinatePlane::AbstractCoordinatePlane ( KChart::Chart* parent )
38  : AbstractArea ( new Private() )
39 {
40  d->parent = parent;
41  d->init();
42 }
43 
44 AbstractCoordinatePlane::~AbstractCoordinatePlane()
45 {
47 }
48 
49 void AbstractCoordinatePlane::init()
50 {
51  d->initialize(); // virtual method to init the correct grid: cartesian, polar, ...
52  connect( this, SIGNAL(internal_geometryChanged(QRect,QRect)),
53  this, SIGNAL(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 );
64  diagram->setCoordinatePlane( this );
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 ) {
80  AbstractDiagram* oldDiagram = oldDiagram_;
81  if ( d->diagrams.count() ) {
82  if ( ! oldDiagram ) {
83  oldDiagram = d->diagrams.first();
84  if ( oldDiagram == diagram )
85  return;
86  }
87  takeDiagram( oldDiagram );
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()) );
108  layoutDiagrams();
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 
177 const KChart::Chart* KChart::AbstractCoordinatePlane::parent() const
178 {
179  return d->parent;
180 }
181 
182 KChart::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 */
196 {
197  return Qt::Vertical | Qt::Horizontal;
198 }
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 
284 void 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 
324 void 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 
338 void 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 
391 void 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)
408 const
409 #endif
411 {
412  return d->isVisiblePoint( this, point );
413 }
414 
415 AbstractCoordinatePlane* KChart::AbstractCoordinatePlane::sharedAxisMasterPlane( QPainter* p )
416 {
417  Q_UNUSED( p );
418  return this;
419 }
420 
421 #if !defined(QT_NO_DEBUG_STREAM)
422 
423 QDebug 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
void relayout()
Calling relayout() on the plane triggers the global KChart::Chart::slotRelayout()
An area in the chart with a background, a frame, etc.
void setParent(QWidget *parent)
virtual void mouseReleaseEvent(QMouseEvent *event) override
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
Q_EMITQ_EMIT
QSize minimumSize() const override
pure virtual in QLayoutItem
void update()
Class only listed here to document inheritance of some KChart classes.
Qt::Orientations expandingDirections() const override
pure virtual in QLayoutItem
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane,...
void setParent(Chart *parent)
Called internally by KChart::Chart.
void push_back(const T &value)
virtual void setCoordinatePlane(AbstractCoordinatePlane *plane)
Set the coordinate plane associated with the diagram.
void setReferenceCoordinatePlane(AbstractCoordinatePlane *plane)
Set another coordinate plane to be used as the reference plane for this one.
LeftButton
virtual void mousePressEvent(QMouseEvent *event) override
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void hide()
virtual void replaceDiagram(AbstractDiagram *diagram, AbstractDiagram *oldDiagram=nullptr)
Replaces the old diagram, or appends the diagram, it there is none yet.
ZoomParameters stores the center and the factor of zooming internally.
AbstractCoordinatePlane * referenceCoordinatePlane() const
There are two ways, in which planes can be caused to interact, in where they are put layouting wise: ...
typedef Orientations
static QString granularitySequenceToString(GranularitySequence sequence)
Converts the specified granularity sequence enum to a string representation.
Definition: KChartEnums.h:95
QueuedConnection
QRect geometry() const override
pure virtual in QLayoutItem
virtual void addDiagram(AbstractDiagram *diagram)
Adds a diagram to this coordinate plane.
void setRubberBandZoomingEnabled(bool enable)
Enables or disables zooming with a rubber band using the mouse.
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
virtual void mouseDoubleClickEvent(QMouseEvent *event) override
A set of attributes controlling the appearance of grids.
virtual void takeDiagram(AbstractDiagram *diagram)
Removes the diagram from the plane, without deleting it.
void geometryChanged(QRect, QRect)
Emitted after the geometry of the Coordinate Plane has been changed.
void destroyedCoordinatePlane(KChart::AbstractCoordinatePlane *)
Emitted when this coordinate plane is destroyed.
DataDimensionsList gridDimensionsList()
Returns the dimensions used for drawing the grid lines.
bool isEmpty() const override
pure virtual in QLayoutItem
void layoutPlanes()
Calling layoutPlanes() on the plane triggers the global KChart::Chart::slotLayoutPlanes()
virtual void layoutDiagrams()=0
Distribute the available space among the diagrams and axes.
QSize sizeHint() const override
pure virtual in QLayoutItem
void update()
Calling update() on the plane triggers the global KChart::Chart::update()
AbstractDiagram defines the interface for diagram classes.
void setCornerSpacersEnabled(bool enable)
Enables or disables the use of spacers in the plane corners.
QSize maximumSize() const override
pure virtual in QLayoutItem
bool isVisiblePoint(const QPointF &point) const
Tests, if a point is visible on the coordinate plane.
Helper class for one dimension of data, e.g.
A chart with one or more diagrams.
Definition: KChartChart.h:84
QObject * parent() const const
void setGeometry(const QRect &r) override
pure virtual in QLayoutItem
QRect normalized() const const
void setGridNeedsRecalculate()
Used by the chart to clear the cached grid data.
virtual void mouseMoveEvent(QMouseEvent *event) override
void setGlobalGridAttributes(const GridAttributes &)
Set the grid attributes to be used by this coordinate plane.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Jun 5 2023 03:58:05 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.