File indexing completed on 2024-05-12 15:54:11
0001 /* 0002 * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. 0003 * 0004 * This file is part of the KD Chart library. 0005 * 0006 * This program is free software; you can redistribute it and/or 0007 * modify it under the terms of the GNU General Public License as 0008 * published by the Free Software Foundation; either version 2 of 0009 * the License, or (at your option) any later version. 0010 * 0011 * This program is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 * GNU General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU General Public License 0017 * along with this program. If not, see <https://www.gnu.org/licenses/>. 0018 */ 0019 0020 #include "KChartAbstractCoordinatePlane.h" 0021 #include "KChartAbstractCoordinatePlane_p.h" 0022 0023 #include "KChartChart.h" 0024 #include "KChartGridAttributes.h" 0025 0026 #include <QGridLayout> 0027 #include <QRubberBand> 0028 #include <QMouseEvent> 0029 #include <qmath.h> 0030 0031 using namespace KChart; 0032 0033 #define d d_func() 0034 0035 AbstractCoordinatePlane::Private::Private() 0036 : AbstractArea::Private() 0037 , parent( nullptr ) 0038 , grid( nullptr ) 0039 , referenceCoordinatePlane( nullptr ) 0040 , enableCornerSpacers( true ) 0041 , enableRubberBandZooming( false ) 0042 , rubberBand( nullptr ) 0043 { 0044 // this bloc left empty intentionally 0045 } 0046 0047 0048 AbstractCoordinatePlane::AbstractCoordinatePlane ( KChart::Chart* parent ) 0049 : AbstractArea ( new Private() ) 0050 { 0051 d->parent = parent; 0052 d->init(); 0053 } 0054 0055 AbstractCoordinatePlane::~AbstractCoordinatePlane() 0056 { 0057 Q_EMIT destroyedCoordinatePlane( this ); 0058 } 0059 0060 void AbstractCoordinatePlane::init() 0061 { 0062 d->initialize(); // virtual method to init the correct grid: cartesian, polar, ... 0063 connect( this, SIGNAL(internal_geometryChanged(QRect,QRect)), 0064 this, SIGNAL(geometryChanged(QRect,QRect)), 0065 Qt::QueuedConnection ); 0066 } 0067 0068 void AbstractCoordinatePlane::addDiagram ( AbstractDiagram* diagram ) 0069 { 0070 // diagrams are invisible and paint through their paint() method 0071 diagram->hide(); 0072 0073 d->diagrams.append( diagram ); 0074 diagram->setParent( d->parent ); 0075 diagram->setCoordinatePlane( this ); 0076 layoutDiagrams(); 0077 layoutPlanes(); // there might be new axes, etc 0078 connect( diagram, SIGNAL(modelsChanged()), this, SLOT(layoutPlanes()) ); 0079 connect( diagram, SIGNAL(modelDataChanged()), this, SLOT(update()) ); 0080 connect( diagram, SIGNAL(modelDataChanged()), this, SLOT(relayout()) ); 0081 connect( this, SIGNAL(boundariesChanged()), diagram, SIGNAL(boundariesChanged()) ); 0082 0083 update(); 0084 Q_EMIT boundariesChanged(); 0085 } 0086 0087 /*virtual*/ 0088 void AbstractCoordinatePlane::replaceDiagram ( AbstractDiagram* diagram, AbstractDiagram* oldDiagram_ ) 0089 { 0090 if ( diagram && oldDiagram_ != diagram ) { 0091 AbstractDiagram* oldDiagram = oldDiagram_; 0092 if ( d->diagrams.count() ) { 0093 if ( ! oldDiagram ) { 0094 oldDiagram = d->diagrams.first(); 0095 if ( oldDiagram == diagram ) 0096 return; 0097 } 0098 takeDiagram( oldDiagram ); 0099 } 0100 delete oldDiagram; 0101 addDiagram( diagram ); 0102 layoutDiagrams(); 0103 layoutPlanes(); // there might be new axes, etc 0104 update(); 0105 } 0106 } 0107 0108 /*virtual*/ 0109 void AbstractCoordinatePlane::takeDiagram ( AbstractDiagram* diagram ) 0110 { 0111 const int idx = d->diagrams.indexOf( diagram ); 0112 if ( idx != -1 ) { 0113 d->diagrams.removeAt( idx ); 0114 diagram->setParent( nullptr ); 0115 diagram->setCoordinatePlane( nullptr ); 0116 disconnect( diagram, SIGNAL(modelsChanged()), this, SLOT(layoutPlanes()) ); 0117 disconnect( diagram, SIGNAL(modelDataChanged()), this, SLOT(update()) ); 0118 disconnect( diagram, SIGNAL(modelDataChanged()), this, SLOT(relayout()) ); 0119 layoutDiagrams(); 0120 update(); 0121 } 0122 } 0123 0124 0125 AbstractDiagram* AbstractCoordinatePlane::diagram() 0126 { 0127 if ( d->diagrams.isEmpty() ) 0128 { 0129 return nullptr; 0130 } else { 0131 return d->diagrams.first(); 0132 } 0133 } 0134 0135 AbstractDiagramList AbstractCoordinatePlane::diagrams() 0136 { 0137 return d->diagrams; 0138 } 0139 0140 ConstAbstractDiagramList AbstractCoordinatePlane::diagrams() const 0141 { 0142 ConstAbstractDiagramList list; 0143 #ifndef QT_NO_STL 0144 qCopy( d->diagrams.begin(), d->diagrams.end(), std::back_inserter( list ) ); 0145 #else 0146 Q_FOREACH( AbstractDiagram * a, d->diagrams ) 0147 list.push_back( a ); 0148 #endif 0149 return list; 0150 } 0151 0152 void KChart::AbstractCoordinatePlane::setGlobalGridAttributes( const GridAttributes& a ) 0153 { 0154 d->gridAttributes = a; 0155 update(); 0156 } 0157 0158 GridAttributes KChart::AbstractCoordinatePlane::globalGridAttributes() const 0159 { 0160 return d->gridAttributes; 0161 } 0162 0163 KChart::DataDimensionsList KChart::AbstractCoordinatePlane::gridDimensionsList() 0164 { 0165 return d->grid->updateData( this ); 0166 } 0167 0168 void KChart::AbstractCoordinatePlane::setGridNeedsRecalculate() 0169 { 0170 d->grid->setNeedRecalculate(); 0171 } 0172 0173 void KChart::AbstractCoordinatePlane::setReferenceCoordinatePlane( AbstractCoordinatePlane * plane ) 0174 { 0175 d->referenceCoordinatePlane = plane; 0176 } 0177 0178 AbstractCoordinatePlane * KChart::AbstractCoordinatePlane::referenceCoordinatePlane( ) const 0179 { 0180 return d->referenceCoordinatePlane; 0181 } 0182 0183 void KChart::AbstractCoordinatePlane::setParent( KChart::Chart* parent ) 0184 { 0185 d->parent = parent; 0186 } 0187 0188 const KChart::Chart* KChart::AbstractCoordinatePlane::parent() const 0189 { 0190 return d->parent; 0191 } 0192 0193 KChart::Chart* KChart::AbstractCoordinatePlane::parent() 0194 { 0195 return d->parent; 0196 } 0197 0198 /* pure virtual in QLayoutItem */ 0199 bool KChart::AbstractCoordinatePlane::isEmpty() const 0200 { 0201 return false; // never empty! 0202 // coordinate planes with no associated diagrams 0203 // are showing a default grid of ()1..10, 1..10) stepWidth 1 0204 } 0205 /* pure virtual in QLayoutItem */ 0206 Qt::Orientations KChart::AbstractCoordinatePlane::expandingDirections() const 0207 { 0208 return Qt::Vertical | Qt::Horizontal; 0209 } 0210 /* pure virtual in QLayoutItem */ 0211 QSize KChart::AbstractCoordinatePlane::maximumSize() const 0212 { 0213 // No maximum size set. Especially not parent()->size(), we are not layouting 0214 // to the parent widget's size when using Chart::paint()! 0215 return QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX); 0216 } 0217 /* pure virtual in QLayoutItem */ 0218 QSize KChart::AbstractCoordinatePlane::minimumSize() const 0219 { 0220 return QSize(60, 60); // this default can be overwritten by derived classes 0221 } 0222 /* pure virtual in QLayoutItem */ 0223 QSize KChart::AbstractCoordinatePlane::sizeHint() const 0224 { 0225 // we return our maxiumu (which is the full size of the Chart) 0226 // even if we know the plane will be smaller 0227 return maximumSize(); 0228 } 0229 /* pure virtual in QLayoutItem */ 0230 void KChart::AbstractCoordinatePlane::setGeometry( const QRect& r ) 0231 { 0232 if ( d->geometry != r ) { 0233 // inform the outside word by Signal geometryChanged() 0234 // via a queued connection to internal_geometryChanged() 0235 Q_EMIT internal_geometryChanged( d->geometry, r ); 0236 0237 d->geometry = r; 0238 // Note: We do *not* call update() here 0239 // because it would invoke KChart::update() recursively. 0240 } 0241 } 0242 /* pure virtual in QLayoutItem */ 0243 QRect KChart::AbstractCoordinatePlane::geometry() const 0244 { 0245 return d->geometry; 0246 } 0247 0248 void KChart::AbstractCoordinatePlane::update() 0249 { 0250 //qDebug("KChart::AbstractCoordinatePlane::update() called"); 0251 Q_EMIT needUpdate(); 0252 } 0253 0254 void KChart::AbstractCoordinatePlane::relayout() 0255 { 0256 //qDebug("KChart::AbstractCoordinatePlane::relayout() called"); 0257 Q_EMIT needRelayout(); 0258 } 0259 0260 void KChart::AbstractCoordinatePlane::layoutPlanes() 0261 { 0262 //qDebug("KChart::AbstractCoordinatePlane::relayout() called"); 0263 Q_EMIT needLayoutPlanes(); 0264 } 0265 0266 void KChart::AbstractCoordinatePlane::setRubberBandZoomingEnabled( bool enable ) 0267 { 0268 d->enableRubberBandZooming = enable; 0269 0270 if ( !enable && d->rubberBand != nullptr ) 0271 { 0272 delete d->rubberBand; 0273 d->rubberBand = nullptr; 0274 } 0275 } 0276 0277 bool KChart::AbstractCoordinatePlane::isRubberBandZoomingEnabled() const 0278 { 0279 return d->enableRubberBandZooming; 0280 } 0281 0282 void KChart::AbstractCoordinatePlane::setCornerSpacersEnabled( bool enable ) 0283 { 0284 if ( d->enableCornerSpacers == enable ) return; 0285 0286 d->enableCornerSpacers = enable; 0287 Q_EMIT needRelayout(); 0288 } 0289 0290 bool KChart::AbstractCoordinatePlane::isCornerSpacersEnabled() const 0291 { 0292 return d->enableCornerSpacers; 0293 } 0294 0295 void KChart::AbstractCoordinatePlane::mousePressEvent( QMouseEvent* event ) 0296 { 0297 if ( event->button() == Qt::LeftButton ) 0298 { 0299 if ( d->enableRubberBandZooming && d->rubberBand == nullptr ) 0300 d->rubberBand = new QRubberBand( QRubberBand::Rectangle, qobject_cast< QWidget* >( parent() ) ); 0301 0302 if ( d->rubberBand != nullptr ) 0303 { 0304 d->rubberBandOrigin = event->pos(); 0305 d->rubberBand->setGeometry( QRect( event->pos(), QSize() ) ); 0306 d->rubberBand->show(); 0307 0308 event->accept(); 0309 } 0310 } 0311 else if ( event->button() == Qt::RightButton ) 0312 { 0313 if ( d->enableRubberBandZooming && !d->rubberBandZoomConfigHistory.isEmpty() ) 0314 { 0315 // restore the last config from the stack 0316 ZoomParameters config = d->rubberBandZoomConfigHistory.pop(); 0317 setZoomFactorX( config.xFactor ); 0318 setZoomFactorY( config.yFactor ); 0319 setZoomCenter( config.center() ); 0320 0321 QWidget* const p = qobject_cast< QWidget* >( parent() ); 0322 if ( p != nullptr ) 0323 p->update(); 0324 0325 event->accept(); 0326 } 0327 } 0328 0329 Q_FOREACH( AbstractDiagram * a, d->diagrams ) 0330 { 0331 a->mousePressEvent( event ); 0332 } 0333 } 0334 0335 void KChart::AbstractCoordinatePlane::mouseDoubleClickEvent( QMouseEvent* event ) 0336 { 0337 if ( event->button() == Qt::RightButton ) 0338 { 0339 // othewise the second click gets lost 0340 // which is pretty annoying when zooming out fast 0341 mousePressEvent( event ); 0342 } 0343 Q_FOREACH( AbstractDiagram * a, d->diagrams ) 0344 { 0345 a->mouseDoubleClickEvent( event ); 0346 } 0347 } 0348 0349 void KChart::AbstractCoordinatePlane::mouseReleaseEvent( QMouseEvent* event ) 0350 { 0351 if ( d->rubberBand != nullptr ) 0352 { 0353 // save the old config on the stack 0354 d->rubberBandZoomConfigHistory.push( ZoomParameters( zoomFactorX(), zoomFactorY(), zoomCenter() ) ); 0355 0356 // this is the height/width of the rubber band in pixel space 0357 const qreal rubberWidth = static_cast< qreal >( d->rubberBand->width() ); 0358 const qreal rubberHeight = static_cast< qreal >( d->rubberBand->height() ); 0359 0360 if ( rubberWidth > 0.0 && rubberHeight > 0.0 ) 0361 { 0362 // this is the center of the rubber band in pixel space 0363 const qreal centerX = qFloor( d->rubberBand->geometry().width() / 2.0 + d->rubberBand->geometry().x() ); 0364 const qreal centerY = qCeil( d->rubberBand->geometry().height() / 2.0 + d->rubberBand->geometry().y() ); 0365 0366 const qreal rubberCenterX = static_cast< qreal >( centerX - geometry().x() ); 0367 const qreal rubberCenterY = static_cast< qreal >( centerY - geometry().y() ); 0368 0369 // this is the height/width of the plane in pixel space 0370 const qreal myWidth = static_cast< qreal >( geometry().width() ); 0371 const qreal myHeight = static_cast< qreal >( geometry().height() ); 0372 0373 // this describes the new center of zooming, relative to the plane pixel space 0374 const qreal newCenterX = rubberCenterX / myWidth / zoomFactorX() + zoomCenter().x() - 0.5 / zoomFactorX(); 0375 const qreal newCenterY = rubberCenterY / myHeight / zoomFactorY() + zoomCenter().y() - 0.5 / zoomFactorY(); 0376 0377 // this will be the new zoom factor 0378 const qreal newZoomFactorX = zoomFactorX() * myWidth / rubberWidth; 0379 const qreal newZoomFactorY = zoomFactorY() * myHeight / rubberHeight; 0380 0381 // and this the new center 0382 const QPointF newZoomCenter( newCenterX, newCenterY ); 0383 0384 setZoomFactorX( newZoomFactorX ); 0385 setZoomFactorY( newZoomFactorY ); 0386 setZoomCenter( newZoomCenter ); 0387 } 0388 0389 d->rubberBand->parentWidget()->update(); 0390 delete d->rubberBand; 0391 d->rubberBand = nullptr; 0392 0393 event->accept(); 0394 } 0395 0396 Q_FOREACH( AbstractDiagram * a, d->diagrams ) 0397 { 0398 a->mouseReleaseEvent( event ); 0399 } 0400 } 0401 0402 void KChart::AbstractCoordinatePlane::mouseMoveEvent( QMouseEvent* event ) 0403 { 0404 if ( d->rubberBand != nullptr ) 0405 { 0406 const QRect normalized = QRect( d->rubberBandOrigin, event->pos() ).normalized(); 0407 d->rubberBand->setGeometry( normalized & geometry() ); 0408 0409 event->accept(); 0410 } 0411 0412 Q_FOREACH( AbstractDiagram * a, d->diagrams ) 0413 { 0414 a->mouseMoveEvent( event ); 0415 } 0416 } 0417 0418 #if defined(Q_COMPILER_MANGLES_RETURN_TYPE) 0419 const 0420 #endif 0421 bool KChart::AbstractCoordinatePlane::isVisiblePoint( const QPointF& point ) const 0422 { 0423 return d->isVisiblePoint( this, point ); 0424 } 0425 0426 AbstractCoordinatePlane* KChart::AbstractCoordinatePlane::sharedAxisMasterPlane( QPainter* p ) 0427 { 0428 Q_UNUSED( p ); 0429 return this; 0430 } 0431 0432 #if !defined(QT_NO_DEBUG_STREAM) 0433 #include "KChartEnums.h" 0434 0435 QDebug KChart::operator<<( QDebug stream, const DataDimension& r ) 0436 { 0437 stream << "DataDimension(" 0438 << " start=" << r.start 0439 << " end=" << r.end 0440 << " sequence=" << KChartEnums::granularitySequenceToString( r.sequence ) 0441 << " isCalculated=" << r.isCalculated 0442 << " calcMode=" << ( r.calcMode == AbstractCoordinatePlane::Logarithmic ? "Logarithmic" : "Linear" ) 0443 << " stepWidth=" << r.stepWidth 0444 << " subStepWidth=" << r.subStepWidth 0445 << " )"; 0446 return stream; 0447 } 0448 #endif 0449 0450 #undef d