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