File indexing completed on 2024-05-12 04:20:28

0001 /*
0002  * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
0003  *
0004  * This file is part of the KD Chart library.
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #ifndef KCHARTABSTRACTCOORDINATEPLANE_H
0010 #define KCHARTABSTRACTCOORDINATEPLANE_H
0011 
0012 #include <QObject>
0013 #include <QList>
0014 
0015 #include "KChartAbstractArea.h"
0016 #include "KChartAbstractDiagram.h"
0017 #include "KChartEnums.h"
0018 
0019 namespace KChart {
0020 
0021     class Chart;
0022     class GridAttributes;
0023     class DataDimension;
0024 
0025     typedef QList<DataDimension> DataDimensionsList;
0026 
0027     /**
0028       * @brief Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane, TernaryCoordinatePlane
0029       */
0030     class KCHART_EXPORT AbstractCoordinatePlane : public AbstractArea
0031     {
0032         Q_OBJECT
0033 
0034         KCHART_DECLARE_PRIVATE_DERIVED_PARENT( AbstractCoordinatePlane, Chart* )
0035 
0036     friend class AbstractGrid;
0037 
0038     public:
0039         enum AxesCalcMode { Linear, Logarithmic };
0040 
0041     protected:
0042         explicit AbstractCoordinatePlane( Chart* parent = nullptr );
0043 
0044     public:
0045         ~AbstractCoordinatePlane() override;
0046 
0047         /**
0048          * Adds a diagram to this coordinate plane.
0049          * @param diagram The diagram to add.
0050          *
0051          * \sa replaceDiagram, takeDiagram
0052          */
0053         virtual void addDiagram( AbstractDiagram* diagram );
0054 
0055         /**
0056          * Replaces the old diagram, or appends the
0057          * diagram, it there is none yet.
0058          *
0059          * @param diagram The diagram to be used instead of the old diagram.
0060          * This parameter must not be zero, or the method will do nothing.
0061          *
0062          * @param oldDiagram The diagram to be removed by the new diagram. This
0063          * diagram will be deleted automatically. If the parameter is omitted,
0064          * the very first diagram will be replaced. In case, there was no
0065          * diagram yet, the new diagram will just be added.
0066          *
0067          * \note If you want to re-use the old diagram, call takeDiagram and
0068          * addDiagram, instead of using replaceDiagram.
0069          *
0070          * \sa addDiagram, takeDiagram
0071          */
0072         virtual void replaceDiagram( AbstractDiagram* diagram, AbstractDiagram* oldDiagram = nullptr );
0073 
0074         /**
0075          * Removes the diagram from the plane, without deleting it.
0076          *
0077          * The plane no longer owns the diagram, so it is
0078          * the caller's responsibility to delete the diagram.
0079          *
0080          * \sa addDiagram, replaceDiagram
0081         */
0082         virtual void takeDiagram( AbstractDiagram* diagram );
0083 
0084         /**
0085          * @return The first diagram associated with this coordinate plane.
0086          */
0087         AbstractDiagram* diagram();
0088 
0089         /**
0090          * @return The list of diagrams associated with this coordinate plane.
0091          */
0092         AbstractDiagramList diagrams();
0093 
0094         /**
0095          * @return The list of diagrams associated with this coordinate plane.
0096          */
0097         ConstAbstractDiagramList diagrams() const;
0098 
0099         /**
0100          * Distribute the available space among the diagrams and axes.
0101          */
0102         virtual void layoutDiagrams() = 0;
0103 
0104         /**
0105          * Translate the given point in value space coordinates to a position
0106          * in pixel space.
0107          * @param diagramPoint The point in value coordinates.
0108          * @returns The translated point.
0109          */
0110         virtual const QPointF translate( const QPointF& diagramPoint ) const = 0;
0111 
0112         /**
0113          * @return Whether zooming with a rubber band using the mouse is enabled.
0114          */
0115         bool isRubberBandZoomingEnabled() const;
0116 
0117         /**
0118          * Enables or disables zooming with a rubber band using the mouse.
0119          */
0120         void setRubberBandZoomingEnabled( bool enable );
0121 
0122         /**
0123          * @return The zoom factor in horizontal direction, that is applied
0124          * to all coordinate transformations.
0125          */
0126         virtual qreal zoomFactorX() const { return 1.0; }
0127 
0128         /**
0129          * @return The zoom factor in vertical direction, that is applied
0130          * to all coordinate transformations.
0131          */
0132         virtual qreal zoomFactorY() const { return 1.0; }
0133 
0134         /**
0135          * Sets both zoom factors in one go.
0136          * \sa setZoomFactorX,setZoomFactorY
0137          */
0138         virtual void setZoomFactors( qreal factorX, qreal factorY ) { Q_UNUSED( factorX ); Q_UNUSED( factorY ); }
0139 
0140         /**
0141          * Sets the zoom factor in horizontal direction, that is applied
0142          * to all coordinate transformations.
0143          * @param factor The new zoom factor
0144          */
0145         virtual void setZoomFactorX( qreal  factor ) { Q_UNUSED( factor ); }
0146 
0147         /**
0148          * Sets the zoom factor in vertical direction, that is applied
0149          * to all coordinate transformations.
0150          * @param factor The new zoom factor
0151          */
0152         virtual void setZoomFactorY( qreal factor ) { Q_UNUSED( factor ); }
0153 
0154         /**
0155          * @return The center point (in value coordinates) of the
0156          * coordinate plane, that is used for zoom operations.
0157          */
0158         virtual QPointF zoomCenter() const { return QPointF(0.0, 0.0); }
0159 
0160         /**
0161          * Set the point (in value coordinates) to be used as the
0162          * center point in zoom operations.
0163          * @param center The point to use.
0164          */
0165         virtual void setZoomCenter( const QPointF& center ) { Q_UNUSED( center ); }
0166 
0167         /**
0168          * Set the grid attributes to be used by this coordinate plane.
0169          * To disable grid painting, for example, your code should like this:
0170          * \code
0171          * GridAttributes ga = plane->globalGridAttributes();
0172          * ga.setGlobalGridVisible( false );
0173          * plane->setGlobalGridAttributes( ga );
0174          * \endcode
0175          * \sa globalGridAttributes
0176          * \sa CartesianCoordinatePlane::setGridAttributes
0177          */
0178         void setGlobalGridAttributes( const GridAttributes & );
0179 
0180         /**
0181          * @return The grid attributes used by this coordinate plane.
0182          * \sa setGlobalGridAttributes
0183          * \sa CartesianCoordinatePlane::gridAttributes
0184          */
0185         GridAttributes globalGridAttributes() const;
0186 
0187         /**
0188          * Returns the dimensions used for drawing the grid lines.
0189          *
0190          * Returned data is the result of (cached) grid calculations,
0191          * so - if you need that information for your own tasks - make sure to
0192          * call again this function after every data modification that has changed
0193          * the data range, since grid calculation is based upon the data range,
0194          * thus the grid start/end might have changed if the data was changed.
0195          *
0196          * @note Returned list will contain different numbers of DataDimension,
0197          * depending on the kind of coordinate plane used.
0198          * For CartesianCoordinatePlane two DataDimension are returned: the first
0199          * representing grid lines in X direction (matching the Abscissa axes)
0200          * and the second indicating vertical grid lines (or Ordinate axes, resp.).
0201          *
0202          * @return The dimensions used for drawing the grid lines.
0203          * @sa DataDimension
0204          */
0205         DataDimensionsList gridDimensionsList();
0206 
0207         /**
0208          * Set another coordinate plane to be used as the reference plane
0209          * for this one.
0210          * @param plane The coordinate plane to be used the reference plane
0211          * for this one.
0212          * @see referenceCoordinatePlane
0213          */
0214         void setReferenceCoordinatePlane( AbstractCoordinatePlane * plane );
0215 
0216         /**
0217          * There are two ways, in which planes can be caused to interact, in
0218          * where they are put layouting wise: The first is the reference plane. If
0219          * such a reference plane is set, on a plane, it will use the same cell in the
0220          * layout as that one. In addition to this, planes can share an axis. In that case
0221          * they will be laid out in relation to each other as suggested by the position
0222          * of the axis. If, for example Plane1 and Plane2 share an axis at position Left,
0223          * that will result in the layout: Axis Plane1 Plane 2, vertically. If Plane1
0224          * also happens to be Plane2's reference plane, both planes are drawn over each
0225          * other. The reference plane concept allows two planes to share the same space
0226          * even if neither has any axis, and in case there are shared axis, it is used
0227          * to decided, whether the planes should be painted on top of each other or
0228          * laid out vertically or horizontally next to each other.
0229          * @return The reference coordinate plane associated with this one.
0230          */
0231         AbstractCoordinatePlane * referenceCoordinatePlane() const;
0232 
0233         /**
0234          * @return Whether this plane should have spacers in the corners
0235          * formed by the presence of axes.
0236          */
0237         bool isCornerSpacersEnabled() const;
0238 
0239         /**
0240          * Enables or disables the use of spacers in the plane corners.
0241          */
0242         void setCornerSpacersEnabled( bool enable );
0243 
0244         virtual AbstractCoordinatePlane* sharedAxisMasterPlane( QPainter* p = nullptr ); // KChart 3: const method?
0245 
0246 
0247         /** pure virtual in QLayoutItem */
0248         bool isEmpty() const override;
0249         /** pure virtual in QLayoutItem */
0250         Qt::Orientations expandingDirections() const override;
0251         /** pure virtual in QLayoutItem */
0252         QSize maximumSize() const override;
0253         /** pure virtual in QLayoutItem */
0254         QSize minimumSize() const override;
0255         /** pure virtual in QLayoutItem */
0256         QSize sizeHint() const override;
0257         /** pure virtual in QLayoutItem
0258           *
0259           * \note Do not call this function directly, unless you know
0260           * exactly what you are doing.  Geometry management is done
0261           * by KChart's internal layouting measures.
0262           */
0263         void setGeometry( const QRect& r ) override;
0264         /** pure virtual in QLayoutItem */
0265         QRect geometry() const override;
0266 
0267         virtual void mousePressEvent( QMouseEvent* event );
0268         virtual void mouseDoubleClickEvent( QMouseEvent* event );
0269         virtual void mouseMoveEvent( QMouseEvent* event );
0270         virtual void mouseReleaseEvent( QMouseEvent* event );
0271 
0272         /**
0273           * Called internally by KChart::Chart
0274           */
0275         void setParent( Chart* parent );
0276         Chart* parent();
0277         const Chart* parent() const;
0278 
0279         /**
0280          * Tests, if a point is visible on the coordinate plane.
0281          *
0282          * \note Before calling this function the point must have been translated into coordinate plane space.
0283          */
0284 #if defined(Q_COMPILER_MANGLES_RETURN_TYPE)
0285         const bool isVisiblePoint( const QPointF& point ) const;
0286 #else
0287         bool isVisiblePoint( const QPointF& point ) const;
0288 #endif
0289 
0290     public Q_SLOTS:
0291         /**
0292           * Calling update() on the plane triggers the global KChart::Chart::update()
0293           */
0294         void update();
0295         /**
0296           * Calling relayout() on the plane triggers the global KChart::Chart::slotRelayout()
0297           */
0298         void relayout();
0299         /**
0300           * Calling layoutPlanes() on the plane triggers the global KChart::Chart::slotLayoutPlanes()
0301           */
0302         void layoutPlanes();
0303         /**
0304          * Used by the chart to clear the cached grid data.
0305          */
0306         void setGridNeedsRecalculate();
0307 
0308     Q_SIGNALS:
0309         /** Emitted when this coordinate plane is destroyed. */
0310         void destroyedCoordinatePlane( KChart::AbstractCoordinatePlane* );
0311 
0312         /** Emitted when plane needs to update its drawings. */
0313         void needUpdate();
0314 
0315         /** Emitted when plane needs to trigger the Chart's layouting. */
0316         void needRelayout();
0317 
0318         /** Emitted when plane needs to trigger the Chart's layouting of the coord. planes. */
0319         void needLayoutPlanes();
0320 
0321         /** Emitted upon change of a property of the Coordinate Plane or any of its components. */
0322         void propertiesChanged();
0323 
0324         void boundariesChanged();
0325 
0326         /** Emitted after the geometry of the Coordinate Plane has been changed.
0327          *  and control has returned to the event loop.
0328          *
0329          * Parameters are the old geometry, the new geometry.
0330         */
0331         void geometryChanged( QRect, QRect );
0332 
0333     private:
0334     Q_SIGNALS:
0335         // Emitted from inside the setGeometry()
0336         // This is connected via QueuedConnection to the geometryChanged() Signal
0337         // that users can connect to safely then.
0338         void internal_geometryChanged( QRect, QRect );
0339         /** Emitted upon change of the view coordinate system */
0340         void viewportCoordinateSystemChanged();
0341 
0342     protected:
0343         virtual DataDimensionsList getDataDimensionsList() const = 0;
0344 
0345         //KCHART_DECLARE_PRIVATE_DERIVED( AbstractCoordinatePlane )
0346     };
0347 
0348     /**
0349      * \brief Helper class for one dimension of data, e.g. for the rows in a data model,
0350      * or for the labels of an axis, or for the vertical lines in a grid.
0351      *
0352      * isCalculated specifies whether this dimension's values are calculated or counted.
0353      * (counted == "Item 1", "Item 2", "Item 3" ...)
0354      *
0355      * sequence is the GranularitySequence, as specified at for the respective
0356      * coordinate plane.
0357      *
0358      * Step width is an optional parameter, to be omitted (or set to Zero, resp.)
0359      * if the step width is unknown.
0360      *
0361      * The default c'tor just gets you counted values from 1..10, using step width 1,
0362      * used by the CartesianGrid, when showing an empty plane without any diagrams.
0363      */
0364     class DataDimension{
0365     public:
0366         DataDimension()
0367             : start( 1.0 )
0368             , end( 10.0 )
0369             , isCalculated( false )
0370             , calcMode( AbstractCoordinatePlane::Linear )
0371             , sequence( KChartEnums::GranularitySequence_10_20 )
0372             , stepWidth( 1.0 )
0373             , subStepWidth( 0.0 )
0374         {}
0375         DataDimension( qreal start_,
0376                        qreal end_,
0377                        bool isCalculated_,
0378                        AbstractCoordinatePlane::AxesCalcMode calcMode_,
0379                        KChartEnums::GranularitySequence sequence_,
0380                        qreal stepWidth_=0.0,
0381                        qreal subStepWidth_=0.0 )
0382             : start( start_ )
0383             , end( end_ )
0384             , isCalculated( isCalculated_ )
0385             , calcMode( calcMode_ )
0386             , sequence( sequence_ )
0387             , stepWidth( stepWidth_ )
0388             , subStepWidth( subStepWidth_ )
0389         {}
0390         /**
0391           * Returns the size of the distance,
0392           * equivalent to the width() (or height(), resp.) of a QRectF.
0393           *
0394           * Note that this value can be negative, e.g. indicating axis labels
0395           * going in reversed direction.
0396           */
0397         qreal distance() const
0398         {
0399             return end-start;
0400         }
0401 
0402         bool operator==( const DataDimension& r ) const
0403         {
0404             return
0405                 (start        == r.start) &&
0406                 (end          == r.end) &&
0407                 (sequence     == r.sequence) &&
0408                 (isCalculated == r.isCalculated) &&
0409                 (calcMode     == r.calcMode) &&
0410                 (stepWidth    == r.stepWidth) &&
0411                 (subStepWidth    == r.subStepWidth);
0412         }
0413 
0414         bool operator!=( const DataDimension& other ) const
0415         { return !operator==( other ); }
0416 
0417 
0418         qreal start;
0419         qreal end;
0420         bool  isCalculated;
0421         AbstractCoordinatePlane::AxesCalcMode calcMode;
0422         KChartEnums::GranularitySequence sequence;
0423         qreal stepWidth;
0424         qreal subStepWidth;
0425     };
0426 
0427 #if !defined(QT_NO_DEBUG_STREAM)
0428     QDebug operator<<( QDebug stream, const DataDimension& r );
0429 #endif
0430 
0431 }
0432 #endif