KD Chart 2
[rev.2.5]
|
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 "KDChartChart.h" 00027 #include "KDChartGridAttributes.h" 00028 00029 #include <KDABLibFakes> 00030 00031 #include <QGridLayout> 00032 #include <QRubberBand> 00033 #include <QMouseEvent> 00034 #include <QtCore/qmath.h> 00035 00036 using namespace KDChart; 00037 00038 #define d d_func() 00039 00040 AbstractCoordinatePlane::Private::Private() 00041 : AbstractArea::Private() 00042 , parent( 0 ) 00043 , grid( 0 ) 00044 , referenceCoordinatePlane( 0 ) 00045 , enableCornerSpacers( true ) 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 connect( this, SIGNAL( boundariesChanged() ), diagram, SIGNAL( boundariesChanged() ) ); 00087 00088 update(); 00089 emit boundariesChanged(); 00090 } 00091 00092 /*virtual*/ 00093 void AbstractCoordinatePlane::replaceDiagram ( AbstractDiagram* diagram, AbstractDiagram* oldDiagram_ ) 00094 { 00095 if( diagram && oldDiagram_ != diagram ){ 00096 AbstractDiagram* oldDiagram = oldDiagram_; 00097 if( d->diagrams.count() ){ 00098 if( ! oldDiagram ){ 00099 oldDiagram = d->diagrams.first(); 00100 if( oldDiagram == diagram ) 00101 return; 00102 } 00103 takeDiagram( oldDiagram ); 00104 } 00105 delete oldDiagram; 00106 addDiagram( diagram ); 00107 layoutDiagrams(); 00108 layoutPlanes(); // there might be new axes, etc 00109 update(); 00110 } 00111 } 00112 00113 /*virtual*/ 00114 void AbstractCoordinatePlane::takeDiagram ( AbstractDiagram* diagram ) 00115 { 00116 const int idx = d->diagrams.indexOf( diagram ); 00117 if( idx != -1 ){ 00118 d->diagrams.removeAt( idx ); 00119 diagram->setParent( 0 ); 00120 diagram->setCoordinatePlane( 0 ); 00121 disconnect( diagram, SIGNAL( modelsChanged() ), this, SLOT( layoutPlanes() ) ); 00122 disconnect( diagram, SIGNAL( modelDataChanged() ), this, SLOT( update()) ); 00123 disconnect( diagram, SIGNAL( modelDataChanged() ), this, SLOT( relayout()) ); 00124 layoutDiagrams(); 00125 update(); 00126 } 00127 } 00128 00129 00130 AbstractDiagram* AbstractCoordinatePlane::diagram() 00131 { 00132 if ( d->diagrams.isEmpty() ) 00133 { 00134 return 0; 00135 } else { 00136 return d->diagrams.first(); 00137 } 00138 } 00139 00140 AbstractDiagramList AbstractCoordinatePlane::diagrams() 00141 { 00142 return d->diagrams; 00143 } 00144 00145 ConstAbstractDiagramList AbstractCoordinatePlane::diagrams() const 00146 { 00147 ConstAbstractDiagramList list; 00148 #ifndef QT_NO_STL 00149 qCopy( d->diagrams.begin(), d->diagrams.end(), std::back_inserter( list ) ); 00150 #else 00151 Q_FOREACH( AbstractDiagram * a, d->diagrams ) 00152 list.push_back( a ); 00153 #endif 00154 return list; 00155 } 00156 00157 void KDChart::AbstractCoordinatePlane::setGlobalGridAttributes( const GridAttributes& a ) 00158 { 00159 d->gridAttributes = a; 00160 update(); 00161 } 00162 00163 GridAttributes KDChart::AbstractCoordinatePlane::globalGridAttributes() const 00164 { 00165 return d->gridAttributes; 00166 } 00167 00168 KDChart::DataDimensionsList KDChart::AbstractCoordinatePlane::gridDimensionsList() 00169 { 00170 return d->grid->updateData( this ); 00171 } 00172 00173 void KDChart::AbstractCoordinatePlane::setGridNeedsRecalculate() 00174 { 00175 d->grid->setNeedRecalculate(); 00176 } 00177 00178 void KDChart::AbstractCoordinatePlane::setReferenceCoordinatePlane( AbstractCoordinatePlane * plane ) 00179 { 00180 d->referenceCoordinatePlane = plane; 00181 } 00182 00183 AbstractCoordinatePlane * KDChart::AbstractCoordinatePlane::referenceCoordinatePlane( ) const 00184 { 00185 return d->referenceCoordinatePlane; 00186 } 00187 00188 void KDChart::AbstractCoordinatePlane::setParent( KDChart::Chart* parent ) 00189 { 00190 d->parent = parent; 00191 } 00192 00193 const KDChart::Chart* KDChart::AbstractCoordinatePlane::parent() const 00194 { 00195 return d->parent; 00196 } 00197 00198 KDChart::Chart* KDChart::AbstractCoordinatePlane::parent() 00199 { 00200 return d->parent; 00201 } 00202 00203 /* pure virtual in QLayoutItem */ 00204 bool KDChart::AbstractCoordinatePlane::isEmpty() const 00205 { 00206 return false; // never empty! 00207 // coordinate planes with no associated diagrams 00208 // are showing a default grid of ()1..10, 1..10) stepWidth 1 00209 } 00210 /* pure virtual in QLayoutItem */ 00211 Qt::Orientations KDChart::AbstractCoordinatePlane::expandingDirections() const 00212 { 00213 return Qt::Vertical | Qt::Horizontal; 00214 } 00215 /* pure virtual in QLayoutItem */ 00216 QSize KDChart::AbstractCoordinatePlane::maximumSize() const 00217 { 00218 // No maximum size set. Especially not parent()->size(), we are not layouting 00219 // to the parent widget's size when using Chart::paint()! 00220 return QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX); 00221 } 00222 /* pure virtual in QLayoutItem */ 00223 QSize KDChart::AbstractCoordinatePlane::minimumSize() const 00224 { 00225 return QSize(60, 60); // this default can be overwritten by derived classes 00226 } 00227 /* pure virtual in QLayoutItem */ 00228 QSize KDChart::AbstractCoordinatePlane::sizeHint() const 00229 { 00230 // we return our maxiumu (which is the full size of the Chart) 00231 // even if we know the plane will be smaller 00232 return maximumSize(); 00233 } 00234 /* pure virtual in QLayoutItem */ 00235 void KDChart::AbstractCoordinatePlane::setGeometry( const QRect& r ) 00236 { 00237 if ( d->geometry != r ) { 00238 // inform the outside word by Signal geometryChanged() 00239 // via a queued connection to internal_geometryChanged() 00240 emit internal_geometryChanged( d->geometry, r ); 00241 00242 d->geometry = r; 00243 // Note: We do *not* call update() here 00244 // because it would invoke KDChart::update() recursively. 00245 } 00246 } 00247 /* pure virtual in QLayoutItem */ 00248 QRect KDChart::AbstractCoordinatePlane::geometry() const 00249 { 00250 return d->geometry; 00251 } 00252 00253 void KDChart::AbstractCoordinatePlane::update() 00254 { 00255 //qDebug("KDChart::AbstractCoordinatePlane::update() called"); 00256 emit needUpdate(); 00257 } 00258 00259 void KDChart::AbstractCoordinatePlane::relayout() 00260 { 00261 //qDebug("KDChart::AbstractCoordinatePlane::relayout() called"); 00262 emit needRelayout(); 00263 } 00264 00265 void KDChart::AbstractCoordinatePlane::layoutPlanes() 00266 { 00267 //qDebug("KDChart::AbstractCoordinatePlane::relayout() called"); 00268 emit needLayoutPlanes(); 00269 } 00270 00271 void KDChart::AbstractCoordinatePlane::setRubberBandZoomingEnabled( bool enable ) 00272 { 00273 d->enableRubberBandZooming = enable; 00274 00275 if( !enable && d->rubberBand != 0 ) 00276 { 00277 delete d->rubberBand; 00278 d->rubberBand = 0; 00279 } 00280 } 00281 00282 bool KDChart::AbstractCoordinatePlane::isRubberBandZoomingEnabled() const 00283 { 00284 return d->enableRubberBandZooming; 00285 } 00286 00287 void KDChart::AbstractCoordinatePlane::setCornerSpacersEnabled( bool enable ) 00288 { 00289 if ( d->enableCornerSpacers == enable ) return; 00290 00291 d->enableCornerSpacers = enable; 00292 emit needRelayout(); 00293 } 00294 00295 bool KDChart::AbstractCoordinatePlane::isCornerSpacersEnabled() const 00296 { 00297 return d->enableCornerSpacers; 00298 } 00299 00300 void KDChart::AbstractCoordinatePlane::mousePressEvent( QMouseEvent* event ) 00301 { 00302 if( event->button() == Qt::LeftButton ) 00303 { 00304 if( d->enableRubberBandZooming && d->rubberBand == 0 ) 00305 d->rubberBand = new QRubberBand( QRubberBand::Rectangle, qobject_cast< QWidget* >( parent() ) ); 00306 00307 if( d->rubberBand != 0 ) 00308 { 00309 d->rubberBandOrigin = event->pos(); 00310 d->rubberBand->setGeometry( QRect( event->pos(), QSize() ) ); 00311 d->rubberBand->show(); 00312 00313 event->accept(); 00314 } 00315 } 00316 else if( event->button() == Qt::RightButton ) 00317 { 00318 if( d->enableRubberBandZooming && !d->rubberBandZoomConfigHistory.isEmpty() ) 00319 { 00320 // restore the last config from the stack 00321 ZoomParameters config = d->rubberBandZoomConfigHistory.pop(); 00322 setZoomFactorX( config.xFactor ); 00323 setZoomFactorY( config.yFactor ); 00324 setZoomCenter( config.center() ); 00325 00326 QWidget* const p = qobject_cast< QWidget* >( parent() ); 00327 if( p != 0 ) 00328 p->update(); 00329 00330 event->accept(); 00331 } 00332 } 00333 00334 KDAB_FOREACH( AbstractDiagram * a, d->diagrams ) 00335 { 00336 a->mousePressEvent( event ); 00337 } 00338 } 00339 00340 void KDChart::AbstractCoordinatePlane::mouseDoubleClickEvent( QMouseEvent* event ) 00341 { 00342 if( event->button() == Qt::RightButton ) 00343 { 00344 // othewise the second click gets lost 00345 // which is pretty annoying when zooming out fast 00346 mousePressEvent( event ); 00347 } 00348 KDAB_FOREACH( AbstractDiagram * a, d->diagrams ) 00349 { 00350 a->mouseDoubleClickEvent( event ); 00351 } 00352 } 00353 00354 void KDChart::AbstractCoordinatePlane::mouseReleaseEvent( QMouseEvent* event ) 00355 { 00356 if( d->rubberBand != 0 ) 00357 { 00358 // save the old config on the stack 00359 d->rubberBandZoomConfigHistory.push( ZoomParameters( zoomFactorX(), zoomFactorY(), zoomCenter() ) ); 00360 00361 // this is the height/width of the rubber band in pixel space 00362 const qreal rubberWidth = static_cast< qreal >( d->rubberBand->width() ); 00363 const qreal rubberHeight = static_cast< qreal >( d->rubberBand->height() ); 00364 00365 if( rubberWidth > 0.0 && rubberHeight > 0.0 ) 00366 { 00367 // this is the center of the rubber band in pixel space 00368 const qreal centerX = qFloor( d->rubberBand->geometry().width() / 2.0 + d->rubberBand->geometry().x() ); 00369 const qreal centerY = qCeil( d->rubberBand->geometry().height() / 2.0 + d->rubberBand->geometry().y() ); 00370 00371 const qreal rubberCenterX = static_cast< qreal >( centerX - geometry().x() ); 00372 const qreal rubberCenterY = static_cast< qreal >( centerY - geometry().y() ); 00373 00374 // this is the height/width of the plane in pixel space 00375 const qreal myWidth = static_cast< qreal >( geometry().width() ); 00376 const qreal myHeight = static_cast< qreal >( geometry().height() ); 00377 00378 // this describes the new center of zooming, relative to the plane pixel space 00379 const qreal newCenterX = rubberCenterX / myWidth / zoomFactorX() + zoomCenter().x() - 0.5 / zoomFactorX(); 00380 const qreal newCenterY = rubberCenterY / myHeight / zoomFactorY() + zoomCenter().y() - 0.5 / zoomFactorY(); 00381 00382 // this will be the new zoom factor 00383 const qreal newZoomFactorX = zoomFactorX() * myWidth / rubberWidth; 00384 const qreal newZoomFactorY = zoomFactorY() * myHeight / rubberHeight; 00385 00386 // and this the new center 00387 const QPointF newZoomCenter( newCenterX, newCenterY ); 00388 00389 setZoomFactorX( newZoomFactorX ); 00390 setZoomFactorY( newZoomFactorY ); 00391 setZoomCenter( newZoomCenter ); 00392 } 00393 00394 d->rubberBand->parentWidget()->update(); 00395 delete d->rubberBand; 00396 d->rubberBand = 0; 00397 00398 event->accept(); 00399 } 00400 00401 KDAB_FOREACH( AbstractDiagram * a, d->diagrams ) 00402 { 00403 a->mouseReleaseEvent( event ); 00404 } 00405 } 00406 00407 void KDChart::AbstractCoordinatePlane::mouseMoveEvent( QMouseEvent* event ) 00408 { 00409 if( d->rubberBand != 0 ) 00410 { 00411 const QRect normalized = QRect( d->rubberBandOrigin, event->pos() ).normalized(); 00412 d->rubberBand->setGeometry( normalized & geometry() ); 00413 00414 event->accept(); 00415 } 00416 00417 KDAB_FOREACH( AbstractDiagram * a, d->diagrams ) 00418 { 00419 a->mouseMoveEvent( event ); 00420 } 00421 } 00422 00423 #if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) 00424 const 00425 #endif 00426 bool KDChart::AbstractCoordinatePlane::isVisiblePoint( const QPointF& point ) const 00427 { 00428 return d->isVisiblePoint( this, point ); 00429 } 00430 00431 AbstractCoordinatePlane* KDChart::AbstractCoordinatePlane::sharedAxisMasterPlane( QPainter* p ) 00432 { 00433 Q_UNUSED( p ); 00434 return this; 00435 } 00436 00437 #if !defined(QT_NO_DEBUG_STREAM) 00438 #include "KDChartEnums.h" 00439 00440 QDebug KDChart::operator<<( QDebug stream, const DataDimension& r ) 00441 { 00442 stream << "DataDimension(" 00443 << " start=" << r.start 00444 << " end=" << r.end 00445 << " sequence=" << KDChartEnums::granularitySequenceToString( r.sequence ) 00446 << " isCalculated=" << r.isCalculated 00447 << " calcMode=" << ( r.calcMode == AbstractCoordinatePlane::Logarithmic ? "Logarithmic" : "Linear" ) 00448 << " stepWidth=" << r.stepWidth 00449 << " subStepWidth=" << r.subStepWidth 00450 << " )"; 00451 return stream; 00452 } 00453 #endif 00454 00455 #undef d