1 /***************************************************************************
2                              qgslayoutruler.h
3                              ----------------
4     Date                 : July 2017
5     Copyright            : (C) 2017 Nyall Dawson
6     Email                : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 #ifndef QGSLAYOUTRULER_H
16 #define QGSLAYOUTRULER_H
17 
18 #include "qgis_gui.h"
19 #include "qgis_sip.h"
20 #include <QWidget>
21 #include <QPointer>
22 #include <QMenu>
23 #include <memory>
24 
25 class QgsLayout;
26 class QGraphicsLineItem;
27 class QgsLayoutView;
28 class QgsLayoutGuide;
29 
30 /**
31  * \ingroup gui
32  * \brief A custom ruler widget for use with QgsLayoutView, displaying the
33  * current zoom and position of the visible layout and for interacting
34  * with guides in a layout.
35  *
36  * \since QGIS 3.0
37  */
38 class GUI_EXPORT QgsLayoutRuler: public QWidget
39 {
40     Q_OBJECT
41 
42   public:
43 
44     /**
45      * Constructor for QgsLayoutRuler, with the specified \a parent widget and \a orientation.
46      */
47     explicit QgsLayoutRuler( QWidget *parent SIP_TRANSFERTHIS = nullptr, Qt::Orientation orientation = Qt::Horizontal );
48 
49     QSize minimumSizeHint() const override;
50 
51     /**
52      * Sets the current scene \a transform. This is usually the transform set for a view
53      * showing the associated scene, in order to synchronize the view's display of
54      * the scene with the rulers.
55      */
56     void setSceneTransform( const QTransform &transform );
57 
58     /**
59      * Returns the current layout view associated with the ruler.
60      * \see setLayoutView()
61      */
layoutView()62     QgsLayoutView *layoutView() { return mView; }
63 
64     /**
65      * Sets the current layout \a view to synchronize the ruler with.
66      * \see layoutView()
67      */
68     void setLayoutView( QgsLayoutView *view );
69 
70     /**
71      * Returns the ruler size (either the height of a horizontal ruler or the
72      * width of a vertical rule).
73      */
rulerSize()74     int rulerSize() const { return mRulerMinSize; }
75 
76     /**
77      * Sets a context \a menu to show when right clicking occurs on the ruler.
78      * Ownership of \a menu is unchanged.
79      */
80     void setContextMenu( QMenu *menu );
81 
82   public slots:
83 
84     /**
85      * Updates the \a position of the marker showing the current mouse position within
86      * the view.
87      * \a position is in layout coordinates.
88      */
89     void setCursorPosition( QPointF position );
90 
91   signals:
92     //! Emitted when mouse cursor coordinates change
93     void cursorPosChanged( QPointF );
94 
95   protected:
96     void paintEvent( QPaintEvent *event ) override;
97     void mouseMoveEvent( QMouseEvent *event ) override;
98     void mousePressEvent( QMouseEvent *event ) override;
99     void mouseReleaseEvent( QMouseEvent *event ) override;
100 
101   private:
102     static const int VALID_SCALE_MULTIPLES[];
103     static const int VALID_SCALE_MAGNITUDES[];
104 
105     Qt::Orientation mOrientation = Qt::Horizontal;
106     QgsLayoutView *mView = nullptr;
107 
108     QTransform mTransform;
109     QPoint mMarkerPos;
110 
111     QFont mRulerFont;
112     std::unique_ptr< QFontMetrics > mRulerFontMetrics;
113 
114     double mScaleMinPixelsWidth = 0.0;
115     int mRulerMinSize;
116     int mMinPixelsPerDivision;
117     int mPixelsBetweenLineAndText;
118     int mTextBaseline;
119     int mMinSpacingVerticalLabels;
120 
121     int mDragGuideTolerance = 0;
122     QgsLayoutGuide *mDraggingGuide = nullptr;
123     QgsLayoutGuide *mHoverGuide = nullptr;
124 
125     bool mCreatingGuide = false;
126     QGraphicsLineItem *mGuideItem = nullptr;
127 
128     //! Polygon for drawing guide markers
129     QPolygonF mGuideMarker;
130 
131     QPointer< QMenu > mMenu;
132 
133     //! Calculates the optimum labeled units for ruler so that labels are a good distance apart
134     int optimumScale( double minPixelDiff, int &magnitude, int &multiple );
135 
136     /**
137      * Calculate the number of small divisions for each ruler unit, ensuring that they
138      * are sufficiently spaced.
139      */
140     int optimumNumberDivisions( double rulerScale, int scaleMultiple );
141 
142     //! Draws vertical text on a painter
143     void drawRotatedText( QPainter *painter, QPointF pos, const QString &text );
144 
145     /**
146      * Draws small ruler divisions.
147      * Starting at startPos in mm, for numDivisions divisions, with major division spacing of rulerScale (in mm)
148      * Stop drawing if position exceeds maxPos
149      */
150     void drawSmallDivisions( QPainter *painter, double startPos, int numDivisions, double rulerScale, double maxPos = 0 );
151 
152     //! Draw current marker pos on ruler
153     void drawMarkerPos( QPainter *painter );
154 
155     void drawGuideMarkers( QPainter *painter, QgsLayout *layout );
156 
157     //! Draw a guide marker on the ruler
158     void drawGuideAtPos( QPainter *painter, QPoint pos );
159 
160     void createTemporaryGuideItem();
161 
162     QPointF convertLocalPointToLayout( QPoint localPoint ) const;
163 
164     QPoint convertLayoutPointToLocal( QPointF layoutPoint ) const;
165 
166     /**
167      * Returns the closest guide to a local ruler point, or NULLPTR if no guides
168      * are within the acceptable tolerance of the point.
169      */
170     QgsLayoutGuide *guideAtPoint( QPoint localPoint ) const;
171 
172 };
173 
174 #endif // QGSLAYOUTRULER_H
175