1 /*
2     SPDX-FileCopyrightText: 2017 Tobias Deiminger <haxtibal@t-online.de>
3     SPDX-FileCopyrightText: 2004-2005 Enrico Ros <eros.kde@email.it>
4     SPDX-FileCopyrightText: 2004-2006 Albert Astals Cid <aacid@kde.org>
5 
6     Work sponsored by the LiMux project of the city of Munich:
7     SPDX-FileCopyrightText: 2017 Klarälvdalens Datakonsult AB a KDAB Group company <info@kdab.com>
8 
9     With portions of code from kpdf/kpdf_pagewidget.cc by:
10     SPDX-FileCopyrightText: 2002 Wilco Greven <greven@kde.org>
11     SPDX-FileCopyrightText: 2003 Christophe Devriese <Christophe.Devriese@student.kuleuven.ac.be>
12     SPDX-FileCopyrightText: 2003 Laurent Montel <montel@kde.org>
13     SPDX-FileCopyrightText: 2003 Dirk Mueller <mueller@kde.org>
14     SPDX-FileCopyrightText: 2004 James Ots <kde@jamesots.com>
15     SPDX-FileCopyrightText: 2011 Jiri Baum - NICTA <jiri@baum.com.au>
16 
17     SPDX-License-Identifier: GPL-2.0-or-later
18 */
19 
20 #ifndef _OKULAR_PAGEVIEWMOUSEANNOTATION_H_
21 #define _OKULAR_PAGEVIEWMOUSEANNOTATION_H_
22 
23 #include <QObject>
24 
25 #include "core/annotations.h"
26 #include "pageviewutils.h"
27 
28 class QHelpEvent;
29 class QPainter;
30 class QPoint;
31 class PageView;
32 class PageViewItem;
33 class AnnotationDescription;
34 
35 namespace Okular
36 {
37 class Document;
38 }
39 
40 /* This class shall help to keep data for one annotation consistent. */
41 class AnnotationDescription
42 {
43 public:
AnnotationDescription()44     AnnotationDescription()
45         : annotation(nullptr)
46         , pageViewItem(nullptr)
47         , pageNumber(-1)
48     {
49     }
50     AnnotationDescription(PageViewItem *newPageViewItem, const QPoint eventPos);
51     bool isValid() const;
52     bool isContainedInPage(const Okular::Document *document, int pageNumber) const;
53     void invalidate();
54     bool operator==(const AnnotationDescription &rhs) const
55     {
56         return (annotation == rhs.annotation);
57     }
58     Okular::Annotation *annotation;
59     PageViewItem *pageViewItem;
60     int pageNumber;
61 };
62 
63 /**
64  * @short Handle UI for annotation interactions, like moving, resizing and triggering actions.
65  *
66  * An object of this class tracks which annotation is currently under the mouse cursor.
67  * Some annotation types can be focused in order to move or resize them.
68  * State is determined from mouse and keyboard events, which are forwarded from the parent PageView object.
69  * Move and resize actions are dispatched to the Document object.
70  */
71 class MouseAnnotation : public QObject
72 {
73     Q_OBJECT
74 
75 public:
76     MouseAnnotation(PageView *parent, Okular::Document *document);
77     ~MouseAnnotation() override;
78 
79     /* Process a mouse press event. eventPos: Mouse position in content area coordinates. */
80     void routeMousePressEvent(PageViewItem *pageViewItem, const QPoint eventPos);
81 
82     /* Process a mouse release event. */
83     void routeMouseReleaseEvent();
84 
85     /* Process a mouse move event. eventPos: Mouse position in content area coordinates. */
86     void routeMouseMoveEvent(PageViewItem *pageViewItem, const QPoint eventPos, bool leftButtonPressed);
87 
88     /* Process a key event. */
89     void routeKeyPressEvent(const QKeyEvent *e);
90 
91     /* Process a tooltip event. eventPos: Mouse position in content area coordinates. */
92     void routeTooltipEvent(const QHelpEvent *helpEvent);
93 
94     /* Process a paint event. */
95     void routePaint(QPainter *painter, const QRect paintRect);
96 
97     /* Cancel the current selection or action, if any. */
98     void cancel();
99 
100     /* Reset to initial state. Cancel current action and relinquish references to PageViewItem widgets. */
101     void reset();
102 
103     Okular::Annotation *annotation() const;
104 
105     /* Return true, if MouseAnnotation demands control for a mouse click on the current cursor position. */
106     bool isMouseOver() const;
107 
108     bool isActive() const;
109 
110     bool isFocused() const;
111 
112     bool isMoved() const;
113 
114     bool isResized() const;
115 
116     bool isModified() const;
117 
118     Qt::CursorShape cursor() const;
119 
120     /* Forward DocumentObserver::notifyPageChanged to this method. */
121     void notifyAnnotationChanged(int pageNumber);
122 
123     /* Forward DocumentObserver::notifySetup to this method. */
124     void updateAnnotationPointers();
125 
126     enum MouseAnnotationState { StateInactive, StateFocused, StateMoving, StateResizing };
127 
128     enum ResizeHandleFlag {
129         RH_None = 0,
130         RH_Top = 1,
131         RH_Right = 2,
132         RH_Bottom = 4,
133         RH_Left = 8,
134         RH_TopLeft = RH_Top | RH_Left,
135         RH_BottomLeft = RH_Bottom | RH_Left,
136         RH_TopRight = RH_Top | RH_Right,
137         RH_BottomRight = RH_Bottom | RH_Right,
138         RH_Content = 16,
139         RH_AllHandles = RH_Top | RH_Right | RH_Bottom | RH_Left
140     };
141     Q_DECLARE_FLAGS(ResizeHandle, ResizeHandleFlag)
142 
143 private:
144     void setState(MouseAnnotationState state, const AnnotationDescription &ad);
145     QRect getFullBoundingRect(const AnnotationDescription &ad) const;
146     void performCommand(const QPoint newPos);
147     void finishCommand();
148     void updateViewport(const AnnotationDescription &ad) const;
149     ResizeHandle getHandleAt(const QPoint eventPos, const AnnotationDescription &ad) const;
150     QRect getHandleRect(ResizeHandle handle, const AnnotationDescription &ad) const;
151     static void handleToAdjust(const QPointF dIn, QPointF &dOut1, QPointF &dOut2, MouseAnnotation::ResizeHandle handle, Okular::Rotation rotation);
152     static QPointF rotateInRect(const QPointF rotated, Okular::Rotation rotation);
153     static ResizeHandle rotateHandle(ResizeHandle handle, Okular::Rotation rotation);
154     void processAction(const AnnotationDescription &ad);
155 
156     /* We often have to delegate to the document model and our parent widget. */
157     Okular::Document *m_document;
158     PageView *m_pageView;
159 
160     /* Remember which annotation is currently focused/modified. */
161     MouseAnnotationState m_state;
162     MouseAnnotation::ResizeHandle m_handle;
163     AnnotationDescription m_focusedAnnotation;
164 
165     /* Mouse tracking, always kept up to date with the latest mouse position and annotation under mouse cursor. */
166     AnnotationDescription m_mouseOverAnnotation;
167     QPoint m_mousePosition; // in page view item coordinates
168 
169     QList<ResizeHandle> m_resizeHandleList;
170 };
171 
172 #endif
173