KD Chart 2 [rev.2.4]
|
00001 /**************************************************************************** 00002 ** Copyright (C) 2001-2012 Klaralvdalens Datakonsult AB. All rights reserved. 00003 ** 00004 ** This file is part of the KD Chart library. 00005 ** 00006 ** Licensees holding valid commercial KD Chart licenses may use this file in 00007 ** accordance with the KD Chart Commercial License Agreement provided with 00008 ** the Software. 00009 ** 00010 ** 00011 ** This file may be distributed and/or modified under the terms of the 00012 ** GNU General Public License version 2 and version 3 as published by the 00013 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. 00014 ** 00015 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 00016 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 00017 ** 00018 ** Contact info@kdab.com if any conditions of this licensing are not 00019 ** clear to you. 00020 ** 00021 **********************************************************************/ 00022 00023 #include "KDChartAbstractCoordinatePlane.h" 00024 #include "KDChartAbstractCoordinatePlane_p.h" 00025 00026 #include <QGridLayout> 00027 #include <QRubberBand> 00028 #include <QMouseEvent> 00029 #include <QtCore/qmath.h> 00030 00031 #include "KDChartChart.h" 00032 #include "KDChartGridAttributes.h" 00033 00034 #include <KDABLibFakes> 00035 00036 00037 using namespace KDChart; 00038 00039 #define d d_func() 00040 00041 AbstractCoordinatePlane::Private::Private() 00042 : AbstractArea::Private() 00043 , parent( 0 ) 00044 , grid( 0 ) 00045 , referenceCoordinatePlane( 0 ) 00046 , enableRubberBandZooming( false ) 00047 , rubberBand( 0 ) 00048 { 00049 // this bloc left empty intentionally 00050 } 00051 00052 00053 AbstractCoordinatePlane::AbstractCoordinatePlane ( KDChart::Chart* parent ) 00054 : AbstractArea ( new Private() ) 00055 { 00056 d->parent = parent; 00057 d->init(); 00058 } 00059 00060 AbstractCoordinatePlane::~AbstractCoordinatePlane() 00061 { 00062 emit destroyedCoordinatePlane( this ); 00063 } 00064 00065 void AbstractCoordinatePlane::init() 00066 { 00067 d->initialize(); // virtual method to init the correct grid: cartesian, polar, ... 00068 connect( this, SIGNAL(internal_geometryChanged( QRect, QRect )), 00069 this, SIGNAL(geometryChanged( QRect, QRect )), 00070 Qt::QueuedConnection ); 00071 } 00072 00073 void AbstractCoordinatePlane::addDiagram ( AbstractDiagram* diagram ) 00074 { 00075 // diagrams are invisible and paint through their paint() method 00076 diagram->hide(); 00077 00078 d->diagrams.append( diagram ); 00079 diagram->setParent( d->parent ); 00080 diagram->setCoordinatePlane( this ); 00081 layoutDiagrams(); 00082 layoutPlanes(); // there might be new axes, etc 00083 connect( diagram, SIGNAL( modelsChanged() ), this, SLOT( layoutPlanes() ) ); 00084 connect( diagram, SIGNAL( modelDataChanged() ), this, SLOT( update()) ); 00085 connect( diagram, SIGNAL( modelDataChanged() ), this, SLOT( relayout()) ); 00086 00087 update(); 00088 } 00089 00090 /*virtual*/ 00091 void AbstractCoordinatePlane::replaceDiagram ( AbstractDiagram* diagram, AbstractDiagram* oldDiagram_ ) 00092 { 00093 if( diagram && oldDiagram_ != diagram ){ 00094 AbstractDiagram* oldDiagram = oldDiagram_; 00095 if( d->diagrams.count() ){ 00096 if( ! oldDiagram ){ 00097 oldDiagram = d->diagrams.first(); 00098 if( oldDiagram == diagram ) 00099 return; 00100 } 00101 takeDiagram( oldDiagram ); 00102 } 00103 delete oldDiagram; 00104 addDiagram( diagram ); 00105 layoutDiagrams(); 00106 layoutPlanes(); // there might be new axes, etc 00107 update(); 00108 } 00109 } 00110 00111 /*virtual*/ 00112 void AbstractCoordinatePlane::takeDiagram ( AbstractDiagram* diagram ) 00113 { 00114 const int idx = d->diagrams.indexOf( diagram ); 00115 if( idx != -1 ){ 00116 d->diagrams.removeAt( idx ); 00117 diagram->setParent( 0 ); 00118 diagram->setCoordinatePlane( 0 ); 00119 disconnect( diagram, SIGNAL( modelsChanged() ), this, SLOT( layoutPlanes() ) ); 00120 disconnect( diagram, SIGNAL( modelDataChanged() ), this, SLOT( update()) ); 00121 disconnect( diagram, SIGNAL( modelDataChanged() ), this, SLOT( relayout()) ); 00122 layoutDiagrams(); 00123 update(); 00124 } 00125 } 00126 00127 00128 AbstractDiagram* AbstractCoordinatePlane::diagram() 00129 { 00130 if ( d->diagrams.isEmpty() ) 00131 { 00132 return 0; 00133 } else { 00134 return d->diagrams.first(); 00135 } 00136 } 00137 00138 AbstractDiagramList AbstractCoordinatePlane::diagrams() 00139 { 00140 return d->diagrams; 00141 } 00142 00143 ConstAbstractDiagramList AbstractCoordinatePlane::diagrams() const 00144 { 00145 ConstAbstractDiagramList list; 00146 #ifndef QT_NO_STL 00147 qCopy( d->diagrams.begin(), d->diagrams.end(), std::back_inserter( list ) ); 00148 #else 00149 Q_FOREACH( AbstractDiagram * a, d->diagrams ) 00150 list.push_back( a ); 00151 #endif 00152 return list; 00153 } 00154 00155 QSize KDChart::AbstractCoordinatePlane::minimumSizeHint() const 00156 { 00157 return QSize( 200, 200 ); 00158 } 00159 00160 00161 QSizePolicy KDChart::AbstractCoordinatePlane::sizePolicy() const 00162 { 00163 return QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding ); 00164 } 00165 00166 void KDChart::AbstractCoordinatePlane::setGlobalGridAttributes( const GridAttributes& a ) 00167 { 00168 d->gridAttributes = a; 00169 update(); 00170 } 00171 00172 GridAttributes KDChart::AbstractCoordinatePlane::globalGridAttributes() const 00173 { 00174 return d->gridAttributes; 00175 } 00176 00177 KDChart::DataDimensionsList KDChart::AbstractCoordinatePlane::gridDimensionsList() 00178 { 00179 //KDChart::DataDimensionsList l( d->grid->updateData( this ) ); 00180 //qDebug() << "AbstractCoordinatePlane::gridDimensionsList() Y-range:" << l.last().end - l.last().start << " step width:" << l.last().stepWidth; 00181 //qDebug() << "AbstractCoordinatePlane::gridDimensionsList() X-range:" << l.first().end - l.first().start << " step width:" << l.first().stepWidth; 00182 return d->grid->updateData( this ); 00183 } 00184 00185 void KDChart::AbstractCoordinatePlane::setGridNeedsRecalculate() 00186 { 00187 d->grid->setNeedRecalculate(); 00188 } 00189 00190 void KDChart::AbstractCoordinatePlane::setReferenceCoordinatePlane( AbstractCoordinatePlane * plane ) 00191 { 00192 d->referenceCoordinatePlane = plane; 00193 } 00194 00195 AbstractCoordinatePlane * KDChart::AbstractCoordinatePlane::referenceCoordinatePlane( ) const 00196 { 00197 return d->referenceCoordinatePlane; 00198 } 00199 00200 void KDChart::AbstractCoordinatePlane::setParent( KDChart::Chart* parent ) 00201 { 00202 d->parent = parent; 00203 } 00204 00205 const KDChart::Chart* KDChart::AbstractCoordinatePlane::parent() const 00206 { 00207 return d->parent; 00208 } 00209 00210 KDChart::Chart* KDChart::AbstractCoordinatePlane::parent() 00211 { 00212 return d->parent; 00213 } 00214 00215 /* pure virtual in QLayoutItem */ 00216 bool KDChart::AbstractCoordinatePlane::isEmpty() const 00217 { 00218 return false; // never empty! 00219 // coordinate planes with no associated diagrams 00220 // are showing a default grid of ()1..10, 1..10) stepWidth 1 00221 } 00222 /* pure virtual in QLayoutItem */ 00223 Qt::Orientations KDChart::AbstractCoordinatePlane::expandingDirections() const 00224 { 00225 return Qt::Vertical | Qt::Horizontal; 00226 } 00227 /* pure virtual in QLayoutItem */ 00228 QSize KDChart::AbstractCoordinatePlane::maximumSize() const 00229 { 00230 // No maximum size set. Especially not parent()->size(), we are not layouting 00231 // to the parent widget's size when using Chart::paint()! 00232 return QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX); 00233 } 00234 /* pure virtual in QLayoutItem */ 00235 QSize KDChart::AbstractCoordinatePlane::minimumSize() const 00236 { 00237 return QSize(60, 60); // this default can be overwritten by derived classes 00238 } 00239 /* pure virtual in QLayoutItem */ 00240 QSize KDChart::AbstractCoordinatePlane::sizeHint() const 00241 { 00242 // we return our maxiumu (which is the full size of the Chart) 00243 // even if we know the plane will be smaller 00244 return maximumSize(); 00245 } 00246 /* pure virtual in QLayoutItem */ 00247 void KDChart::AbstractCoordinatePlane::setGeometry( const QRect& r ) 00248 { 00249 // qDebug() << "KDChart::AbstractCoordinatePlane::setGeometry(" << r << ") called"; 00250 if( d->geometry != r ){ 00251 //qDebug() << "entering KDChart::AbstractCoordinatePlane::setGeometry(" << r << ")"; 00252 // inform the outside word by Signal geometryChanged() 00253 // via a queued connection to internal_geometryChanged() 00254 emit internal_geometryChanged( d->geometry, r ); 00255 00256 d->geometry = r; 00257 // Note: We do *not* call update() here 00258 // because it would invoke KDChart::update() recursively. 00259 //qDebug() << "leaving KDChart::AbstractCoordinatePlane::setGeometry(" << r << ")"; 00260 } 00261 } 00262 /* pure virtual in QLayoutItem */ 00263 QRect KDChart::AbstractCoordinatePlane::geometry() const 00264 { 00265 return d->geometry; 00266 } 00267 00268 void KDChart::AbstractCoordinatePlane::update() 00269 { 00270 //qDebug("KDChart::AbstractCoordinatePlane::update() called"); 00271 emit needUpdate(); 00272 } 00273 00274 void KDChart::AbstractCoordinatePlane::relayout() 00275 { 00276 //qDebug("KDChart::AbstractCoordinatePlane::relayout() called"); 00277 emit needRelayout(); 00278 } 00279 00280 void KDChart::AbstractCoordinatePlane::layoutPlanes() 00281 { 00282 //qDebug("KDChart::AbstractCoordinatePlane::relayout() called"); 00283 emit needLayoutPlanes(); 00284 } 00285 00286 void KDChart::AbstractCoordinatePlane::setRubberBandZoomingEnabled( bool enable ) 00287 { 00288 d->enableRubberBandZooming = enable; 00289 00290 if( !enable && d->rubberBand != 0 ) 00291 { 00292 delete d->rubberBand; 00293 d->rubberBand = 0; 00294 } 00295 } 00296 00297 bool KDChart::AbstractCoordinatePlane::isRubberBandZoomingEnabled() const 00298 { 00299 return d->enableRubberBandZooming; 00300 } 00301 00302 void KDChart::AbstractCoordinatePlane::mousePressEvent( QMouseEvent* event ) 00303 { 00304 if( event->button() == Qt::LeftButton ) 00305 { 00306 if( d->enableRubberBandZooming && d->rubberBand == 0 ) 00307 d->rubberBand = new QRubberBand( QRubberBand::Rectangle, qobject_cast< QWidget* >( parent() ) ); 00308 00309 if( d->rubberBand != 0 ) 00310 { 00311 d->rubberBandOrigin = event->pos(); 00312 d->rubberBand->setGeometry( QRect( event->pos(), QSize() ) ); 00313 d->rubberBand->show(); 00314 00315 event->accept(); 00316 } 00317 } 00318 else if( event->button() == Qt::RightButton ) 00319 { 00320 if( d->enableRubberBandZooming && !d->rubberBandZoomConfigHistory.isEmpty() ) 00321 { 00322 // restore the last config from the stack 00323 ZoomParameters config = d->rubberBandZoomConfigHistory.pop(); 00324 setZoomFactorX( config.xFactor ); 00325 setZoomFactorY( config.yFactor ); 00326 setZoomCenter( config.center() ); 00327 00328 QWidget* const p = qobject_cast< QWidget* >( parent() ); 00329 if( p != 0 ) 00330 p->update(); 00331 00332 event->accept(); 00333 } 00334 } 00335 00336 KDAB_FOREACH( AbstractDiagram * a, d->diagrams ) 00337 { 00338 a->mousePressEvent( event ); 00339 } 00340 } 00341 00342 void KDChart::AbstractCoordinatePlane::mouseDoubleClickEvent( QMouseEvent* event ) 00343 { 00344 if( event->button() == Qt::RightButton ) 00345 { 00346 // othewise the second click gets lost 00347 // which is pretty annoying when zooming out fast 00348 mousePressEvent( event ); 00349 } 00350 KDAB_FOREACH( AbstractDiagram * a, d->diagrams ) 00351 { 00352 a->mouseDoubleClickEvent( event ); 00353 } 00354 } 00355 00356 void KDChart::AbstractCoordinatePlane::mouseReleaseEvent( QMouseEvent* event ) 00357 { 00358 if( d->rubberBand != 0 ) 00359 { 00360 // save the old config on the stack 00361 d->rubberBandZoomConfigHistory.push( ZoomParameters( zoomFactorX(), zoomFactorY(), zoomCenter() ) ); 00362 00363 // this is the height/width of the rubber band in pixel space 00364 const double rubberWidth = static_cast< double >( d->rubberBand->width() ); 00365 const double rubberHeight = static_cast< double >( d->rubberBand->height() ); 00366 00367 if( rubberWidth > 0.0 && rubberHeight > 0.0 ) 00368 { 00369 // this is the center of the rubber band in pixel space 00370 const qreal centerX = qFloor( d->rubberBand->geometry().width() / 2.0 + d->rubberBand->geometry().x() ); 00371 const qreal centerY = qCeil( d->rubberBand->geometry().height() / 2.0 + d->rubberBand->geometry().y() ); 00372 00373 const qreal rubberCenterX = static_cast< qreal >( centerX - geometry().x() ); 00374 const qreal rubberCenterY = static_cast< qreal >( centerY - geometry().y() ); 00375 00376 // this is the height/width of the plane in pixel space 00377 const double myWidth = static_cast< double >( geometry().width() ); 00378 const double myHeight = static_cast< double >( geometry().height() ); 00379 00380 // this describes the new center of zooming, relative to the plane pixel space 00381 const double newCenterX = rubberCenterX / zoomFactorX() / myWidth + zoomCenter().x() - 0.5 / zoomFactorX(); 00382 const double newCenterY = rubberCenterY / zoomFactorY() / myHeight + zoomCenter().y() - 0.5 / zoomFactorY(); 00383 00384 // this will be the new zoom factor 00385 const double newZoomFactorX = zoomFactorX() * myWidth / rubberWidth; 00386 const double newZoomFactorY = zoomFactorY() * myHeight / rubberHeight; 00387 00388 // and this the new center 00389 const QPointF newZoomCenter( newCenterX, newCenterY ); 00390 00391 setZoomFactorX( newZoomFactorX ); 00392 setZoomFactorY( newZoomFactorY ); 00393 setZoomCenter( newZoomCenter ); 00394 } 00395 00396 d->rubberBand->parentWidget()->update(); 00397 delete d->rubberBand; 00398 d->rubberBand = 0; 00399 00400 event->accept(); 00401 } 00402 00403 KDAB_FOREACH( AbstractDiagram * a, d->diagrams ) 00404 { 00405 a->mouseReleaseEvent( event ); 00406 } 00407 } 00408 00409 void KDChart::AbstractCoordinatePlane::mouseMoveEvent( QMouseEvent* event ) 00410 { 00411 if( d->rubberBand != 0 ) 00412 { 00413 const QRect normalized = QRect( d->rubberBandOrigin, event->pos() ).normalized(); 00414 d->rubberBand->setGeometry( normalized & geometry() ); 00415 00416 event->accept(); 00417 } 00418 00419 KDAB_FOREACH( AbstractDiagram * a, d->diagrams ) 00420 { 00421 a->mouseMoveEvent( event ); 00422 } 00423 } 00424 00425 #if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) 00426 const 00427 #endif 00428 bool KDChart::AbstractCoordinatePlane::isVisiblePoint( const QPointF& point ) const 00429 { 00430 return d->isVisiblePoint( this, point ); 00431 } 00432 00433 AbstractCoordinatePlane* KDChart::AbstractCoordinatePlane::sharedAxisMasterPlane( QPainter* p ) 00434 { 00435 Q_UNUSED( p ); 00436 return this; 00437 } 00438 00439 #if !defined(QT_NO_DEBUG_STREAM) 00440 #include "KDChartEnums.h" 00441 00442 QDebug KDChart::operator<<( QDebug stream, const DataDimension& r ) 00443 { 00444 stream << "DataDimension(" 00445 << " start=" << r.start 00446 << " end=" << r.end 00447 << " sequence=" << KDChartEnums::granularitySequenceToString( r.sequence ) 00448 << " isCalculated=" << r.isCalculated 00449 << " calcMode=" << ( r.calcMode == AbstractCoordinatePlane::Logarithmic ? "Logarithmic" : "Linear" ) 00450 << " stepWidth=" << r.stepWidth 00451 << " subStepWidth=" << r.subStepWidth 00452 << " )"; 00453 return stream; 00454 } 00455 #endif 00456 00457 #undef d