1 /**********************************************************************************************
2 Copyright (C) 2014 Oliver Eichler <oliver.eichler@gmx.de>
3 Copyright (C) 2017 Norbert Truchsess <norbert.truchsess@t-online.de>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 **********************************************************************************************/
19
20 #ifndef IGISPROJECT_H
21 #define IGISPROJECT_H
22
23 #include "gis/IGisItem.h"
24 #include "gis/rte/router/IRouter.h"
25 #include "gis/search/CProjectFilterItem.h"
26 #include "gis/search/CSearch.h"
27 #include "helpers/CSelectCopyAction.h"
28 #include <QDebug>
29 #include <QMessageBox>
30 #include <QPointer>
31 #include <QTreeWidgetItem>
32
33 class CGisListWks;
34 class CGisDraw;
35 class CGisItemWpt;
36 class QDataStream;
37 class CDetailsPrj;
38 class IDevice;
39
40 class IGisProject : public QTreeWidgetItem
41 {
42 Q_DECLARE_TR_FUNCTIONS(IGisProject)
43 public:
44 enum type_e
45 {
46 eTypeGeoSearch
47 , eTypeQms
48 , eTypeGpx
49 , eTypeDb
50 , eTypeLostFound
51 , eTypeTwoNav
52 , eTypeSlf // the Sigma Log Format
53 , eTypeFit
54 , eTypeTcx
55 , eTypeSml
56 , eTypeLog
57 , eTypeQlb
58 };
59
60 /// flags used to serialize trivial flags in qms file
61 enum flags_e
62 {
63 eFlagNoCorrelation = 0x1
64 , eFlagAutoSave = 0x2
65 , eFlagInvalidDataOk = 0x4
66 , eFlagAutoSyncToDev = 0x8
67 };
68
69 enum sorting_roadbook_e
70 {
71 eSortRoadbookNone
72 , eSortRoadbookTrackWithDouble
73 , eSortRoadbookTrackWithoutDouble
74 , eSortRoadbookTrackWithDetails
75 };
76
77 enum sorting_folder_e
78 {
79 eSortFolderTime
80 , eSortFolderName
81 , eSortFolderSymbol
82 , eSortFolderRating
83 };
84
85 struct person_t
86 {
87 QString name;
88 QString id;
89 QString domain;
90 IGisItem::link_t link;
91 };
92
93 struct copyright_t
94 {
95 QString author;
96 QString year;
97 QString license;
98 };
99
100 struct metadata_t
101 {
metadata_tmetadata_t102 metadata_t() : time(QDateTime::currentDateTimeUtc())
103 {
104 }
105 QString name;
106 QString desc;
107 person_t author;
108 copyright_t copyright;
109 QList<IGisItem::link_t> links;
110 QDateTime time;
111 QString keywords;
112 QRectF bounds;
113 // -- all gpx tags - stop
114 QMap<QString, QVariant> extensions;
115 };
116
117 static const QString filedialogAllSupported;
118 static const QString filedialogFilterGPX;
119 static const QString filedialogFilterTCX;
120 static const QString filedialogFilterSML;
121 static const QString filedialogFilterLOG;
122 static const QString filedialogFilterQLB;
123 static const QString filedialogFilterQMS;
124 static const QString filedialogFilterSLF;
125 static const QString filedialogFilterFIT;
126 static const QString filedialogSaveFilters;
127 static const QString filedialogLoadFilters;
128
129 IGisProject(type_e type, const QString& filename, CGisListWks* parent);
130 IGisProject(type_e type, const QString& filename, IDevice* parent);
131 virtual ~IGisProject();
132
133 static IGisProject* create(const QString filename, CGisListWks* parent);
134
135 /**
136 @brief Ask to save the project before it is closed.
137
138 If the project is closed, the user is asked if the project should be saved and saved on user request.
139
140 @return True if the operation is aborted. False on "save" and "no".
141 */
142 bool askBeforClose();
143
144 IGisProject& operator=(const IGisProject& p)
145 {
146 key = p.key;
147 metadata = p.metadata;
148 return *this;
149 }
150
151 /**
152 @brief Summon the project details dialog.
153 */
154 void edit();
155
156 /**
157 @brief Returns true if a project of given format can be saved, false if it cannot be saved (just as .slf atm)
158 */
canSave()159 virtual bool canSave() const
160 {
161 return false;
162 }
163
164 /**
165 @brief Return true if saving should be skipped.
166 */
skipSave()167 virtual bool skipSave() const
168 {
169 return false;
170 }
171
getFileDialogFilter()172 virtual const QString getFileDialogFilter() const
173 {
174 return QString();
175 }
176
getFileExtension()177 virtual const QString getFileExtension() const
178 {
179 return QString();
180 }
181
182 /**
183 @brief Save the project using it's native format.
184 */
185 virtual bool save();
186
187 /**
188 @brief Save the project selecting one of the available formats.
189 */
190 bool saveAs(QString fn = QString(), QString filter = QString());
191
192 /**
193 @brief Save as strict GPX V 1.1 without any extensions and HTML
194 @return True on success
195 */
196 bool saveAsStrictGpx11();
197
setFilename(const QString & fn)198 virtual void setFilename(const QString& fn)
199 {
200 filename = fn;
201 }
202
getFilename()203 virtual QString getFilename() const
204 {
205 return filename;
206 }
207
208 /**
209 @brief Get the project type enumeration.
210
211 @Note: usually dynamic_cast should be used to get a pointer of correct type.
212 However if the project is serialized, a type id is needed.
213
214 @return One of type_e
215 */
getType()216 type_e getType() const
217 {
218 return type;
219 }
220
221 /**
222 @brief Get unique project key.
223 @return A MD5 hash string
224 */
getKey()225 const QString& getKey() const
226 {
227 genKey();
228 return key;
229 }
230
231 /**
232 @brief Get the unique key of the device the project is attached to
233 @return If the project is not attached to a device the string is empty
234 */
235 QString getDeviceKey() const;
236
237
238 QPixmap getIcon() const;
239
240 /**
241 @brief Get the project's name
242 @return The name from metadata.name
243 */
244 QString getName() const;
245 /**
246 @brief Get the project's name extended with the parent's name.
247 @return The name from metadata.nam appended with either the device name or the database parent folder's name.
248 */
249 QString getNameEx() const;
250
getTime()251 const QDateTime& getTime() const
252 {
253 return metadata.time;
254 }
getKeywords()255 const QString& getKeywords() const
256 {
257 return metadata.keywords;
258 }
getDescription()259 const QString& getDescription() const
260 {
261 return metadata.desc;
262 }
getLinks()263 const QList<IGisItem::link_t>& getLinks() const
264 {
265 return metadata.links;
266 }
267
getMetadata()268 const metadata_t& getMetadata() const
269 {
270 return metadata;
271 }
272
273 /**
274 @brief Get the sorting mode
275 @return One of sorting_e
276 */
getSortingRoadbook()277 sorting_roadbook_e getSortingRoadbook() const
278 {
279 return sortingRoadbook;
280 }
281
getSortingFolder()282 sorting_folder_e getSortingFolder() const
283 {
284 return sortingFolder;
285 }
286
287 void setName(const QString& str);
288 void setKeywords(const QString& str);
289 void setDescription(const QString& str);
290 void setLinks(const QList<IGisItem::link_t>& links);
291 /**
292 @brief Set change mark
293 */
294 void setChanged();
295
296 /**
297 @brief Set the sorting mode for the roadbook in the details dialog
298
299 This will mark the project as changed.
300
301 @param s the mode
302 */
303 void setSortingRoadbook(sorting_roadbook_e s);
304
305 /**
306 @brief Set the sorting mode for workspace folder
307
308 This will mark the project as changed.
309
310 @param s the mode
311 */
312 void setSortingFolder(sorting_folder_e s);
313
314 /**
315 @brief Get a short metadata summary
316 @return Informational string.
317 */
318 virtual QString getInfo() const;
319 /**
320 @brief Get a temporary pointer to the item with matching key
321 @param key
322 @return If no item is found 0 is returned.
323 */
324 IGisItem* getItemByKey(const IGisItem::key_t& key);
325
326 void getItemsByKeys(const QList<IGisItem::key_t>& keys, QList<IGisItem*>& items);
327 /**
328 @brief Get a list of items that are close to a given pixel coordinate of the screen
329
330 @note: The returned pointers are just for temporary use. Best you use them to get the item's key.
331
332 @param pos the coordinate on the screen in pixel
333 @param items a list the item's pointer is stored to.
334 */
335 void getItemsByPos(const QPointF& pos, QList<IGisItem*>& items);
336
337 void getItemsByArea(const QRectF& area, IGisItem::selflags_t flags, QList<IGisItem*>& items);
338
339 void getNogoAreas(QList<IGisItem*>& nogos) const;
340
getItemCountByType(IGisItem::type_e type)341 int getItemCountByType(IGisItem::type_e type) const
342 {
343 return cntItemsByType[type];
344 }
345
getTotalDistance()346 qreal getTotalDistance() const
347 {
348 return totalDistance;
349 }
getTotalAscent()350 qreal getTotalAscent() const
351 {
352 return totalAscent;
353 }
getTotalDescent()354 qreal getTotalDescent() const
355 {
356 return totalDescent;
357 }
getTotalElapsedSeconds()358 qreal getTotalElapsedSeconds() const
359 {
360 return totalElapsedSeconds;
361 }
getTotalElapsedSecondsMoving()362 qreal getTotalElapsedSecondsMoving() const
363 {
364 return totalElapsedSecondsMoving;
365 }
366
doCorrelation()367 bool doCorrelation() const
368 {
369 return !noCorrelation;
370 }
371
372 void switchOnCorrelation();
373
374 void setAutoSave(bool on);
375
setInvalidDataOk(bool ok)376 void setInvalidDataOk(bool ok)
377 {
378 invalidDataOk = ok;
379 setChanged();
380 }
381
getInvalidDataOk()382 bool getInvalidDataOk() const
383 {
384 return invalidDataOk;
385 }
386
387 /**
388 @brief Receive the current mouse position
389
390 Iterate over all items and pass the position
391
392 @param pos the mouse position on the screen in pixel
393 */
394 virtual void mouseMove(const QPointF& pos);
395
396 /**
397 @brief Delete items with matching key
398 @param key
399 */
400 bool delItemByKey(const IGisItem::key_t& key, QMessageBox::StandardButtons& last);
401
402 /**
403 @brief Call IGisItem::edit() method for items with given key
404
405 @param key a MD5 hash key
406 */
407 void editItemByKey(const IGisItem::key_t& key);
408
409 /**
410 @brief Add a copy if the given item to the project
411
412 Before the item is inserted the method will use it's key to find a duplicate item.
413 If there is an item with the same item key a copy option dialog is shown. Depending
414 the result the action is performed or aborted. The result will be copied into
415 lastResult to repeat the same decision on subsequent items.
416
417 @param item pointer to item
418 @param off the offset into the tree widget, -1 for none
419 @param lastResult a reference to hold the last result of the copy option dialog
420 */
421 void insertCopyOfItem(IGisItem* item, int off, CSelectCopyAction::result_e& lastResult);
422
423 /**
424 @brief Check if the project was initialized correctly.
425
426 For example a if a GPX file does not load correctly the project is invalid.
427
428 @return True if project is valid
429 */
isValid()430 bool isValid() const
431 {
432 return valid;
433 }
434
435 /**
436 @brief Test if visibility check mark is set
437 @return True if project is visible
438 */
439 bool isVisible() const;
440
isAutoSave()441 bool isAutoSave() const
442 {
443 return autoSave;
444 }
445
446 /**
447 @brief Test if this project is handled by a device
448 @return The device type (IDevice::type_e). IDevice::eTypeNone if the project is not stored on a device.
449 */
450 qint32 isOnDevice() const;
451
452 /**
453 @brief Test if project has been changed
454 @return True if changed.
455 */
456 bool isChanged() const;
457
458 void drawItem(QPainter& p, const QPolygonF& viewport, QList<QRectF>& blockedAreas, CGisDraw* gis);
459 void drawLabel(QPainter& p, const QPolygonF& viewport, QList<QRectF>& blockedAreas, const QFontMetricsF& fm, CGisDraw* gis);
460 void drawItem(QPainter& p, const QRectF& viewport, CGisDraw* gis);
461
462 /**
463 @brief Serialize object out of a QDataStream
464
465 See CGisSerialization.cpp for implementation
466
467 @param stream the binary data stream
468 @return The stream object.
469 */
470 virtual QDataStream& operator<<(QDataStream& stream);
471
472 /**
473 @brief Serialize object into a QDataStream
474
475 See CGisSerialization.cpp for implementation
476
477 @param stream the binary data stream
478 @return The stream object.
479 */
480 virtual QDataStream& operator>>(QDataStream& stream) const;
481
482 /**
483 @brief writeMetadata
484 @param doc
485 @return
486 */
487 QDomNode writeMetadata(QDomDocument& doc, bool strictGpx11);
488
489 /**
490 @brief Mount volume the project's file is stored at
491
492 This is only valid for projects located on GPS devices.
493 For all other projects the method does nothing.
494 */
495 void mount();
496 /**
497 @brief Umount volume the project's file is stored at
498
499 This is only valid for projects located on GPS devices.
500 For all other projects the method does nothing.
501 */
502 void umount();
503
504 /**
505 @brief Removed the projects file from disk.
506
507 This is only valid for projects located on GPS devices.
508 For all other projects the method does nothing.
509 */
510 bool remove();
511
512 /**
513 @brief Block update of items.
514
515 Use this to speed up actions with many items, e.g. copy actions.
516 If the blocking is stopped (yes == false) updateItems() is called.
517
518 @param yes set true to block updating items
519 */
520 void blockUpdateItems(bool yes);
521
522 /**
523 @brief Return state of current update block
524 @return True if updates are blocked.
525 */
blockUpdateItems()526 bool blockUpdateItems() const
527 {
528 return noUpdate;
529 }
530
531 void setProjectFilter(const CSearch& search);
532 void setWorkspaceFilter(const CSearch& search);
533 void applyFilters();
534
confirmPendingAutoSave()535 void confirmPendingAutoSave()
536 {
537 autoSavePending = false;
538 }
confirmPendingAutoSyncToDev()539 void confirmPendingAutoSyncToDev()
540 {
541 autoSyncToDevPending = false;
542 }
543
544 bool findPolylineCloseBy(const QPointF& pt1, const QPointF& pt2, qint32& threshold, QPolygonF& polyline);
545
546 void gainUserFocus(bool yes);
547
hasUserFocus()548 bool hasUserFocus() const
549 {
550 return keyUserFocus == key;
551 }
552
getUserFocus()553 static const QString& getUserFocus()
554 {
555 return keyUserFocus;
556 }
557
558 void setAutoSyncToDevice(bool yes);
559
doAutoSyncToDevice()560 bool doAutoSyncToDevice() const
561 {
562 return autoSyncToDev;
563 }
564
565 CProjectFilterItem* filterProject(bool filter);
getProjectFilterItem()566 CProjectFilterItem* getProjectFilterItem()
567 {
568 return projectFilter;
569 }
570 protected:
571 void genKey() const;
572 virtual void setupName(const QString& defaultName);
573 void markAsSaved();
574 void readMetadata(const QDomNode& xml, metadata_t& metadata);
575 void updateItems();
576 void updateItemCounters();
577 void updateDecoration();
578 void updateDecoration(bool saved);
579 void sortItems();
580 void sortItems(QList<IGisItem*>& items) const;
581
582 /**
583 @brief Converts a string with HTML tags to a string without HTML depending on the device
584
585 Some devices e.g. Garmin can not handle HTML.
586
587 @param str a string
588 @return A string with HTML removed depending on the device
589 */
590 QString html2Dev(const QString& str);
591
592 // Those are the URIs of the GPX extensions we support
593 static const QString gpxx_ns;
594 static const QString gpxtpx_ns;
595 static const QString wptx1_ns;
596 static const QString rmc_ns;
597 static const QString ql_ns;
598 static const QString gs_ns;
599 static const QString tp1_ns;
600 // Those are standard GPX/XML namespaces
601 static const QString gpx_ns;
602 static const QString xsi_ns;
603 static const QString gpxdata_ns;
604
605 static QString keyUserFocus;
606
607 QPointer<CDetailsPrj> dlgDetails;
608
609 type_e type;
610 mutable QString key;
611 QString filename;
612 bool valid = false;
613 bool noUpdate = false;
614 bool noCorrelation = false;
615 bool changedRoadbookMode = false;
616 bool autoSave = false; ///< flag to show if auto save is on or off
617 bool autoSavePending = false; ///< flag to show if auto save event has been sent. will be reset by save()
618 bool invalidDataOk = false; ///< if set invalid data in GIS items will not raise any dialog
619 bool autoSyncToDev = false; ///< if set true sync the project with every device connected
620 bool autoSyncToDevPending = false; ///< flag to show that a sync to device is already pending
621
622 metadata_t metadata;
623 QString nameSuffix;
624
625 sorting_roadbook_e sortingRoadbook = eSortRoadbookNone;
626 sorting_folder_e sortingFolder = eSortFolderTime;
627
628 qint32 cntItemsByType[IGisItem::eTypeMax];
629
630 qint32 cntTrkPts = 0;
631 qint32 cntWpts = 0;
632
633 qreal totalDistance = 0;
634 qreal totalAscent = 0;
635 qreal totalDescent = 0;
636 quint32 totalElapsedSeconds = 0;
637 quint32 totalElapsedSecondsMoving = 0;
638
639 QString hashTrkWpt[2];
640
641 CSearch projectSearch = CSearch("");
642 CSearch workspaceSearch = CSearch("");
643
644 CProjectFilterItem* projectFilter = nullptr;
645 };
Q_DECLARE_METATYPE(IGisProject *)646 Q_DECLARE_METATYPE(IGisProject*)
647
648 class CProjectMountLock
649 {
650 public:
651 CProjectMountLock(IGisProject& project)
652 : project(project)
653 {
654 project.mount();
655 }
656
657 ~CProjectMountLock()
658 {
659 project.umount();
660 }
661
662 private:
663 IGisProject& project;
664 };
665
666 #endif //IGISPROJECT_H
667
668