/**************************************************************************** ** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. ** ** This file is part of the KD Chart library. ** ** Licensees holding valid commercial KD Chart licenses may use this file in ** accordance with the KD Chart Commercial License Agreement provided with ** the Software. ** ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 and version 3 as published by the ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** Contact info@kdab.com if any conditions of this licensing are not ** clear to you. ** **********************************************************************/ #include "KDChartAbstractCoordinatePlane.h" #include "KDChartAbstractCoordinatePlane_p.h" #include #include #include #include "KDChartChart.h" #include "KDChartGridAttributes.h" #include using namespace KDChart; #define d d_func() AbstractCoordinatePlane::Private::Private() : AbstractArea::Private() , parent( 0 ) , grid( 0 ) , referenceCoordinatePlane( 0 ) , enableRubberBandZooming( false ) , rubberBand( 0 ) { // this bloc left empty intentionally } AbstractCoordinatePlane::AbstractCoordinatePlane ( KDChart::Chart* parent ) : AbstractArea ( new Private() ) { d->parent = parent; d->init(); } AbstractCoordinatePlane::~AbstractCoordinatePlane() { emit destroyedCoordinatePlane( this ); } void AbstractCoordinatePlane::init() { d->initialize(); // virtual method to init the correct grid: cartesian, polar, ... connect( this, SIGNAL(internal_geometryChanged( QRect, QRect )), this, SIGNAL(geometryChanged( QRect, QRect )), Qt::QueuedConnection ); } void AbstractCoordinatePlane::addDiagram ( AbstractDiagram* diagram ) { // diagrams are invisible and paint through their paint() method diagram->hide(); d->diagrams.append( diagram ); diagram->setParent( d->parent ); diagram->setCoordinatePlane( this ); layoutDiagrams(); layoutPlanes(); // there might be new axes, etc connect( diagram, SIGNAL( modelsChanged() ), this, SLOT( layoutPlanes() ) ); connect( diagram, SIGNAL( modelDataChanged() ), this, SLOT( update()) ); connect( diagram, SIGNAL( modelDataChanged() ), this, SLOT( relayout()) ); update(); } /*virtual*/ void AbstractCoordinatePlane::replaceDiagram ( AbstractDiagram* diagram, AbstractDiagram* oldDiagram_ ) { if( diagram && oldDiagram_ != diagram ){ AbstractDiagram* oldDiagram = oldDiagram_; if( d->diagrams.count() ){ if( ! oldDiagram ){ oldDiagram = d->diagrams.first(); if( oldDiagram == diagram ) return; } takeDiagram( oldDiagram ); } delete oldDiagram; addDiagram( diagram ); layoutDiagrams(); layoutPlanes(); // there might be new axes, etc update(); } } /*virtual*/ void AbstractCoordinatePlane::takeDiagram ( AbstractDiagram* diagram ) { const int idx = d->diagrams.indexOf( diagram ); if( idx != -1 ){ d->diagrams.removeAt( idx ); diagram->setParent( 0 ); diagram->setCoordinatePlane( 0 ); disconnect( diagram, SIGNAL( modelsChanged() ), this, SLOT( layoutPlanes() ) ); disconnect( diagram, SIGNAL( modelDataChanged() ), this, SLOT( update()) ); disconnect( diagram, SIGNAL( modelDataChanged() ), this, SLOT( relayout()) ); layoutDiagrams(); update(); } } AbstractDiagram* AbstractCoordinatePlane::diagram() { if ( d->diagrams.isEmpty() ) { return 0; } else { return d->diagrams.first(); } } AbstractDiagramList AbstractCoordinatePlane::diagrams() { return d->diagrams; } ConstAbstractDiagramList AbstractCoordinatePlane::diagrams() const { ConstAbstractDiagramList list; #ifndef QT_NO_STL qCopy( d->diagrams.begin(), d->diagrams.end(), std::back_inserter( list ) ); #else Q_FOREACH( AbstractDiagram * a, d->diagrams ) list.push_back( a ); #endif return list; } QSize KDChart::AbstractCoordinatePlane::minimumSizeHint() const { return QSize( 200, 200 ); } QSizePolicy KDChart::AbstractCoordinatePlane::sizePolicy() const { return QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding ); } void KDChart::AbstractCoordinatePlane::setGlobalGridAttributes( const GridAttributes& a ) { d->gridAttributes = a; update(); } GridAttributes KDChart::AbstractCoordinatePlane::globalGridAttributes() const { return d->gridAttributes; } KDChart::DataDimensionsList KDChart::AbstractCoordinatePlane::gridDimensionsList() { //KDChart::DataDimensionsList l( d->grid->updateData( this ) ); //qDebug() << "AbstractCoordinatePlane::gridDimensionsList() Y-range:" << l.last().end - l.last().start << " step width:" << l.last().stepWidth; //qDebug() << "AbstractCoordinatePlane::gridDimensionsList() X-range:" << l.first().end - l.first().start << " step width:" << l.first().stepWidth; return d->grid->updateData( this ); } void KDChart::AbstractCoordinatePlane::setGridNeedsRecalculate() { d->grid->setNeedRecalculate(); } void KDChart::AbstractCoordinatePlane::setReferenceCoordinatePlane( AbstractCoordinatePlane * plane ) { d->referenceCoordinatePlane = plane; } AbstractCoordinatePlane * KDChart::AbstractCoordinatePlane::referenceCoordinatePlane( ) const { return d->referenceCoordinatePlane; } void KDChart::AbstractCoordinatePlane::setParent( KDChart::Chart* parent ) { d->parent = parent; } const KDChart::Chart* KDChart::AbstractCoordinatePlane::parent() const { return d->parent; } KDChart::Chart* KDChart::AbstractCoordinatePlane::parent() { return d->parent; } /* pure virtual in QLayoutItem */ bool KDChart::AbstractCoordinatePlane::isEmpty() const { return false; // never empty! // coordinate planes with no associated diagrams // are showing a default grid of ()1..10, 1..10) stepWidth 1 } /* pure virtual in QLayoutItem */ Qt::Orientations KDChart::AbstractCoordinatePlane::expandingDirections() const { return Qt::Vertical | Qt::Horizontal; } /* pure virtual in QLayoutItem */ QSize KDChart::AbstractCoordinatePlane::maximumSize() const { // No maximum size set. Especially not parent()->size(), we are not layouting // to the parent widget's size when using Chart::paint()! return QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX); } /* pure virtual in QLayoutItem */ QSize KDChart::AbstractCoordinatePlane::minimumSize() const { return QSize(60, 60); // this default can be overwritten by derived classes } /* pure virtual in QLayoutItem */ QSize KDChart::AbstractCoordinatePlane::sizeHint() const { // we return our maxiumu (which is the full size of the Chart) // even if we know the plane will be smaller return maximumSize(); } /* pure virtual in QLayoutItem */ void KDChart::AbstractCoordinatePlane::setGeometry( const QRect& r ) { // qDebug() << "KDChart::AbstractCoordinatePlane::setGeometry(" << r << ") called"; if( d->geometry != r ){ //qDebug() << "entering KDChart::AbstractCoordinatePlane::setGeometry(" << r << ")"; // inform the outside word by Signal geometryChanged() // via a queued connection to internal_geometryChanged() emit internal_geometryChanged( d->geometry, r ); d->geometry = r; // Note: We do *not* call update() here // because it would invoke KDChart::update() recursively. //qDebug() << "leaving KDChart::AbstractCoordinatePlane::setGeometry(" << r << ")"; } } /* pure virtual in QLayoutItem */ QRect KDChart::AbstractCoordinatePlane::geometry() const { return d->geometry; } void KDChart::AbstractCoordinatePlane::update() { //qDebug("KDChart::AbstractCoordinatePlane::update() called"); emit needUpdate(); } void KDChart::AbstractCoordinatePlane::relayout() { //qDebug("KDChart::AbstractCoordinatePlane::relayout() called"); emit needRelayout(); } void KDChart::AbstractCoordinatePlane::layoutPlanes() { //qDebug("KDChart::AbstractCoordinatePlane::relayout() called"); emit needLayoutPlanes(); } void KDChart::AbstractCoordinatePlane::setRubberBandZoomingEnabled( bool enable ) { d->enableRubberBandZooming = enable; if( !enable && d->rubberBand != 0 ) { delete d->rubberBand; d->rubberBand = 0; } } bool KDChart::AbstractCoordinatePlane::isRubberBandZoomingEnabled() const { return d->enableRubberBandZooming; } void KDChart::AbstractCoordinatePlane::mousePressEvent( QMouseEvent* event ) { if( event->button() == Qt::LeftButton ) { if( d->enableRubberBandZooming && d->rubberBand == 0 ) d->rubberBand = new QRubberBand( QRubberBand::Rectangle, qobject_cast< QWidget* >( parent() ) ); if( d->rubberBand != 0 ) { d->rubberBandOrigin = event->pos(); d->rubberBand->setGeometry( QRect( event->pos(), QSize() ) ); d->rubberBand->show(); event->accept(); } } else if( event->button() == Qt::RightButton ) { if( d->enableRubberBandZooming && !d->rubberBandZoomConfigHistory.isEmpty() ) { // restore the last config from the stack ZoomParameters config = d->rubberBandZoomConfigHistory.pop(); setZoomFactorX( config.xFactor ); setZoomFactorY( config.yFactor ); setZoomCenter( config.center() ); QWidget* const p = qobject_cast< QWidget* >( parent() ); if( p != 0 ) p->update(); event->accept(); } } KDAB_FOREACH( AbstractDiagram * a, d->diagrams ) { a->mousePressEvent( event ); } } void KDChart::AbstractCoordinatePlane::mouseDoubleClickEvent( QMouseEvent* event ) { if( event->button() == Qt::RightButton ) { // othewise the second click gets lost // which is pretty annoying when zooming out fast mousePressEvent( event ); } KDAB_FOREACH( AbstractDiagram * a, d->diagrams ) { a->mouseDoubleClickEvent( event ); } } void KDChart::AbstractCoordinatePlane::mouseReleaseEvent( QMouseEvent* event ) { if( d->rubberBand != 0 ) { // save the old config on the stack d->rubberBandZoomConfigHistory.push( ZoomParameters( zoomFactorX(), zoomFactorY(), zoomCenter() ) ); // this is the height/width of the rubber band in pixel space const double rubberWidth = static_cast< double >( d->rubberBand->width() ); const double rubberHeight = static_cast< double >( d->rubberBand->height() ); if( rubberWidth > 0.0 && rubberHeight > 0.0 ) { // this is the center of the rubber band in pixel space const double rubberCenterX = static_cast< double >( d->rubberBand->geometry().center().x() - geometry().x() ); const double rubberCenterY = static_cast< double >( d->rubberBand->geometry().center().y() - geometry().y() ); // this is the height/width of the plane in pixel space const double myWidth = static_cast< double >( geometry().width() ); const double myHeight = static_cast< double >( geometry().height() ); // this describes the new center of zooming, relative to the plane pixel space const double newCenterX = rubberCenterX / myWidth / zoomFactorX() + zoomCenter().x() - 0.5 / zoomFactorX(); const double newCenterY = rubberCenterY / myHeight / zoomFactorY() + zoomCenter().y() - 0.5 / zoomFactorY(); // this will be the new zoom factor const double newZoomFactorX = zoomFactorX() * myWidth / rubberWidth; const double newZoomFactorY = zoomFactorY() * myHeight / rubberHeight; // and this the new center const QPointF newZoomCenter( newCenterX, newCenterY ); setZoomFactorX( newZoomFactorX ); setZoomFactorY( newZoomFactorY ); setZoomCenter( newZoomCenter ); } d->rubberBand->parentWidget()->update(); delete d->rubberBand; d->rubberBand = 0; event->accept(); } KDAB_FOREACH( AbstractDiagram * a, d->diagrams ) { a->mouseReleaseEvent( event ); } } void KDChart::AbstractCoordinatePlane::mouseMoveEvent( QMouseEvent* event ) { if( d->rubberBand != 0 ) { const QRect normalized = QRect( d->rubberBandOrigin, event->pos() ).normalized(); d->rubberBand->setGeometry( normalized & geometry() ); event->accept(); } KDAB_FOREACH( AbstractDiagram * a, d->diagrams ) { a->mouseMoveEvent( event ); } } #if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) const #endif bool KDChart::AbstractCoordinatePlane::isVisiblePoint( const QPointF& point ) const { return d->isVisiblePoint( this, point ); } AbstractCoordinatePlane* KDChart::AbstractCoordinatePlane::sharedAxisMasterPlane( QPainter* p ) { Q_UNUSED( p ); return this; } #if !defined(QT_NO_DEBUG_STREAM) #include "KDChartEnums.h" QDebug KDChart::operator<<( QDebug stream, const DataDimension& r ) { stream << "DataDimension(" << " start=" << r.start << " end=" << r.end << " sequence=" << KDChartEnums::granularitySequenceToString( r.sequence ) << " isCalculated=" << r.isCalculated << " calcMode=" << ( r.calcMode == AbstractCoordinatePlane::Logarithmic ? "Logarithmic" : "Linear" ) << " stepWidth=" << r.stepWidth << " subStepWidth=" << r.subStepWidth << " )"; return stream; } #endif #undef d