KChart

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

KDE's Doxygen guidelines are available online.