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