1 /*
2  *    Copyright 2012, 2013 Thomas Schöps
3  *    Copyright 2012-2020 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_TEMPLATE_H
23 #define OPENORIENTEERING_TEMPLATE_H
24 
25 #include <functional>
26 #include <memory>
27 #include <vector>
28 
29 #include <QtGlobal>
30 #include <QObject>
31 #include <QPointF>
32 #include <QString>
33 
34 #include "core/map_coord.h"
35 #include "util/matrix.h"
36 #include "util/transformation.h"
37 
38 class QByteArray;
39 class QColor;
40 class QDir;
41 class QFileInfo;
42 class QPainter;
43 class QPointF;
44 class QRectF;
45 class QTransform;
46 class QWidget;
47 class QXmlStreamReader;
48 class QXmlStreamWriter;
49 
50 namespace OpenOrienteering {
51 
52 class Map;
53 class MapView;
54 class Object;
55 
56 
57 /**
58  * Transformation parameters for non-georeferenced templates,
59  * transforming template coordinates to map coordinates.
60  *
61  * The parameters are applied to painter in the order
62  * 1. translate
63  * 2. rotate
64  * 3. scale.
65  *
66  * So this order is also chosen for the member variables, and
67  * thus used in list initialization.
68  *
69  * \see Template::applyTemplateTransform()
70  *
71  * Coordinate transformations use the opposite order for the
72  * same effect.
73  */
74 struct TemplateTransform
75 {
76 	/// x position in 1/1000 mm
77 	qint32 template_x = 0;
78 	/// x position in 1/1000 mm
79 	qint32 template_y = 0;
80 
81 	/// Rotation in radians, a positive rotation is counter-clockwise.
82 	double template_rotation = 0.0;
83 
84 	/// Scaling in x direction, smaller than 1 shrinks.
85 	double template_scale_x = 1.0;
86 	/// Scaling in y direction, smaller than 1 shrinks.
87 	double template_scale_y = 1.0;
88 	/// Adjustment to scaling if anisotropy is askew.
89 	double template_shear = 0.0;
90 
91 	static TemplateTransform fromQTransform(const QTransform& qt) noexcept;
92 
93 	std::function<void (Object*)> makeObjectTransform() const;
94 
95 	void save(QXmlStreamWriter& xml, const QString& role) const;
96  	void load(QXmlStreamReader& xml);
97 };
98 
99 bool operator==(const TemplateTransform& lhs, const TemplateTransform& rhs) noexcept;
100 bool operator!=(const TemplateTransform& lhs, const TemplateTransform& rhs) noexcept;
101 
102 
103 
104 /**
105  * Abstract base class for templates.
106  */
107 class Template : public QObject
108 {
109 Q_OBJECT
110 public:
111 	/**
112 	 * States in the lifetime of a template.
113 	 */
114 	enum State
115 	{
116 		/// The template data is loaded and ready to be displayed.
117 		Loaded = 0,
118 		/// The template data is not yet loaded or has been unloaded.
119 		/// It is assumed to be available, so it can be (re-)loaded if needed.
120 		Unloaded,
121 		/// A required resource cannot be found (e.g. missing image or font),
122 		/// so the template data cannot be loaded.
123 		Invalid
124 	};
125 
126 	/**
127 	 * The result of template file lookup attempts.
128 	 */
129 	enum LookupResult
130 	{
131 		NotFound       = 0,  ///< File not found at all.
132 		FoundInMapDir  = 1,  ///< File name found in the map's directory.
133 		FoundByRelPath = 2,  ///< File found by relative path from the map's directory.
134 		FoundByAbsPath = 3,  ///< File found by absolute path.
135 	};
136 
137 	/**
138 	 * Indicates arguments which must not be nullptr.
139 	 * \todo Use the Guideline Support Library
140 	 */
141 	template <typename T>
142 	using not_null = T;
143 
144 
145 protected:
146 	/**
147 	 * Initializes the template as "Unloaded".
148 	 */
149 	Template(const QString& path, not_null<Map*> map);
150 
151 	/**
152 	 * Copy-construction as duplication helper.
153 	 */
154 	Template(const Template& proto);
155 
156 public:
157 	~Template() override;
158 
159 	/**
160 	 * Creates a duplicate of the template
161 	 */
162 	virtual Template* duplicate() const = 0;
163 
164 	/**
165 	 * Returns a string which should identify the type of the template uniquely:
166 	 * the class name. Very simple RTTI feature.
167 	 */
168 	virtual const char* getTemplateType() const = 0;
169 
170 
171 	/**
172 	 * Returns a description of the last error that occurred.
173 	 */
174 	QString errorString() const;
175 
176 	/**
177 	 * Returns true if the template is raster graphics.
178 	 *
179 	 * Raster graphics cannot be printed in the foreground in vector mode.
180 	 */
181 	virtual bool isRasterGraphics() const = 0;
182 
183 
184 	/**
185 	 * Saves template parameters.
186 	 *
187 	 * This method saves common properties such as filename, transformation,
188 	 * adjustment, etc., and it calls saveTypeSpecificTemplateConfiguration for
189 	 * saving type-specific parameters (e.g. filtering mode for images).
190 	 *
191 	 * If a target directory is given via `map_dir`, the relative template
192 	 * path is determined for this directory.
193 	 */
194 	void saveTemplateConfiguration(QXmlStreamWriter& xml, bool open, const QDir* map_dir = nullptr) const;
195 
196 	/**
197 	 * Creates and returns a template from the configuration in the XML stream.
198 	 *
199 	 * Returns a null pointer in the case of error.
200 	 */
201 	static std::unique_ptr<Template> loadTemplateConfiguration(QXmlStreamReader& xml, Map& map, bool& open);
202 
203 
204 	/**
205 	 *  Saves the template itself, returns true if successful.
206 	 *
207 	 * This is called when saving the map if the template's hasUnsavedChanges()
208 	 * is true.
209 	 */
210 	virtual bool saveTemplateFile() const;
211 
212 	/**
213 	 * Changes a template's file without changing the parameters.
214 	 *
215 	 * Useful when a template file has been moved.
216 	 * If load_file is true, tries to load the given file.
217 	 */
218 	void switchTemplateFile(const QString& new_path, bool load_file);
219 
220 	/**
221 	 * Shows the dialog to find a moved template.
222 	 *
223 	 * If the user selects a new file, tries to switch to the selected template
224 	 * file using switchTemplateFile() and by trying to load the new file.
225 	 * Returns true if this succeeds; if not, reverts the switch and returns
226 	 * false. Also returns false if the dialog is aborted.
227 	 */
228 	bool execSwitchTemplateFileDialog(QWidget* dialog_parent);
229 
230 
231 	/**
232 	 * Does everything needed to load a template.
233 	 *
234 	 * Calls preLoadConfiguration(), loadTemplateFile() and
235 	 * postLoadConfiguration(). Returns if the process was successful.
236 	 *
237 	 * The passed-in view is used to center the template if needed.
238 	 */
239 	bool configureAndLoad(QWidget* dialog_parent, MapView* view);
240 
241 	/**
242 	 * Tries to find the template file non-interactively.
243 	 *
244 	 * Thus function updates the path and name variables, and the template state.
245 	 * If successful, changes the state from Invalid to Unloaded if necessary,
246 	 * and returns true. Otherwise, changes the state from Unloaded to Invalid
247 	 * if necessary, and returns false. (If the state is Loaded, it is left
248 	 * unchanged.) It returns an indication of its success.
249 	 *
250 	 * This function searches for the template in the following locations:
251 	 *
252 	 *  1. The relative path with regard to the map directory, if both are valid.
253 	 *     This alternative has precedence because it should always work,
254 	 *     especially after coyping or moving a whole working directory on the
255 	 *     same computer or to another one.
256 	 *
257 	 *  2. The absolute path of the template.
258 	 *     This is the most explicit alternative. It works on the same computer
259 	 *     when the map file is copied or moved to another location.
260 	 *
261 	 *  3. The map directory, if valid, for the filename of the template.
262 	 *     This is a fallback for use cases where a map and selected templates
263 	 *     are moved to the same flat folder, e.g. when receiving them via
264 	 *     individual e-mail attachments.
265 	 *
266 	 * \param map_path  Either the full filepath of the map, or an arbitrary
267 	 *                  directory which shall be regarded as the map directory.
268 	 */
269 	LookupResult tryToFindTemplateFile(const QString& map_path);
270 
271 	/**
272 	 * Tries to find and load the template file non-interactively.
273 	 *
274 	 * Returns true if the template was loaded successful.
275 	 *
276 	 * If out_loaded_from_map_dir is given, it is set to true if the template file
277 	 * is found using the template filename in the map's directory.
278 	 *
279 	 * \see tryToFindTemplateFile
280 	 */
281 	bool tryToFindAndReloadTemplateFile(const QString& map_path);
282 
283 
284 	/**
285 	 * Does configuration before the actual template is loaded.
286 	 *
287 	 * This function is called after the user chooses the template file, but
288 	 * before it is loaded. Derived classes can show dialogs here to get user
289 	 * input which is needed to interpret the template file.
290 	 *
291 	 * If the implementation returns false, loading the template is aborted.
292 	 *
293 	 * \note Derived classes should set is_georeferenced either here or in
294 	 *       postLoadConfiguration().
295 	 *       By default templates are loaded as non-georeferenced.
296 	 */
297 	virtual bool preLoadConfiguration(QWidget* dialog_parent);
298 
299 	/**
300 	 * Loads the template file.
301 	 *
302 	 * This function can be called if the template state is Invalid or Unloaded.
303 	 * It must not be called if the template file is already loaded.
304 	 * It returns true if the template is loaded successfully.
305 	 *
306 	 * Set the configuring parameter to true if the template is currently being
307 	 * configured by the user (in contrast to the case where it is reloaded, e.g.
308 	 * when loaded while reopening an existing map file).
309 	 */
310 	bool loadTemplateFile(bool configuring);
311 
312 	/**
313 	 * Does configuration after the actual template is loaded.
314 	 *
315 	 * This function is called after the user chose the template file and after
316 	 * the chosen file was successfully loaded. Derived classes can show dialogs
317 	 * here to get user input which is needed to interpret the template file.
318 	 *
319 	 * If the implementation returns false, loading the template is aborted.
320 	 *
321 	 * By setting out_center_in_view, the implementation can decide if the
322 	 * template should be centered in the active view (only if it is not
323 	 * georeferenced.)
324 	 */
325 	virtual bool postLoadConfiguration(QWidget* dialog_parent, bool& out_center_in_view);
326 
327 	/**
328 	 * Unloads the template file.
329 	 *
330 	 * Can be called if the template state is Loaded.
331 	 * Must not be called if the template file is already unloaded, or invalid.
332 	 */
333 	void unloadTemplateFile();
334 
335 	/**
336 	 * Draws the template using the given painter with the given opacity.
337 	 *
338 	 * The painter transformation is set to use template coordinates.
339 	 * The clip rect is in template coordinates.
340 	 * The scale is the combined view & template scale. It can be used to give
341 	 * a minimum size to elements.
342 	 */
343     virtual void drawTemplate(QPainter* painter, const QRectF& clip_rect, double scale, bool on_screen, qreal opacity) const = 0;
344 
345 
346 	/**
347 	 * Calculates the template's bounding box in map coordinates.
348 	 */
349 	virtual QRectF calculateTemplateBoundingBox() const;
350 
351 	/**
352 	 * Returns the extra extent of the template out of the bounding box.
353 	 *
354 	 * While the bounding box is defined in map coordinates, this border is
355 	 * given in pixels. This is useful for elements which stay the same size
356 	 * regardless of the zoom level so that a bounding box in map coords
357 	 * cannot be calculated.
358 	 */
getTemplateBoundingBoxPixelBorder()359 	virtual int getTemplateBoundingBoxPixelBorder() {return 0;}
360 
361 	/**
362 	 * Marks the whole area of the template as needing a redraw.
363 	 *
364 	 * Use this before and after modifications to the template transformation.
365 	 *
366 	 * The default implementation marks everything as "to be redrawn" for
367 	 * georeferenced templates and uses the reported extent otherwise.
368 	 */
369 	virtual void setTemplateAreaDirty();
370 
371 
372 	/**
373 	 * Must return if freehand drawing onto the template is possible.
374 	 */
375 	virtual bool canBeDrawnOnto() const;
376 
377 	/**
378 	 * Draws onto the template.
379 	 *
380 	 * This only works for templates for which canBeDrawnOnto() returns true.
381 	 *
382 	 * coords is an array of points with which the drawn line is defined and
383 	 * must contain at least 2 points.
384 	 *
385 	 * If map_bbox is an invalid rect, then the method will calculate it itself.
386 	 *
387 	 * \todo Rewrite using a range of MapCoordF.
388 	 */
389 	void drawOntoTemplate(not_null<MapCoordF*> coords, int num_coords, const QColor& color, qreal width, QRectF map_bbox);
390 
391 	/**
392 	 * Triggers an undo or redo action for template freehand drawing.
393 	 *
394 	 * This only works for templates for which canBeDrawnOnto() returns true.
395 	 */
396 	virtual void drawOntoTemplateUndo(bool redo);
397 
398 
399 	// Transformation related methods for non-georeferenced templates only
400 
401 	/**
402 	 * Changes the painter's transformation so it can be used to draw in template coordinates.
403 	 *
404 	 * The previous transformation of the painter must be the map transformation.
405 	 *
406 	 * \note For non-georeferenced templates only, or if the template
407 	 *       transformation has been set by the template nevertheless.
408 	 */
409 	void applyTemplateTransform(QPainter* painter) const;
410 
411 	/**
412 	 * Returns the extent of the template in template coordinates.
413 	 *
414 	 * The default implementation returns a "very big" rectangle.
415 	 *
416 	 * \note For non-georeferenced templates only!
417 	 */
418 	virtual QRectF getTemplateExtent() const;
419 
420 	/**
421 	 * Scales the template with the given scaling center.
422 	 *
423 	 * \note For non-georeferenced templates only!
424 	 */
425 	void scale(double factor, const MapCoord& center);
426 
427 	/**
428 	 * Rotates the template around the given point.
429 	 *
430 	 * \note For non-georeferenced templates only!
431 	 */
432 	void rotate(double rotation, const MapCoord& center);
433 
434 
435 	// Coordinate transformations between template coordinates and map coordinates
436 
mapToTemplate(const MapCoordF & coords)437 	inline MapCoordF mapToTemplate(const MapCoordF& coords) const
438 	{
439 		return MapCoordF(map_to_template.get(0, 0) * coords.x() + map_to_template.get(0, 1) * coords.y() + map_to_template.get(0, 2),
440 		                 map_to_template.get(1, 0) * coords.x() + map_to_template.get(1, 1) * coords.y() + map_to_template.get(1, 2));
441 	}
mapToTemplateOther(const MapCoordF & coords)442 	inline MapCoordF mapToTemplateOther(const MapCoordF& coords) const	// normally not needed - this uses the other transformation parameters
443 	{
444 		Q_ASSERT(!is_georeferenced);
445 		// SLOW - cache this matrix if needed often
446 		Matrix map_to_template_other;
447 		template_to_map_other.invert(map_to_template_other);
448 		return MapCoordF(map_to_template_other.get(0, 0) * coords.x() + map_to_template_other.get(0, 1) * coords.y() + map_to_template_other.get(0, 2),
449 		                 map_to_template_other.get(1, 0) * coords.x() + map_to_template_other.get(1, 1) * coords.y() + map_to_template_other.get(1, 2));
450 	}
templateToMap(const QPointF & coords)451 	inline MapCoordF templateToMap(const QPointF& coords) const
452 	{
453 		return MapCoordF(template_to_map.get(0, 0) * coords.x() + template_to_map.get(0, 1) * coords.y() + template_to_map.get(0, 2),
454 		                 template_to_map.get(1, 0) * coords.x() + template_to_map.get(1, 1) * coords.y() + template_to_map.get(1, 2));
455 	}
templateToMapOther(const QPointF & coords)456 	inline MapCoordF templateToMapOther(const QPointF& coords) const	// normally not needed - this uses the other transformation parameters
457 	{
458 		Q_ASSERT(!is_georeferenced);
459 		return MapCoordF(template_to_map_other.get(0, 0) * coords.x() + template_to_map_other.get(0, 1) * coords.y() + template_to_map_other.get(0, 2),
460 		                 template_to_map_other.get(1, 0) * coords.x() + template_to_map_other.get(1, 1) * coords.y() + template_to_map_other.get(1, 2));
461 	}
462 
463 
464 	// Pass points & adjustment
465 
getPassPointList()466 	inline const PassPointList& getPassPointList() const {return passpoints;}
getPassPointList()467 	inline PassPointList& getPassPointList() {return passpoints;}
getNumPassPoints()468 	inline int getNumPassPoints() const {return passpoints.size();}
getPassPoint(int i)469 	inline PassPoint* getPassPoint(int i) {return &passpoints[i];}
470 	void addPassPoint(const PassPoint& point, int pos);
471 	void deletePassPoint(int pos);
472 	void clearPassPoints();
473 
474 	/// Change from adjusted into original state or the other way round
475 	void switchTransforms();
476 	void getTransform(TemplateTransform& out) const;
477 	void setTransform(const TemplateTransform& transform);
478 	void getOtherTransform(TemplateTransform& out) const;
479 	void setOtherTransform(const TemplateTransform& transform);
480 
481 
482 	// Getters / Setters
483 	// General
484 
getMap()485 	inline Map* getMap() const {return map;}
486 
getTemplateFilename()487 	inline const QString& getTemplateFilename() const {return template_file;}
488 
489 	/// Changes the path and filename only. Does not do any reloading etc.
490 	void setTemplateFileInfo(const QFileInfo& file_info);
491 
getTemplatePath()492 	inline const QString& getTemplatePath() const {return template_path;}
493 	/// Changes the path and filename only. Does not do any reloading etc.
494 	void setTemplatePath(const QString& value);
495 
getTemplateRelativePath()496 	inline const QString& getTemplateRelativePath() const {return template_relative_path;}
setTemplateRelativePath(const QString & value)497 	inline void setTemplateRelativePath(const QString& value) {template_relative_path = value;}
498 
getTemplateState()499 	inline State getTemplateState() const {return template_state;}
setTemplateState(State state)500 	inline void setTemplateState(State state) {template_state = state;}
501 
getTemplateGroup()502 	inline int getTemplateGroup() const {return template_group;}
setTemplateGroup(int value)503 	inline void setTemplateGroup(int value) {template_group = value;}
504 
hasUnsavedChanges()505 	inline bool hasUnsavedChanges() const {return has_unsaved_changes;}
506 	void setHasUnsavedChanges(bool value);
507 
isTemplateGeoreferenced()508 	inline bool isTemplateGeoreferenced() const {return is_georeferenced;}
509 
510 	/**
511 	 * Returns if the template allows the georefencing state to be changed at all.
512 	 *
513 	 * The default implementation returns false.
514 	 */
515 	virtual bool canChangeTemplateGeoreferenced();
516 
517 	/**
518 	 * Tries to change the usage of georeferencing data.
519 	 *
520 	 * If supported by the actual template, this function tries to switch the
521 	 * state between non-georeferenced and georeferenced. It returns false when
522 	 * an error occurred, and true if the change was successful or if it was
523 	 * explicitly cancelled by the user.
524 	 *
525 	 * The default implementation returns true iff the state matches the value.
526 	 */
527 	virtual bool trySetTemplateGeoreferenced(bool value, QWidget* dialog_parent);
528 
529 
530 	// Transformation of non-georeferenced templates
531 
532 	MapCoord templatePosition() const;
533 	void setTemplatePosition(const MapCoord& coord);
534 
535 	MapCoord templatePositionOffset() const;
536 	void setTemplatePositionOffset(const MapCoord& offset);
537 	void applyTemplatePositionOffset();
538 	void resetTemplatePositionOffset();
539 
getTemplateX()540 	inline qint64 getTemplateX() const {return transform.template_x;}
setTemplateX(qint64 x)541 	inline void setTemplateX(qint64 x) {transform.template_x = x; updateTransformationMatrices();}
542 
getTemplateY()543 	inline qint64 getTemplateY() const {return transform.template_y;}
setTemplateY(qint64 y)544 	inline void setTemplateY(qint64 y) {transform.template_y = y; updateTransformationMatrices();}
545 
getTemplateScaleX()546 	inline double getTemplateScaleX() const {return transform.template_scale_x;}
setTemplateScaleX(double scale_x)547 	inline void setTemplateScaleX(double scale_x) {transform.template_scale_x = scale_x; updateTransformationMatrices();}
getTemplateScaleY()548 	inline double getTemplateScaleY() const {return transform.template_scale_y;}
setTemplateScaleY(double scale_y)549 	inline void setTemplateScaleY(double scale_y) {transform.template_scale_y = scale_y; updateTransformationMatrices();}
getTemplateShear()550 	inline double getTemplateShear() const {return transform.template_shear;}
setTemplateShear(double shear)551 	inline void setTemplateShear(double shear) {transform.template_shear = shear; updateTransformationMatrices();}
552 
getTemplateRotation()553 	inline double getTemplateRotation() const {return transform.template_rotation;}
setTemplateRotation(double rotation)554 	inline void setTemplateRotation(double rotation) {transform.template_rotation = rotation; updateTransformationMatrices();}
555 
isAdjustmentApplied()556 	inline bool isAdjustmentApplied() const {return adjusted;}
isAdjustmentDirty()557 	inline bool isAdjustmentDirty() const {return adjustment_dirty;}
558 	void setAdjustmentDirty(bool value);
559 
560 
561 	/**
562 	 * Returns true if the template has elements which are not opaque.
563 	 *
564 	 * The default implementation returns true when the template is loaded.
565 	 */
566 	virtual bool hasAlpha() const;
567 
568 
569 	// Static
570 	/**
571 	 * Returns the filename extensions supported by known subclasses.
572 	 */
573 	static const std::vector<QByteArray>& supportedExtensions();
574 
575 	/**
576 	 * Creates a Template instance for the given path.
577 	 *
578 	 * This function tries to find a matching template subclass by looking at
579 	 * the file extension. It may return nullptr if no subclass supports the
580 	 * extension.
581 	 */
582 	static std::unique_ptr<Template> templateForFile(const QString& path, Map* map);
583 
584 	/**
585 	 * A flag which disables the writing of absolute paths for template files.
586 	 *
587 	 * By default, class Template saves absolute paths. This behavior can be
588 	 * changed by setting this flag to true. This allows to hide local (or
589 	 * private) directory names.
590 	 *
591 	 * This flag defaults to false.
592 	 */
593 	static bool suppressAbsolutePaths;
594 
595 
596 signals:
597 	/**
598 	 * Emitted whenever template_state was changed.
599 	 */
600 	void templateStateChanged();
601 
602 
603 protected:
604 	/**
605 	 * Sets the error description which will be returned by errorString().
606 	 */
607 	void setErrorString(const QString &text);
608 
609 
610 	/**
611 	 * Hook for saving parameters needed by the actual template type.
612 	 *
613 	 * The default implementation does nothing.
614 	 */
615 	virtual void saveTypeSpecificTemplateConfiguration(QXmlStreamWriter& xml) const;
616 
617 	/**
618 	 * Hook for loading parameters needed by the actual template type.
619 	 *
620 	 * \note The default implementation calls xml.skipCurrentElement().
621 	 *       Implementations must do the same if they do not parse it.
622 	 *
623 	 * Returns true on success.
624 	 */
625 	virtual bool loadTypeSpecificTemplateConfiguration(QXmlStreamReader& xml);
626 
627 
628 	/**
629 	 * Hook for loading the actual template file non-interactively.
630 	 *
631 	 * Returns true if successful.
632 	 *
633 	 * If configuring is true, a call to postLoadConfiguration() will follow
634 	 * if this returns true.
635 	 */
636 	virtual bool loadTemplateFileImpl(bool configuring) = 0;
637 
638 	/**
639 	 * Hook for unloading the template file.
640 	 */
641 	virtual void unloadTemplateFileImpl() = 0;
642 
643 
644 	/**
645 	 * Hook for drawing on the template.
646 	 *
647 	 * Draws the polyline given by the points onto the template.
648 	 * Required if canBeDrawnOnto() returns true.
649 	 */
650 	virtual void drawOntoTemplateImpl(MapCoordF* coords, int num_coords, const QColor& color, qreal width);
651 
652 
653 	/**
654 	 * Must be called after direct changes to transform or other_transform.
655 	 */
656 	void updateTransformationMatrices();
657 
658 
659 	// General properties
660 
661 	/// Map containing this template
662 	Map* map;
663 
664 	/// The filename of the template file (e.g. "map.bmp")
665 	QString template_file;
666 
667 	/// The complete path to the template file including the filename (e.g. "/home/me/map.bmp")
668 	QString template_path;
669 
670 	/// The template path relative to the map file (e.g. "../me/map.bmp").
671 	/// Can be empty as long as the map file has not been saved yet.
672 	QString template_relative_path;
673 
674 	/// The template lifetime state
675 	State template_state = Unloaded;
676 
677 	/// The description of the last error
678 	QString error_string;
679 
680 	/// Does the template itself (not its transformation) have unsaved changes (e.g. GPS track has changed, image has been painted on)
681 	bool has_unsaved_changes = false;
682 
683 	/// Is the template in georeferenced mode?
684 	bool is_georeferenced = false;
685 
686 private:
687 	// Properties for non-georeferenced templates (invalid if is_georeferenced is true)
688 
689 	/// Bounds correction offset for map templates. Must be masked out when saving.
690 	MapCoord accounted_offset;
691 
692 	/**
693 	 * This class reverts the template's accounted offset for its lifetime.
694 	 *
695 	 * \todo Copy/restore the transformation matrices when this no longer needs
696 	 *       allocations.
697 	 */
698 	class ScopedOffsetReversal;
699 
700 protected:
701 	/// Currently active transformation. NOTE: after direct changes here call updateTransformationMatrices()
702 	TemplateTransform transform;
703 
704 	/// Currently inactive transformation (adjusted or original). NOTE: after direct changes here call updateTransformationMatrices()
705 	TemplateTransform other_transform;
706 
707 	/// If true, transform is the adjusted transformation, otherwise it is the original one
708 	bool adjusted = false;
709 
710 	/// If true, the adjusted transformation has to be recalculated
711 	bool adjustment_dirty = true;
712 
713 	/// List of pass points for position adjustment
714 	PassPointList passpoints;
715 
716 	/// Number of the template group. If the template is not grouped, this is set to -1.
717 	/// \todo Switch to initialization with -1. ATM 0 is kept for compatibility.
718 	int template_group = 0;
719 
720 	// Transformation matrices calculated from cur_trans
721 	Matrix map_to_template;
722 	Matrix template_to_map;
723 	Matrix template_to_map_other;
724 };
725 
726 
727 }  // namespace OpenOrienteering
728 
729 #endif
730