1 /*
2     Scan Tailor - Interactive post-processing tool for scanned pages.
3     Copyright (C) 2007-2009  Joseph Artsimovich <joseph_a@mail.ru>
4 
5     This program is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #ifndef PAGE_LAYOUT_IMAGEVIEW_H_
20 #define PAGE_LAYOUT_IMAGEVIEW_H_
21 
22 #include <imageproc/BinaryImage.h>
23 #include <interaction/DraggableLineSegment.h>
24 #include <QMenu>
25 #include <QPoint>
26 #include <QPointF>
27 #include <QRectF>
28 #include <QSizeF>
29 #include <QTransform>
30 #include <unordered_map>
31 #include "Alignment.h"
32 #include "DragHandler.h"
33 #include "DraggableObject.h"
34 #include "Guide.h"
35 #include "ImageTransformation.h"
36 #include "ImageViewBase.h"
37 #include "InteractionHandler.h"
38 #include "ObjectDragHandler.h"
39 #include "PageId.h"
40 #include "ZoomHandler.h"
41 #include "intrusive_ptr.h"
42 
43 class Margins;
44 
45 namespace imageproc {
46 class GrayImage;
47 }
48 
49 namespace page_layout {
50 class OptionsWidget;
51 class Settings;
52 
53 class ImageView : public ImageViewBase, private InteractionHandler {
54   Q_OBJECT
55  public:
56   ImageView(const intrusive_ptr<Settings>& settings,
57             const PageId& page_id,
58             const QImage& image,
59             const QImage& downscaled_image,
60             const imageproc::GrayImage& gray_image,
61             const ImageTransformation& xform,
62             const QRectF& adapted_content_rect,
63             const OptionsWidget& opt_widget);
64 
65   ~ImageView() override;
66 
67  signals:
68 
69   void invalidateThumbnail(const PageId& page_id);
70 
71   void invalidateAllThumbnails();
72 
73   void marginsSetLocally(const Margins& margins_mm);
74 
75  public slots:
76 
77   void marginsSetExternally(const Margins& margins_mm);
78 
79   void leftRightLinkToggled(bool linked);
80 
81   void topBottomLinkToggled(bool linked);
82 
83   void alignmentChanged(const Alignment& alignment);
84 
85   void aggregateHardSizeChanged();
86 
87  protected:
88   void updatePhysSize() override;
89 
90  private:
91   enum Edge { LEFT = 1, RIGHT = 2, TOP = 4, BOTTOM = 8 };
92 
93   enum FitMode { FIT, DONT_FIT };
94 
95   enum AggregateSizeChanged { AGGREGATE_SIZE_UNCHANGED, AGGREGATE_SIZE_CHANGED };
96 
97   struct StateBeforeResizing {
98     /**
99      * Transformation from virtual image coordinates to widget coordinates.
100      */
101     QTransform virtToWidget;
102 
103     /**
104      * Transformation from widget coordinates to virtual image coordinates.
105      */
106     QTransform widgetToVirt;
107 
108     /**
109      * m_middleRect in widget coordinates.
110      */
111     QRectF middleWidgetRect;
112 
113     /**
114      * Mouse pointer position in widget coordinates.
115      */
116     QPointF mousePos;
117 
118     /**
119      * The point in image that is to be centered on the screen,
120      * in pixel image coordinates.
121      */
122     QPointF focalPoint;
123   };
124 
125   void onPaint(QPainter& painter, const InteractionState& interaction) override;
126 
127   void onContextMenuEvent(QContextMenuEvent* event, InteractionState& interaction) override;
128 
129   void onMouseDoubleClickEvent(QMouseEvent* event, InteractionState& interaction) override;
130 
131   Proximity cornerProximity(int edge_mask, const QRectF* box, const QPointF& mouse_pos) const;
132 
133   Proximity edgeProximity(int edge_mask, const QRectF* box, const QPointF& mouse_pos) const;
134 
135   void dragInitiated(const QPointF& mouse_pos);
136 
137   void innerRectDragContinuation(int edge_mask, const QPointF& mouse_pos);
138 
139   void middleRectDragContinuation(int edge_mask, const QPointF& mouse_pos);
140 
141   void dragFinished();
142 
143   void recalcBoxesAndFit(const Margins& margins_mm);
144 
145   void updatePresentationTransform(FitMode fit_mode);
146 
147   void forceNonNegativeHardMargins(QRectF& middle_rect) const;
148 
149   Margins calcHardMarginsMM() const;
150 
151   void recalcOuterRect();
152 
153   QSizeF origRectToSizeMM(const QRectF& rect) const;
154 
155   AggregateSizeChanged commitHardMargins(const Margins& margins_mm);
156 
157   void invalidateThumbnails(AggregateSizeChanged agg_size_changed);
158 
159   void setupContextMenuInteraction();
160 
161   void setupGuides();
162 
163   void addHorizontalGuide(double y);
164 
165   void addVerticalGuide(double x);
166 
167   void removeAllGuides();
168 
169   void removeGuide(int index);
170 
171   QTransform widgetToGuideCs() const;
172 
173   QTransform guideToWidgetCs() const;
174 
175   void syncGuidesSettings();
176 
177   void setupGuideInteraction(int index);
178 
179   QLineF guidePosition(int index) const;
180 
181   void guideMoveRequest(int index, QLineF line);
182 
183   void guideDragFinished();
184 
185   QLineF widgetGuideLine(int index) const;
186 
187   int getGuideUnderMouse(const QPointF& screenMousePos, const InteractionState& state) const;
188 
189   void enableGuidesInteraction(bool state);
190 
191   void forceInscribeGuides();
192 
193   Proximity rectProximity(const QRectF& box, const QPointF& mouse_pos) const;
194 
195   void innerRectMoveRequest(const QPointF& mouse_pos, Qt::KeyboardModifiers mask = Qt::NoModifier);
196 
197   void buildContentImage(const imageproc::GrayImage& gray_image, const ImageTransformation& xform);
198 
199   void attachContentToNearestGuide(const QPointF& pos, Qt::KeyboardModifiers mask = Qt::NoModifier);
200 
201   QRect findContentInArea(const QRect& area) const;
202 
203   void enableMiddleRectInteraction(bool state);
204 
205   bool isShowingMiddleRectEnabled() const;
206 
207 
208   DraggableObject m_innerCorners[4];
209   ObjectDragHandler m_innerCornerHandlers[4];
210   DraggableObject m_innerEdges[4];
211   ObjectDragHandler m_innerEdgeHandlers[4];
212 
213   DraggableObject m_middleCorners[4];
214   ObjectDragHandler m_middleCornerHandlers[4];
215   DraggableObject m_middleEdges[4];
216   ObjectDragHandler m_middleEdgeHandlers[4];
217 
218   DragHandler m_dragHandler;
219   ZoomHandler m_zoomHandler;
220 
221   intrusive_ptr<Settings> m_settings;
222 
223   const PageId m_pageId;
224 
225   /**
226    * Transformation between the pixel image coordinates and millimeters,
227    * assuming that point (0, 0) in pixel coordinates corresponds to point
228    * (0, 0) in millimeter coordinates.
229    */
230   const QTransform m_pixelsToMmXform;
231   const QTransform m_mmToPixelsXform;
232 
233   const ImageTransformation m_xform;
234 
235   /**
236    * Content box in virtual image coordinates.
237    */
238   const QRectF m_innerRect;
239 
240   /**
241    * \brief Content box + hard margins in virtual image coordinates.
242    *
243    * Hard margins are margins that will be there no matter what.
244    * Soft margins are those added to extend the page to match its
245    * size with other pages.
246    */
247   QRectF m_middleRect;
248 
249   /**
250    * \brief Content box + hard + soft margins in virtual image coordinates.
251    *
252    * Hard margins are margins that will be there no matter what.
253    * Soft margins are those added to extend the page to match its
254    * size with other pages.
255    */
256   QRectF m_outerRect;
257 
258   /**
259    * \brief Aggregate (max width + max height) hard page size.
260    *
261    * This one is for displaying purposes only.  It changes during
262    * dragging, and it may differ from what
263    * m_settings->getAggregateHardSizeMM() would return.
264    *
265    * \see m_committedAggregateHardSizeMM
266    */
267   QSizeF m_aggregateHardSizeMM;
268 
269   /**
270    * \brief Aggregate (max width + max height) hard page size.
271    *
272    * This one is supposed to be the cached version of what
273    * m_settings->getAggregateHardSizeMM() would return.
274    *
275    * \see m_aggregateHardSizeMM
276    */
277   QSizeF m_committedAggregateHardSizeMM;
278 
279   Alignment m_alignment;
280 
281   /**
282    * Some data saved at the beginning of a resizing operation.
283    */
284   StateBeforeResizing m_beforeResizing;
285 
286   bool m_leftRightLinked;
287   bool m_topBottomLinked;
288 
289   /** Guides settings. */
290   std::unordered_map<int, Guide> m_guides;
291   int m_guidesFreeIndex;
292 
293   std::unordered_map<int, DraggableLineSegment> m_draggableGuides;
294   std::unordered_map<int, ObjectDragHandler> m_draggableGuideHandlers;
295 
296   QMenu* m_contextMenu;
297   QAction* m_addHorizontalGuideAction;
298   QAction* m_addVerticalGuideAction;
299   QAction* m_removeAllGuidesAction;
300   QAction* m_removeGuideUnderMouseAction;
301   QAction* m_guideActionsSeparator;
302   QAction* m_showMiddleRectAction;
303   QPointF m_lastContextMenuPos;
304   int m_guideUnderMouse;
305 
306   DraggableObject m_innerRectArea;
307   ObjectDragHandler m_innerRectAreaHandler;
308 
309   Qt::KeyboardModifier m_innerRectVerticalDragModifier;
310   Qt::KeyboardModifier m_innerRectHorizontalDragModifier;
311 
312   imageproc::BinaryImage m_contentImage;
313   QTransform m_originalToContentImage;
314   QTransform m_contentImageToOriginal;
315 
316   const bool m_nullContentRect;
317 };
318 }  // namespace page_layout
319 #endif  // ifndef PAGE_LAYOUT_IMAGEVIEW_H_
320