1 /*
2  *    Copyright 2012, 2014 Thomas Schöps
3  *    Copyright 2013-2017 Kai Pastor
4  *
5  *    This file is part of OpenOrienteering.
6  *
7  *    OpenOrienteering is free software: you can redistribute it and/or modify
8  *    it under the terms of the GNU General Public License as published by
9  *    the Free Software Foundation, either version 3 of the License, or
10  *    (at your option) any later version.
11  *
12  *    OpenOrienteering is distributed in the hope that it will be useful,
13  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *    GNU General Public License for more details.
16  *
17  *    You should have received a copy of the GNU General Public License
18  *    along with OpenOrienteering.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 
22 #ifndef OPENORIENTEERING_MAP_EDITOR_TOOL_BASE_H
23 #define OPENORIENTEERING_MAP_EDITOR_TOOL_BASE_H
24 
25 #include <memory>
26 #include <set>
27 #include <utility>
28 #include <vector>
29 
30 #include <Qt>
31 #include <QCursor>
32 #include <QObject>
33 #include <QPoint>
34 #include <QPointF>
35 #include <QString>
36 
37 #include <QPointer>
38 
39 #include "core/map_coord.h"
40 #include "core/objects/object.h"
41 #include "tools/tool.h"
42 
43 class QAction;
44 class QKeyEvent;
45 class QMouseEvent;
46 class QPainter;
47 class QRectF;
48 
49 namespace OpenOrienteering {
50 
51 class ConstrainAngleToolHelper;
52 class KeyButtonBar;
53 class MapEditorController;
54 class MapRenderables;
55 class MapWidget;
56 class SnappingToolHelper;
57 
58 
59 /**
60  * Provides a simple interface to base tools on.
61  *
62  * Look at the top of the protected section for the methods to override,
63  * and below that for some helper methods.
64  */
65 class MapEditorToolBase : public MapEditorTool
66 {
67 Q_OBJECT
68 
69 	/**
70 	 * An edited item consist of an active object and a corresponding duplicate.
71 	 *
72 	 * The active object is owned by the map.
73 	 * The duplicate is owned by this item.
74 	 * The duplicate may be used when for restoring the active's object's data
75 	 * when aborting editing,
76 	 * or it can be transferred to an undo step when committing the changes.
77 	 */
78 	struct EditedItem
79 	{
80 		Object* active_object;
81 		std::unique_ptr<Object> duplicate;
82 
83 		// Convenience constructor
84 		EditedItem(Object* active_object);
85 
86 		// All methods which are needed for efficient containers
87 		EditedItem() noexcept = default;
88 		EditedItem(const EditedItem& prototype);
89 		EditedItem(EditedItem&& prototype) noexcept;
90 		EditedItem& operator=(const EditedItem& prototype);
91 		EditedItem& operator=(EditedItem&& prototype) noexcept;
92 
93 		~EditedItem() = default;
94 
95 		bool isModified() const;
96 	};
97 
98 public:
99 	/**
100 	 * An accessor to the active objects in a std::vector<EditedItem>.
101 	 *
102 	 * This makes the edited objects available in range-for loops
103 	 * by providing forward iteration, but hides the implementation details.
104 	 */
105 	class ObjectsRange
106 	{
107 	private:
108 		using container = std::vector<EditedItem>;
109 		container::iterator range_begin;
110 		container::iterator range_end;
111 
112 	public:
ObjectsRange(container & items)113 		explicit ObjectsRange(container& items) : range_begin { items.begin() }, range_end { items.end() } {}
114 		struct iterator : private container::iterator
115 		{
iteratoriterator116 			explicit iterator(const container::iterator& it) noexcept : container::iterator { it } {}
iteratoriterator117 			explicit iterator(container::iterator&& it) noexcept : container::iterator { std::move(it) } {}
118 			Object* operator*() noexcept  { return container::iterator::operator*().active_object; }
119 			Object* operator->() noexcept { return operator*(); }
120 			iterator& operator++() noexcept { container::iterator::operator++(); return *this; }
121 			bool operator==(const iterator& rhs) noexcept { return static_cast<container::iterator>(*this)==static_cast<container::iterator>(rhs); }
122 			bool operator!=(const iterator& rhs) noexcept { return !operator==(rhs); }
123 		};
begin()124 		iterator begin() noexcept { return iterator { range_begin }; }
end()125 		iterator end() noexcept { return iterator { range_end }; }
126 	};
127 
128 
129 	MapEditorToolBase(const QCursor& cursor, MapEditorTool::Type type, MapEditorController* editor, QAction* tool_action);
130 	~MapEditorToolBase() override;
131 
132 	void init() override;
133 	const QCursor& getCursor() const override;
134 
135 	/**
136 	 * Updates the saved current position (raw and constrained), map widget, and modifiers.
137 	 *
138 	 * This function is called by the other mouse*Event handlers of this class.
139 	 * Derived classes which override the handlers may wish to call this, too.
140 	 */
141 	void mousePositionEvent(QMouseEvent* event, const MapCoordF& map_coord, MapWidget* widget);
142 
143 	bool mousePressEvent(QMouseEvent* event, const MapCoordF& map_coord, MapWidget* widget) override;
144 	bool mouseMoveEvent(QMouseEvent* event, const MapCoordF& map_coord, MapWidget* widget) override;
145 	bool mouseReleaseEvent(QMouseEvent* event, const MapCoordF& map_coord, MapWidget* widget) override;
146 
147 	bool keyPressEvent(QKeyEvent* event) override;
148 	bool keyReleaseEvent(QKeyEvent* event) override;
149 
150 	/// Draws the preview renderables. Should be overridden to draw custom elements.
151 	void draw(QPainter* painter, MapWidget* widget) override;
152 
153 	void finishEditing() override;
154 
155 protected slots:
156 	void updateDirtyRect();
157 	void objectSelectionChanged();
158 	void updatePreviewObjectsSlot();
159 
160 protected:
161 	// Virtual methods to be implemented by the derived class.
162 	// To access the cursor position, use [click/cur]_pos(_map) to get the mouse press
163 	// respectively current position on the screen or on the map, or constrained_pos(_map)
164 	// to get the position constrained by the snap and angle tool helpers, if activated.
165 
166 	/// Can do additional initializations at a time where no other tool is active (in contrast to the constructor)
167 	virtual void initImpl();
168 	/// Must include the area of all custom drawings into the rect,
169 	/// which already contains the area of the selection preview and activated tool helpers when this method is called.
170 	/// Must return the size of the pixel border, or -1 to clear the drawing.
171 	virtual int updateDirtyRectImpl(QRectF& rect);
172 	/// Must draw the tool's graphics.
173 	/// The implementation draws the preview renderables.
174 	/// MapEditorToolBase::draw() draws the activated tool helpers afterwards.
175 	/// If this is not desired, you can override draw() directly.
176 	virtual void drawImpl(QPainter* painter, MapWidget* widget);
177 	/// Must update the status bar text
178 	virtual void updateStatusText() = 0;
179 	/// Called when the object selection in the map is changed.
180 	/// This can happen for example as result of an undo/redo operation.
181 	virtual void objectSelectionChangedImpl() = 0;
182 
183 	/// Called when the left mouse button is pressed
184 	virtual void clickPress();
185 	/// Called when the left mouse button is released without a drag operation before
186 	virtual void clickRelease();
187 
188 	/// Called when the mouse is moved without the left mouse button being pressed
189 	virtual void mouseMove();
190 
191 	void gestureStarted() override;
192 
193 	void startDragging();
194 	void updateDragging();
195 	void finishDragging();
196 	void cancelDragging();
197 	/// Is the left mouse button pressed and has a drag move been started (by moving the mouse a minimum amount of pixels)?
isDragging()198 	bool isDragging() const { return dragging; }
199 
200 	/// Called when a drag operation is started. This happens when dragging the mouse some pixels
201 	/// away from the mouse press position. The distance is determined by start_drag_distance.
202 	/// dragMove() is called immediately after this call to account for the already moved distance.
203 	virtual void dragStart();
204 	/// Called when the mouse is moved with the left mouse button being pressed
205 	virtual void dragMove();
206 	/// Called when a drag operation is finished. There is no need to update the edit operation with the
207 	/// current cursor coordinates in this call as it is ensured that dragMove() is called before.
208 	virtual void dragFinish();
209 
210 	virtual void dragCanceled();
211 
212 	/// Called when a key is pressed down. Return true if the key was processed by the tool.
213 	virtual bool keyPress(QKeyEvent* event);
214 	/// Called when a key is released. Return true if the key was processed by the tool.
215 	virtual bool keyRelease(QKeyEvent* event);
216 
217 	// Helper methods
218 
219 	/**
220 	 * Applies the constraint helpers and generates a pointer event.
221 	 *
222 	 * This function re-applies the constraint helpers to the current pointer
223 	 * position. Then it calls either mouseMove() or dragMove(), depending on
224 	 * the current state.
225 	 */
226 	void reapplyConstraintHelpers();
227 
228 	/// Call this before editing the selected objects, and finish/abortEditing() afterwards.
229 	/// Takes care of the preview renderables handling, map dirty flag, and objects edited signal.
230 	void startEditing();
231 	void startEditing(Object* object);
232 	void startEditing(const std::set<Object*>& objects);
233 	void abortEditing();
234 
editedObjects()235 	ObjectsRange editedObjects() { return ObjectsRange { edited_items }; }
236 	bool editedObjectsModified() const;
237 	void resetEditedObjects();
238 
239 	/// Call this to display changes to the preview objects between startEditing() and finish/abortEditing().
240 	virtual void updatePreviewObjects();
241 
242 	/// Call this to display changes to the preview objects between startEditing() and finish/abortEditing().
243 	/// This method delays the actual redraw by a short amount of time to reduce the load when editing many objects.
244 	void updatePreviewObjectsAsynchronously();
245 
246 	/// If the tool created custom renderables (e.g. with updatePreviewObjects()), draws the preview renderables,
247 	/// else draws the renderables of the selected map objects.
248 	void drawSelectionOrPreviewObjects(QPainter* painter, MapWidget* widget, bool draw_opaque = false);
249 
250 	/// Activates or deactivates the angle helper, recalculates (un-)constrained cursor position,
251 	/// and calls mouseMove() or dragMove() to update the tool.
252 	void activateAngleHelperWhileEditing(bool enable = true);
253 
254 	/// Activates or deactivates the snap helper, recalculates (un-)constrained cursor position,
255 	/// and calls mouseMove() or dragMove() to update the tool.
256 	void activateSnapHelperWhileEditing(bool enable = true);
257 
258 	/**
259 	 * Calculates the constrained cursor position for the current position and map widget.
260 	 *
261 	 * Key event handlers which change constraint helper activation must call
262 	 * this to be able to visualize the effect of the change. Overrides of
263 	 * mouse*Event handlers shall call mousePositionEvent() instead. This is
264 	 * also done by the default implementation of these handlers. Thus it is not
265 	 * necessary to call this explicitly from clickPress(), clickRelease(),
266 	 * mouseMove(), dragStart(), dragMove(), or dragFinish() handlers.
267 	 */
268 	void updateConstrainedPositions();
269 
270 #ifdef MAPPER_DEVELOPMENT_BUILD
271 
272 protected slots:
273 	/**
274 	 * Sends simulated sequences of mouse button-press, move, button-release.
275 	 *
276 	 * The mouse position is modified randomly for every single event.
277 	 * After each event, there is a small delay.
278 	 *
279 	 * This a debugging aid. The implementation is meant to be modified as
280 	 * needed with regard to modifier keys, stopping the simulation, etc.
281 	 * It is to be started from an event handler via a single-shot timer:
282 	 *
283 	 *     QTimer::singleShot(200, this, &MapEditorToolBase::generateNextSimulatedEvent);
284 	 */
285 	void generateNextSimulatedEvent();
286 
287 private:
288 	int simulation_state = 0;
289 
290 #endif  // MAPPER_DEVELOPMENT_BUILD
291 
292 protected:
293 	// Mouse handling
294 
295 	/// Position where the left mouse button was pressed, with no constraints applied.
296 	QPoint click_pos;
297 	MapCoordF click_pos_map;
298 	/// Position where the left mouse button was pressed, constrained by tool helpers, if active.
299 	QPointF constrained_click_pos;
300 	MapCoordF constrained_click_pos_map;
301 	/// Position where the cursor is currently
302 	QPoint cur_pos;
303 	MapCoordF cur_pos_map;
304 	/// Position where the cursor is currently, constrained by tool helpers, if active
305 	QPointF constrained_pos;
306 	MapCoordF constrained_pos_map;
307 	/// This is set to true when constrained_pos(_map) is a snapped position
308 	bool snapped_to_pos;
309 	/// The amount of pixels the mouse has to be moved to start dragging.
310 	/// Defaults to Settings::getInstance().getStartDragDistancePx(), but can be modified.
311 	int effective_start_drag_distance;
312 
313 	/// Angle tool helper. If activated, it is included in the dirty rect and drawn automatically.
314 	std::unique_ptr<ConstrainAngleToolHelper> angle_helper;
315 	/// Snapping tool helper. If a non-null filter is set, it is included in the dirty rect and drawn automatically.
316 	std::unique_ptr<SnappingToolHelper> snap_helper;
317 	/// An object to exclude from snapping, or nullptr to snap to all objects.
318 	Object* snap_exclude_object = nullptr;
319 
320 	// Key handling
321 
322 	/// Bitmask containing active modifiers
323 	Qt::KeyboardModifiers active_modifiers;
324 
325 	// Other
326 
327 	/// The map widget in which the tool was last used
328 	MapWidget* cur_map_widget;
329 
330 	/// Must be set by derived classes if a key button bar is used.
331 	/// MapEditorToolBase will take care of including its modifiers into
332 	/// active_modifiers and destruct it when the tool is destructed.
333 	QPointer<KeyButtonBar> key_button_bar;
334 
335 private:
336 	// Miscellaneous internals
337 	QCursor cursor;
338 	bool preview_update_triggered = false;
339 	bool dragging                 = false;
340 	bool dragging_canceled        = false;
341 	std::unique_ptr<MapRenderables> renderables;
342 	std::unique_ptr<MapRenderables> old_renderables;
343 	std::vector<EditedItem> edited_items;
344 };
345 
346 
347 }  // namespace OpenOrienteering
348 #endif
349