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_DRAW_PATH_H
23 #define OPENORIENTEERING_DRAW_PATH_H
24 
25 #include <memory>
26 
27 #include <QtGlobal>
28 #include <QObject>
29 #include <QPoint>
30 #include <QPointer>
31 #include <QString>
32 
33 #include "core/map_coord.h"
34 #include "tools/draw_line_and_area_tool.h"
35 
36 class QAction;
37 class QCursor;
38 class QKeyEvent;
39 class QMouseEvent;
40 class QPainter;
41 class QToolButton;
42 
43 namespace OpenOrienteering {
44 
45 class AzimuthInfoHelper;
46 class ConstrainAngleToolHelper;
47 class FollowPathToolHelper;
48 class KeyButtonBar;
49 class MapEditorController;
50 class MapWidget;
51 class PathObject;
52 class SnappingToolHelper;
53 class SnappingToolHelperSnapInfo;
54 class Symbol;
55 
56 
57 /**
58  * Tool to draw arbitrarily shaped PathObjects.
59  */
60 class DrawPathTool : public DrawLineAndAreaTool
61 {
62 Q_OBJECT
63 public:
64 	DrawPathTool(MapEditorController* editor, QAction* tool_action, bool is_helper_tool, bool allow_closing_paths);
65 	~DrawPathTool() override;
66 
67 	void init() override;
68 	const QCursor& getCursor() const override;
69 
70 	bool mousePressEvent(QMouseEvent* event, const MapCoordF& map_coord, MapWidget* widget) override;
71 	bool mouseMoveEvent(QMouseEvent* event, const MapCoordF& map_coord, MapWidget* widget) override;
72 	bool mouseReleaseEvent(QMouseEvent* event, const MapCoordF& map_coord, MapWidget* widget) override;
73 	bool mouseDoubleClickEvent(QMouseEvent* event, const MapCoordF& map_coord, MapWidget* widget) override;
74 
75 	bool keyPressEvent(QKeyEvent* event) override;
76 	bool keyReleaseEvent(QKeyEvent* event) override;
77 
78 	void draw(QPainter* painter, MapWidget* widget) override;
79 
80 protected slots:
81 	void updateDirtyRect();
82 	void setDrawingSymbol(const OpenOrienteering::Symbol* symbol) override;
83 
84 	/** This slot listens to changes in the map's object selection. */
85 	virtual void objectSelectionChanged();
86 
87 protected:
88 	void updatePreviewPath() override;
89 	/** Should be called when moving the cursor without the draw button being held */
90 	void updateHover();
91 	/** Called by updateHover() if the user is currently drawing */
92 	void updateDrawHover();
93 	/** Updates the last three points of the path to form a bezier curve */
94 	void createPreviewCurve(MapCoord position, qreal direction);
95 	/** Closes the preview path */
96 	void closeDrawing();
97 	void finishDrawing() override;
98 	void abortDrawing() override;
99 	void undoLastPoint();
100 	/** When not drawing but when the current selection is a single path,
101 	 *  removes the last point from that path, or deletes the whole path if
102 	 *  there are too few remaining points.
103 	 *  Returns true if the path was modified or deleted. */
104 	bool removeLastPointFromSelectedPath();
105 	void updateAngleHelper();
106 	bool pickAngle(const MapCoordF& coord, MapWidget* widget);
107 	void updateSnapHelper();
108 
109 	/** Starts appending to another, existing object */
110 	void startAppending(SnappingToolHelperSnapInfo& snap_info);
111 
112 	/** Starts following another, existing path */
113 	void startFollowing(SnappingToolHelperSnapInfo& snap_info, const MapCoord& snap_coord);
114 	void updateFollowing();
115 	void finishFollowing();
116 
117 	/**
118 	 * Checks if the user dragged the mouse away a certain minimum distance from
119 	 * the click point and if yes, returns the drag angle, otherwise returns 0.
120 	 */
121 	qreal calculateRotation(const QPoint& mouse_pos, const MapCoordF& mouse_pos_map) const;
122 	/**
123 	 * Activates or deactivates dash point drawing depending on if a line symbol
124 	 * with dash symbols is selected.
125 	 */
126 	void updateDashPointDrawing();
127 	void updateStatusText();
128 
129 
130 	QPointer<KeyButtonBar> key_button_bar;
131 	QPointer<QToolButton> dash_points_button;
132 	QPointer<QToolButton> azimuth_button;
133 
134 	MapWidget* cur_map_widget;
135 
136 	QPoint click_pos;
137 	MapCoordF click_pos_map;
138 
139 	QPoint cur_pos;
140 	MapCoordF cur_pos_map;
141 
142 	/** The beginning of the current curve in the preview path. */
143 	MapCoordF previous_pos_map;
144 	/** A control point defining the tangent at the beginning of the current curve. */
145 	MapCoordF previous_drag_map;
146 
147 	std::unique_ptr<ConstrainAngleToolHelper> angle_helper;
148 	MapCoordF constrained_pos_map;
149 
150 	std::unique_ptr<AzimuthInfoHelper> azimuth_helper;
151 
152 	std::unique_ptr<SnappingToolHelper> snap_helper;
153 
154 	PathObject* append_to_object;
155 
156 	std::unique_ptr<FollowPathToolHelper> follow_helper;
157 	MapCoordVector::size_type follow_start_index;
158 
159 	qreal previous_point_direction = 0;
160 
161 	bool allow_closing_paths = true;
162 	bool ctrl_pressed     = false;
163 	bool shift_pressed    = false;
164 	bool left_mouse_down  = false;
165 	bool appending        = false;
166 	bool following        = false;
167 	bool picking_angle    = false; ///< Indicates picking of the initial angle from an object.
168 	bool picked_angle     = false; ///< Indicates an active angle picked from another object.
169 	bool dragging         = false;
170 	bool draw_dash_points = false;
171 	bool create_segment   = false;
172 	bool create_spline_corner = false; ///< For drawing bezier splines without parallel handles
173 	bool path_has_preview_point = false;
174 	bool previous_point_is_curve_point = false;
175 	bool created_point_at_last_mouse_press = false;  ///< Used for finishing on double click.
176 	bool finished_path_is_selected = false;  ///< True just after finishing a path
177 
178 };
179 
180 
181 }  // namespace OpenOrienteering
182 
183 #endif
184