1 /**********************************************************************************************
2     Copyright (C) 2014 Oliver Eichler <oliver.eichler@gmx.de>
3     Copyright (C) 2020 Henri Hornburg <hrnbg@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 IGISITEM_H
21 #define IGISITEM_H
22 
23 #include <QTreeWidgetItem>
24 
25 #include <QColor>
26 #include <QCoreApplication>
27 #include <QDateTime>
28 #include <QDomNode>
29 #include <QMap>
30 #include <QMutex>
31 #include <QPainter>
32 #include <QString>
33 #include <QStringList>
34 #include <QUrl>
35 #include <QVariant>
36 
37 #include "units/IUnit.h"
38 
39 class CGisDraw;
40 class IScrOpt;
41 class IMouse;
42 class QSqlDatabase;
43 class IGisProject;
44 struct searchValue_t;
45 enum searchProperty_e : unsigned int;
46 
47 class IGisItem : public QTreeWidgetItem
48 {
49     Q_DECLARE_TR_FUNCTIONS(IGisItem)
50 public:
51     struct history_event_t
52     {
53         QDateTime time;
54         QString hash;
55         QString who = "QMapShack";
56         QString icon;
57         QString comment;
58         QByteArray data;
59     };
60 
61     struct history_t
62     {
history_thistory_t63         history_t() : histIdxInitial(NOIDX), histIdxCurrent(NOIDX)
64         {
65         }
66 
resethistory_t67         void reset()
68         {
69             histIdxInitial = NOIDX;
70             histIdxCurrent = NOIDX;
71             events.clear();
72         }
73 
74         qint32 histIdxInitial;
75         qint32 histIdxCurrent;
76         QList<history_event_t> events;
77     };
78 
79 
80     struct link_t
81     {
82         QUrl uri;
83         QString text;
84         QString type;
85     };
86 
87     struct wpt_t
88     {
wpt_twpt_t89         wpt_t() :
90             lat(NOFLOAT),
91             lon(NOFLOAT),
92             ele(NOINT),
93             magvar(NOINT),
94             geoidheight(NOINT),
95             sat(NOINT),
96             hdop(NOINT),
97             vdop(NOINT),
98             pdop(NOINT),
99             ageofdgpsdata(NOINT),
100             dgpsid(NOINT)
101         {
102         }
103 
QPointFwpt_t104         operator QPointF() const
105         {
106             return QPointF(lon, lat);
107         }
108 
109         // -- all gpx tags - start
110         qreal lat;
111         qreal lon;
112         qint32 ele;
113         QDateTime time;
114         qint32 magvar;
115         qint32 geoidheight;
116         QString name;
117         QString cmt;
118         QString desc;
119         QString src;
120         QList<link_t> links;
121         QString sym;
122         QString type;
123         QString fix;
124         qint32 sat;
125         qint32 hdop;
126         qint32 vdop;
127         qint32 pdop;
128         qint32 ageofdgpsdata;
129         qint32 dgpsid;
130         // -- all gpx tags - stop
131         QMap<QString, QVariant> extensions;
132     };
133 
134     /// never ever change these numbers. it will break binary data files
135     enum type_e
136     {
137         eTypeWpt = 1
138         , eTypeTrk = 2
139         , eTypeRte = 3
140         , eTypeOvl = 4
141         , eTypeMax = 5
142     };
143 
144     enum mark_e
145     {
146         eMarkNone      = 0
147         , eMarkChanged   = 0x00000001
148         , eMarkNotPart   = 0x00000002
149         , eMarkNotInDB   = 0x00000004
150     };
151 
152     enum selection_e
153     {
154         eSelectionNone          = 0
155         , eSelectionExact       = 0x00000001
156         , eSelectionIntersect   = 0x00000002
157         , eSelectionTrk         = 0x80000000
158         , eSelectionWpt         = 0x40000000
159         , eSelectionRte         = 0x20000000
160         , eSelectionOvl         = 0x10000000
161         , eSelectionPoi         = 0x08000000
162     };
163 
164     using selflags_t = quint32;
165 
166     enum color_e
167     {
168         eColorBlack         = 0
169         , eColorDarkRed     = 1
170         , eColorDarkGreen   = 2
171         , eColorDarkYellow  = 3
172         , eColorDarkBlue    = 4
173         , eColorDarkMagenta = 5
174         , eColorDarkCyan    = 6
175         , eColorLightGray   = 7
176         , eColorDarkGray    = 8
177         , eColorRed         = 9
178         , eColorGreen       = 10
179         , eColorYellow      = 11
180         , eColorBlue        = 12
181         , eColorMagenta     = 13
182         , eColorCyan        = 14
183         , eColorWhite       = 15
184         , eColorTransparent = 16
185     };
186 
187     struct key_t
188     {
189         bool operator==(const key_t& k) const
190         {
191             return (item == k.item) && (project == k.project) && (device == k.device);
192         }
193         bool operator!=(const key_t& k) const
194         {
195             return (item != k.item) || (project != k.project) || (device != k.device);
196         }
clearkey_t197         void clear()
198         {
199             item.clear();
200             project.clear();
201             device.clear();
202         }
203         QString item;
204         QString project;
205         QString device;
206     };
207 
208     IGisItem(IGisProject* parent, type_e typ, int idx);
209     virtual ~IGisItem();
210 
211     /// this mutex has to be locked when ever the item list is accessed.
212     static QMutex mutexItems;
213 
214     static void init();
215     static QMenu* getColorMenu(const QString& title, QObject* obj, const char* slot, QWidget* parent);
216     static qint32 selectColor(QWidget* parent);
217 
218     /**
219        @brief If the item is part of a database project it will update itself with the database content
220      */
221     virtual void updateFromDB(quint64 id, QSqlDatabase& db);
222 
223     /**
224        @brief Update the visual representation of the QTreeWidgetItem
225        @param enable
226        @param disable
227      */
228     virtual void updateDecoration(quint32 enable, quint32 disable);
229 
230     /**
231        @brief Save the item's data into a GPX structure
232        @param gpx       the files <gpx> tag to attach the data to
233      */
234     virtual void save(QDomNode& gpx, bool strictGpx11) = 0;
235 
236     /**
237        @brief Get key string to identify object
238        @return
239      */
240     const key_t& getKey() const;
241 
242     /**
243        @brief Get a hash over the items data.
244 
245        Every entry in the history has a hash over the item's serialized data. If the
246        data changes a new history entry is created and a new hash calculated. Thus the
247        has can be used to detect if an item has been changed between the last time the
248        hash was read.
249 
250        @return The hash as a string reference.
251      */
252     const QString& getHash();
253 
254     /**
255        @brief Get the hash stored in the database when the item was loaded
256 
257        @return The hash as a string
258      */
259     const QString& getLastDatabaseHash();
260 
261     /**
262        @brief Read the hash stored in the database
263      */
264     void setLastDatabaseHash(quint64 id, QSqlDatabase& db);
265 
266     /**
267        @brief Get the icon attached to object
268        @return
269      */
270     void setIcon(const QPixmap& icon);
271 
getIcon()272     const QPixmap& getIcon() const
273     {
274         return icon;
275     }
276 
getDisplayIcon()277     const QPixmap& getDisplayIcon() const
278     {
279         return displayIcon;
280     }
281     /**
282        @brief Get name of this item.
283        @return A reference to the internal string object
284      */
285     virtual const QString& getName() const = 0;
286 
287     /**
288        @brief Get name of this item extended by the project name
289        @return A string object.
290      */
291     virtual QString getNameEx() const;
292 
293 
294     enum features_e
295     {
296         eFeatureNone            = 0
297         , eFeatureShowName      = 0x00000001
298         , eFeatureShowFullText  = 0x00000002
299         , eFeatureShowActivity  = 0x00000004
300         , eFeatureShowDateTime  = 0x00000008
301         , eFeatureShowLinks     = 0x00000010
302     };
303 
304     /**
305        @brief Get a short string with the items properties to be displayed in tool tips or similar
306 
307        @param showName          set true if the first line should be the item's name
308        @param features          a combination of features_e types
309 
310        @return A string object.
311      */
312     virtual QString getInfo(quint32 features) const = 0;
313 
314     virtual const QString& getComment() const = 0;
315     virtual const QString& getDescription() const = 0;
316     virtual const QList<link_t>& getLinks() const = 0;
317     virtual QDateTime getTimestamp() const = 0;
318 
319 
320     virtual void setComment(const QString& str) = 0;
321     virtual void setDescription(const QString& str) = 0;
322     virtual void setLinks(const QList<link_t>& links) = 0;
323 
324     /**
325         @brief Edit content of item.
326 
327         This is quite dependent on the item. The default implementation does nothing. It has to be
328         overwritten and the item has to generate what ever is needed to edit/view it's details.
329 
330      */
edit()331     virtual void edit()
332     {
333     }
334 
335     /**
336        @brief Get the dimension of the item
337 
338        All coordinates are in Rad. Items with no
339 
340        @return
341      */
getBoundingRect()342     virtual const QRectF& getBoundingRect() const
343     {
344         return boundingRect;
345     }
346 
347     /**
348        @brief Get screen option object to display and handle actions for this item.
349        @param mouse     a pointer to the mouse object initiating the action
350        @return A null pointer is returned if no screen option are available
351      */
getScreenOptions(const QPoint & origin,IMouse * mouse)352     virtual IScrOpt* getScreenOptions(const QPoint& origin, IMouse* mouse)
353     {
354         return nullptr;
355     }
356 
357     /**
358        @brief Get a point of the item that is close by the given screen pixel coordinate
359        @param point     a point in screen pixels
360        @return If no point is found NOPOINTF is returned.
361      */
getPointCloseBy(const QPoint & point)362     virtual QPointF getPointCloseBy(const QPoint& point)
363     {
364         return NOPOINTF;
365     }
366 
367     /**
368        @brief Test if the item is close to a given pixel coordinate of the screen
369 
370        @param pos       the coordinate on the screen in pixel
371        @return If no point can be found NOPOINTF is returned.
372      */
373     virtual bool isCloseTo(const QPointF& pos) = 0;
374 
375     virtual bool isWithin(const QRectF& area, selflags_t mode) = 0;
376 
377     /**
378        @brief Receive the current mouse position
379 
380        The default does nothing. Override if needed.
381 
382        @param pos   the mouse position on the screen in pixel
383      */
mouseMove(const QPointF & pos)384     virtual void mouseMove(const QPointF& pos)
385     {
386         Q_UNUSED(pos);
387     }
388 
389     /**
390        @brief Query if this item is read only
391        @return True if it is read only.
392      */
393     bool isReadOnly() const;
394 
395     /**
396        @brief Query if the item is imported and was changed
397        @return True if content was changed.
398      */
399     bool isTainted() const;
400 
401     /**
402        @brief Check if item is on a GPS device
403        @return The device type (IDevice::type_e). IDevice::eTypeNone if the item is not stored on a device.
404      */
405     qint32 isOnDevice() const;
406 
407     /**
408        @brief Check if there are any pending unsaved changes
409        @return True if the are changes to be saved
410      */
411     bool isChanged() const;
412 
413     /**
414        @brief Set the read only mode.
415 
416        This is quite dependent on the item. The default implementation will display a
417        message box with a warning and ask the user to confirm.
418 
419        @param readOnly      set true to make item read only
420 
421        @return Return true if the mode change has been accepted.
422      */
423     virtual bool setReadOnlyMode(bool readOnly);
424 
425     virtual void drawItem(QPainter& p, const QPolygonF& viewport, QList<QRectF>& blockedAreas, CGisDraw* gis) = 0;
drawItem(QPainter & p,const QRectF & viewport,CGisDraw * gis)426     virtual void drawItem(QPainter& p, const QRectF& viewport, CGisDraw* gis)
427     {
428     }
429     virtual void drawLabel(QPainter& p, const QPolygonF& viewport, QList<QRectF>& blockedAreas, const QFontMetricsF& fm, CGisDraw* gis) = 0;
430     virtual void drawHighlight(QPainter& p) = 0;
431 
432     virtual void gainUserFocus(bool yes) = 0;
433 
434     /**
435        @brief Check for user focus
436 
437        @return True if the item has user focus. The default implementation is always false.
438      */
hasUserFocus()439     virtual bool hasUserFocus() const
440     {
441         return false;
442     }
443 
444 
445     /**
446        @brief Serialize object out of a QDataStream
447 
448        See CGisSerialization.cpp for implementation
449 
450        @param stream the binary data stream
451        @return The stream object.
452      */
453     virtual QDataStream& operator<<(QDataStream& stream) = 0;
454     /**
455        @brief Serialize object into a QDataStream
456 
457        See CGisSerialization.cpp for implementation
458 
459        @param stream the binary data stream
460        @return The stream object.
461      */
462     virtual QDataStream& operator>>(QDataStream& stream) const = 0;
463 
464     /**
465        @brief Get read access to history of changes
466 
467        @return A reference to the history structure.
468      */
getHistory()469     const history_t& getHistory() const
470     {
471         return history;
472     }
473 
474     /**
475        @brief Load a given state of change from the history
476        @param idx
477      */
478     void loadHistory(int idx);
479 
480     /**
481        @brief Remove all history entries younger than the current selected one.
482      */
483     void cutHistoryAfter();
484 
485     /**
486        @brief Remove all history entries older than the current selected one.
487      */
488     void cutHistoryBefore();
489 
490     /**
491        @brief Take data of the most recent entry and apply meta information of first one
492 
493        All other entries are lost
494      */
495     void squashHistory();
496 
497     /**
498        @brief Create a clone of itself and pass back the pointer
499 
500        Add the cloned item to the project with the same index as the original
501 
502        @return The pointer of the cloned item
503      */
504     virtual IGisItem* createClone() = 0;
505 
506     void setNogo(bool yes);
isNogo()507     bool isNogo() const
508     {
509         return bool(flags & eFlagNogo);
510     }
511 
512     static const QBrush& getNogoTextureBrush();
513 
514     IGisProject* getParentProject() const;
515 
516     /**
517        @brief Remove all HTML tags from a string
518        @param str the string
519        @return A string without HTML tags
520      */
521     static QString removeHtml(const QString& str);
522     /**
523        @brief Create a HTML formatted text with comment, description and link section.
524 
525        Depending on the isReadOnly flag the section headers are links to trigger a function
526 
527        @param isReadOnly    true if the text should have no active links
528        @param cmt           the comment string
529        @param desc          the description string
530        @param links         a list of links
531        @param key           some key to be sent with the header links
532        @return The formatted text ready to be used.
533      */
534     static QString createText(bool isReadOnly, const QString& cmt, const QString& desc, const QList<link_t>& links, const QString& key = "");
535     /**
536        @brief Create a HTML formatted text with description and link section.
537 
538        Depending on the isReadOnly flag the section headers are links to trigger a function
539 
540        @param isReadOnly    true if the text should have no active links
541        @param desc          the description string
542        @param links         a list of links
543        @param key           some key to be sent with the header links
544        @return The formatted text ready to be used.
545      */
546     static QString createText(bool isReadOnly, const QString& desc, const QList<link_t>& links, const QString& key = "");
547     /**
548        @brief Create a HTML formatted text with a link.
549 
550        Depending on the isReadOnly flag the section headers are links to trigger a function
551 
552        @param isReadOnly    true if the text should have no active links
553        @param href          the link address
554        @param str           the link's string
555        @param key           some key to be sent with the link
556        @return The formated text ready to be used.
557      */
558     static QString toLink(bool isReadOnly, const QString& href, const QString& str, const QString& key);
559 
560     /**
561        @brief Unified handler to get a new item name and a pointer to the traget project
562 
563        @param name      a reference to a string object with the default name and to receive the name
564        @param project   a reference to a IGisProject pointer. On success it will point to the project instance
565        @param itemtype  a string to be used for the item type in the dialogs
566 
567 
568        @return Returns true on success. Otherwise false.
569      */
570     static bool getNameAndProject(QString& name, IGisProject*& project, const QString& itemtype);
571 
572 
573     static IGisItem* newGisItem(quint32 type, quint64 id, QSqlDatabase& db, IGisProject* project);
574 
575 
576     /// a no key value that can be used to nullify references.
577     const static QString noKey;
578 
579     const static QString noName;
580 
581     struct color_t
582     {
583         const char* name;
584         const QString label;
585         const QColor color;
586         const QString bullet;
587         const QString line;
588     };
589 
getColorMap()590     static const QVector<color_t>& getColorMap()
591     {
592         return colorMap;
593     }
594 
595     virtual const searchValue_t getValueByKeyword(searchProperty_e keyword) = 0;
596 
597     qreal getRating() const;
598     void setRating(qreal rating);
599     const QSet<QString>& getKeywords() const;
600     QList<QString> getKeywordsSorted() const;
601     void addKeywords(const QSet<QString>& otherKeywords);
602     void removeKeywords(const QSet<QString>& otherKeywords);
603     const QString getRatingKeywordInfo() const;
604 
605 protected:
606     /// set icon of QTreeWidgetItem
607     virtual void setSymbol() = 0;
608     /// read waypoint data from an XML snippet
609     void readWpt(const QDomNode& xml, wpt_t& wpt);
610     /// write waypoint data to an XML snippet
611     void writeWpt(QDomElement& xml, const wpt_t& wpt, bool strictGpx11);
612     /// generate a unique key from item's data
613     virtual void genKey() const;
614     /// setup the history structure right after the creation of the item
615     void setupHistory();
616     /// update current history entry (e.g. to save the flags)
617     virtual void updateHistory();
618     /// convert a color string from GPX to a QT color
619     QColor str2color(const QString& name);
620     /// convert a QT color to a string to be used in a GPX file
621     QString color2str(const QColor& color);
622     /// to optimize drawing of large polylines split the line into sections that are visible
623     void splitLineToViewport(const QPolygonF& line, const QRectF& extViewport, QList<QPolygonF>& lines);
624     /// call when ever you make a change to the item's data
625     virtual void changed(const QString& what, const QString& icon);
626 
627     void loadFromDb(quint64 id, QSqlDatabase& db);
628     bool isVisible(const QRectF& rect, const QPolygonF& viewport, CGisDraw* gis);
629     bool isVisible(const QPointF& point, const QPolygonF& viewport, CGisDraw* gis);
630     bool isWithin(const QRectF& area, selflags_t flags, const QPolygonF& points);
631     void setNogoFlag(bool yes);
632 
633     /**
634        @brief Converts a string with HTML tags to a string without HTML depending on the device
635 
636        Some devices e.g. Garmin can not handle HTML.
637 
638        @param str   a string
639        @return A string with HTML removed depending on the device
640      */
641     QString html2Dev(const QString& str, bool strictGpx11);
642 
643     /// see flags_e for possible flags
644     quint32 flags = 0;
645     /// the item's unique key
646     mutable key_t key;
647     /// each item has an icon for the tree widget
648     QPixmap icon;
649     QPixmap displayIcon;
650     /// the dimensions of the item
651     QRectF boundingRect;
652     /// that's where the real data is. An item is completely defined by it's history
653     history_t history;
654     /// the hash in the database when the item was loaded/saved
655     QString lastDatabaseHash;
656 
657     enum flags_e
658     {
659         eFlagCreatedInQms   = 0x00000001
660         , eFlagWriteAllowed  = 0x00000002
661         , eFlagTainted       = 0x00000004
662         , eFlagWptBubble     = 0x00000100
663         , eFlagNogo          = 0x00000200
664     };
665 
666     static QVector<color_t> colorMap;
667 
668     /// labeling the GisItems
669     qreal rating = 0;
670     QSet<QString> keywords;
671 private:
672     void showIcon();
673 };
674 
675 QDataStream& operator>>(QDataStream& stream, IGisItem::history_t& h);
676 QDataStream& operator<<(QDataStream& stream, const IGisItem::history_t& h);
677 
678 #endif //IGISITEM_H
679 
680