1 /**
2  * Copyright (C) 2012  Stefan Löffler
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the Free
6  * Software Foundation; either version 2, or (at your option) any later
7  * version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  */
14 #ifndef PDFDocumentTools_H
15 #define PDFDocumentTools_H
16 
17 #include <QKeyEvent>
18 #include <QMouseEvent>
19 #include <QPaintEvent>
20 #include <QCursor>
21 #include <QRubberBand>
22 #include <QComboBox>
23 #include <QGraphicsView>
24 #include <QGraphicsLineItem>
25 #include <QGraphicsProxyWidget>
26 
27 #include "PDFBackend.h"
28 
29 #ifdef DEBUG
30   #include <QDebug>
31 #endif
32 
33 namespace QtPDF {
34 
35 class PDFDocumentView;
36 class PDFDocumentMagnifierView;
37 
38 namespace DocumentTool {
39 
40 class AbstractTool
41 {
42   friend class QtPDF::PDFDocumentView;
43 public:
44   enum Type { Tool_None, Tool_MagnifyingGlass, Tool_ZoomIn, Tool_ZoomOut, Tool_MarqueeZoom, Tool_Move, Tool_ContextMenu, Tool_ContextClick, Tool_Measure, Tool_Select };
AbstractTool(PDFDocumentView * parent)45   AbstractTool(PDFDocumentView * parent) : _parent(parent), _cursor(QCursor(Qt::ArrowCursor)) { }
~AbstractTool()46   virtual ~AbstractTool() { }
47 
type()48   virtual Type type() const { return Tool_None; }
49   virtual bool operator==(const AbstractTool & o) { return (type() == o.type()); }
50 protected:
51   virtual void arm();
52   virtual void disarm();
53 
54   // By default, key events will call the parent view's maybeArmTool(). Derived
55   // classes that rely on key events can override this behavior to handle
56   // certain key events itself.
57   virtual void keyPressEvent(QKeyEvent *event);
58   virtual void keyReleaseEvent(QKeyEvent *event);
59   virtual void mousePressEvent(QMouseEvent * event);
mouseMoveEvent(QMouseEvent * event)60   virtual void mouseMoveEvent(QMouseEvent * event) { }
61   virtual void mouseReleaseEvent(QMouseEvent * event);
paintEvent(QPaintEvent * event)62   virtual void paintEvent(QPaintEvent * event) { }
63 
64   PDFDocumentView * _parent;
65   QCursor _cursor;
66 };
67 
68 class ZoomIn : public AbstractTool
69 {
70 public:
71   ZoomIn(PDFDocumentView * parent);
type()72   virtual Type type() const { return Tool_ZoomIn; }
73 protected:
arm()74   virtual void arm() { AbstractTool::arm(); _started = false; }
disarm()75   virtual void disarm() { AbstractTool::disarm(); _started = false; }
76 
77   virtual void mousePressEvent(QMouseEvent * event);
78   virtual void mouseReleaseEvent(QMouseEvent * event);
79   QPoint _startPos;
80   bool _started;
81 };
82 
83 class ZoomOut : public AbstractTool
84 {
85 public:
86   ZoomOut(PDFDocumentView * parent);
type()87   virtual Type type() const { return Tool_ZoomOut; }
88 protected:
arm()89   virtual void arm() { AbstractTool::arm(); _started = false; }
disarm()90   virtual void disarm() { AbstractTool::disarm(); _started = false; }
91 
92   virtual void mousePressEvent(QMouseEvent * event);
93   virtual void mouseReleaseEvent(QMouseEvent * event);
94   QPoint _startPos;
95   bool _started;
96 };
97 
98 class MagnifyingGlass : public AbstractTool
99 {
100 public:
101   enum MagnifierShape { Magnifier_Rectangle, Magnifier_Circle };
102   MagnifyingGlass(PDFDocumentView * parent);
type()103   virtual Type type() const { return Tool_MagnifyingGlass; }
magnifier()104   PDFDocumentMagnifierView * magnifier() { return _magnifier; }
105 
106   void setMagnifierShape(const MagnifierShape shape);
107   void setMagnifierSize(const int size);
108 
109 protected:
arm()110   virtual void arm() { AbstractTool::arm(); _started = false; }
disarm()111   virtual void disarm() { AbstractTool::disarm(); _started = false; }
112 
113   virtual void mousePressEvent(QMouseEvent * event);
114   virtual void mouseMoveEvent(QMouseEvent * event);
115   virtual void mouseReleaseEvent(QMouseEvent * event);
116   virtual void paintEvent(QPaintEvent * event);
117 
118   PDFDocumentMagnifierView * _magnifier;
119   bool _started;
120 };
121 
122 class MarqueeZoom : public AbstractTool
123 {
124 public:
125   MarqueeZoom(PDFDocumentView * parent);
type()126   virtual Type type() const { return Tool_MarqueeZoom; }
127 protected:
arm()128   virtual void arm() { AbstractTool::arm(); _started = false; }
disarm()129   virtual void disarm() { AbstractTool::disarm(); _started = false; }
130 
131   virtual void mousePressEvent(QMouseEvent * event);
132   virtual void mouseMoveEvent(QMouseEvent * event);
133   virtual void mouseReleaseEvent(QMouseEvent * event);
134 
135   bool _started;
136   QPoint _startPos;
137   QRubberBand * _rubberBand;
138 };
139 
140 class Move : public AbstractTool
141 {
142 public:
143   Move(PDFDocumentView * parent);
type()144   virtual Type type() const { return Tool_Move; }
145 protected:
arm()146   virtual void arm() { AbstractTool::arm(); _started = false; }
disarm()147   virtual void disarm() { AbstractTool::disarm(); _started = false; }
148 
149   virtual void mousePressEvent(QMouseEvent * event);
150   virtual void mouseMoveEvent(QMouseEvent * event);
151   virtual void mouseReleaseEvent(QMouseEvent * event);
152 
153   bool _started;
154   QPoint _oldPos;
155   QCursor _closedHandCursor;
156 };
157 
158 class ContextClick : public AbstractTool
159 {
160 public:
ContextClick(PDFDocumentView * parent)161   ContextClick(PDFDocumentView * parent) : AbstractTool(parent), _started(false) { }
type()162   virtual Type type() const { return Tool_ContextClick; }
163 protected:
164   virtual void mousePressEvent(QMouseEvent * event);
165   virtual void mouseReleaseEvent(QMouseEvent * event);
166   bool _started;
167 };
168 
169 class Measure;
170 class MeasureLine;
171 
172 class MeasureLineGrip : public QGraphicsRectItem
173 {
174   friend class Measure;
175 public:
176   // pt should be 1 for the first handle, 2 for the second handle
177   MeasureLineGrip(MeasureLine * parent, const int pt);
178 protected:
179   virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
180   virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
181   virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
182   virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
183 
184   // to be called from friend class Measure during creation of the MeasureLine
185   void mouseMove(const QPointF scenePos, const Qt::KeyboardModifiers modifiers);
186 
187   // 1 for the first handle, 2 for the second handle
188   int _pt;
189 };
190 
191 class MeasureLine : public QGraphicsLineItem
192 {
193   friend class Measure;
194 public:
195   MeasureLine(QGraphicsView * primaryView, QGraphicsItem * parent = NULL);
~MeasureLine()196   virtual ~MeasureLine() { }
197 
setLine(qreal x1,qreal y1,qreal x2,qreal y2)198   void setLine(qreal x1, qreal y1, qreal x2, qreal y2) { setLine(QLineF(x1, y1, x2, y2)); }
setLine(QPointF p1,QPointF p2)199   void setLine(QPointF p1, QPointF p2) { setLine(QLineF(p1, p2)); }
200   void setLine(QLineF line);
201   virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
202 protected:
203   void updateMeasurement();
204   void updateMeasureBoxPos();
205 
206   virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
207   virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
208   virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
209   virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
210 
211   QComboBox * _measureBox;
212   QGraphicsProxyWidget * _measureBoxProxy;
213   MeasureLineGrip * _grip1, * _grip2;
214   QMap<QString, float> _measures;
215   QGraphicsView * _primaryView;
216   QPointF _grabOffset;
217 };
218 
219 class Measure : public AbstractTool
220 {
221 public:
222   Measure(PDFDocumentView * parent);
type()223   virtual Type type() const { return Tool_Measure; }
224 protected:
225   virtual void mousePressEvent(QMouseEvent * event);
226   virtual void mouseMoveEvent(QMouseEvent *event);
227   virtual void mouseReleaseEvent(QMouseEvent * event);
228   virtual void keyPressEvent(QKeyEvent *event);
229   virtual void keyReleaseEvent(QKeyEvent *event);
230 
231   MeasureLine * _measureLine;
232   bool _started;
233   QPoint _startPos;
234 };
235 
236 // Text selection tool
237 // Supports:
238 // - "line based" selection (i.e., selects all boxes from the box the mouse
239 //   press event occured over to the closest box to the mouse when it was
240 //   released (or the current position if the mouse button is still pressed);
241 //   relies on the ordering of boxes in the pdf file
242 // - marquee selection (selects all boxes inside a rectangle drawn by the user
243 // - Ctrl+C to copy selected text (if supported by backend)
244 //
245 // TODO: Marquee selection is slow for large rectangles
246 // TODO: Handle selections spanning multiple pages
247 // TODO: possibly load boxes asynchronously
248 // TODO: possibly support Ctrl+A to select all, etc.
249 // TODO: possibly support image selection (like in Adobe Reader), e.g. using a
250 //       keyboard modifier
251 // TODO: when the application/widget loses focus, the highlight color should
252 //       (temporarily) be switched to QPalette::Inactive/QPalette::Highlight
253 // TOOO: when scrolling (e.g., with the mouse wheel) to a new page but not
254 //       moving the mouse, the boxes information is not updated
255 class Select : public AbstractTool
256 {
257   friend class QtPDF::PDFDocumentView;
258 public:
259   Select(PDFDocumentView * parent);
260   virtual ~Select();
type()261   virtual Type type() const { return Tool_Select; }
262 
highlightColor()263   QColor highlightColor() const { return _highlightColor; }
264   void setHighlightColor(const QColor & color);
265 protected:
266   virtual void disarm();
267   virtual void mousePressEvent(QMouseEvent * event);
268   virtual void mouseMoveEvent(QMouseEvent *event);
269   virtual void mouseReleaseEvent(QMouseEvent * event);
270   virtual void keyPressEvent(QKeyEvent *event);
271 
272   void resetBoxes(const int pageNum = -1);
273   // Call this to notify Select that the page graphics item it has been working
274   // on has been destroyed so all pointers to graphics items should be
275   // invalidated
276   void pageDestroyed();
277 
278   // The mouse mode depends on whether the LMB is pressed, and where/how it was
279   // pressed initially (e.g., over a box, over a free area, etc.)
280   enum MouseMode { MouseMode_None, MouseMode_MarqueeSelect, MouseMode_TextSelect, MouseMode_ImageSelect };
281 
282   bool _cursorOverBox;
283   QPointF _startPos;
284   QGraphicsPathItem * _highlightPath;
285   MouseMode _mouseMode;
286   QRubberBand * _rubberBand;
287   QColor _highlightColor;
288 
289   int _pageNum;
290   QList<Backend::Page::Box> _boxes;
291   int _startBox, _startSubbox;
292 #ifdef DEBUG
293   QList<QGraphicsRectItem*> _displayBoxes;
294 #endif
295 };
296 
297 } // namepsace DocumentTool
298 
299 } // namespace QtPDF
300 
301 #endif // End header guard
302 // vim: set sw=2 ts=2 et
303 
304