1 /**********************************************************************************************
2     Copyright (C) 2014 Oliver Eichler <oliver.eichler@gmx.de>
3     Copyright (C) 2017 Norbert Truchsess <norbert.truchsess@t-online.de>
4     Copyright (C) 2019 Henri Hornburg <hrnbg@t-online.de>
5 
6     This program is free software: you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation, either version 3 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 
19 **********************************************************************************************/
20 
21 #ifndef CGISITEMWPT_H
22 #define CGISITEMWPT_H
23 
24 #include "gis/IGisItem.h"
25 #include "gis/tnv/CTwoNavProject.h"
26 
27 #include <QCoreApplication>
28 #include <QPointer>
29 
30 struct poi_t;
31 class IGisProject;
32 class QDomNode;
33 class CScrOptWpt;
34 class CScrOptWptRadius;
35 class QSqlDatabase;
36 class CQlgtWpt;
37 class QTextEdit;
38 class QDir;
39 class CFitStream;
40 class CGisItemWpt : public IGisItem
41 {
42     Q_DECLARE_TR_FUNCTIONS(CGisItemWpt)
43 public:
44     enum geocacheservice_e {eGcCom, eOc, eTc, eGcSu, eUnknown=-1};
45 
46     struct geocachelog_t
47     {
48         quint32 id = 0;
49         QDateTime date;
50         QString type;
51         QString finderId;
52         QString finder;
53         bool textIsHtml = false;
54         QString text;
55     };
56 
57     /*
58      * The Date the geocache was hidden is stored in the enclosing wpt's time
59      */
60     struct geocache_t
61     {
62         geocacheservice_e service = eUnknown;
63         bool hasData = false;
64         quint32 id = 0;
65         bool available = true;
66         bool archived = false;
67         bool needsMaintenance = false;
68         qreal difficulty = 0;
69         qreal terrain = 0;
70         QString name;
71         QString owner;
72         QString ownerId;
73         QString type;
74         QString container;
75         //See ZGeocacheAttributes.txt for meanings.
76         QMap<quint8, bool> attributes;
77         bool shortDescIsHtml = false;
78         QString shortDesc;
79         bool longDescIsHtml = false;
80         QString longDesc;
81         QString hint;
82         QString country;
83         QString state;
84         QString locale;
85         QList<geocachelog_t> logs;
86 
87         const static QList<QString> attributeMeanings;
88         static QList<QString> attributeMeaningsTranslated;
89         static QList<QString> initAttributeMeaningsTranslated();
90         QDateTime getLastFound() const;
91         QString getLogs() const;
92     };
93 
94     struct image_t
95     {
96         QImage pixmap;
97         qreal direction = 0;
98         QString info;
99         QString filePath;
100         QString fileName;
101     };
102 
103     CGisItemWpt(const QPointF& pos, qreal ele, const QDateTime& time, const QString& name, const QString& icon, IGisProject* project);
104 
105     /**
106        @brief Create a completely new waypoint
107        @param pos       the waypoint's position [°]
108        @param name      the waypoint's name
109        @param icon      the waypoint's icon
110        @param project   the project the waypoint is added to
111      */
112     CGisItemWpt(const QPointF& pos, const QString& name, const QString& icon, IGisProject* project);
113     /**
114        @brief Create a copy of an existing waypoint with a new position
115        @param pos       the waypoint's new position [°]
116        @param parentWpt the waypoint to copy
117        @param project   the project the waypoint is added to
118      */
119     CGisItemWpt(const QPointF& pos, const CGisItemWpt& parentWpt, IGisProject* project);
120     /**
121        @brief Create a 1:1 copy of an existing waypoint (with new key)
122        @param parentWpt the waypoint to copy
123        @param project   the project the waypoint is added to
124        @param idx       the index to insert the item. If -1 the item will be appended to it's group
125      */
126     CGisItemWpt(const CGisItemWpt& parentWpt, IGisProject* project, int idx, bool clone);
127     /**
128        @brief Create item from GPX.
129        @param xml       the GPX section containing the item
130        @param project   the project to append with item
131      */
132     CGisItemWpt(const QDomNode& xml, IGisProject* project);
133 
134     /**
135        @brief Create item from list of changes
136        @param hist      the change history
137        @param project   the project to append with item
138      */
139     CGisItemWpt(const history_t& hist, const QString& dbHash, IGisProject* project);
140 
141     /**
142        @brief Read item from database by it's database ID
143        @param id        the item's ID in the database
144        @param db        the database itself
145        @param project   the project to append with item
146      */
147     CGisItemWpt(quint64 id, QSqlDatabase& db, IGisProject* project);
148 
149     /**
150        @brief Read item from text stream with TwoNav encoding
151        @param tnvWpt
152        @param project
153      */
154     CGisItemWpt(const CTwoNavProject::wpt_t& tnvWpt, IGisProject* project);
155 
156     CGisItemWpt(const CQlgtWpt& wpt1, IGisProject* project = nullptr);
157 
158     CGisItemWpt(CFitStream& stream, IGisProject* project);
159 
160     virtual ~CGisItemWpt();
161 
162     IGisItem* createClone() override;
163 
164     /**
165        @brief Save waypoint to GPX tree
166        @param gpx   The <gpx> node to append by the waypoint
167      */
168     void save(QDomNode& gpx, bool strictGpx11) override;
169     /**
170        @brief Save waypoint to TwoNav waypoint file
171        @param out   the text stream to write to
172      */
173     void saveTwoNav(QTextStream& out, const QDir& dir);
174     /**
175        @brief Save waypoint to TCX file
176        @param  courseNode  The course node to append by the waypoint
177        @param  crsPtDateTimeToBeSaved  course point dateTime to be saved (NOT the waypoint date and time !)
178      */
179     void saveTCX(QDomNode& courseNode, const QDateTime crsPtDateTimeToBeSaved);
180     /**
181        @brief Read serialized waypoint from a binary data stream
182        @param stream  the data stream to read from
183        @return A reference to the stream
184      */
185     QDataStream& operator<<(QDataStream& stream) override;
186     /**
187        @brief Serialize waypoint into a binary data stream
188        @param stream  the data stream to write to.
189        @return A reference to the stream
190      */
191     QDataStream& operator>>(QDataStream& stream) const override;
192 
193     void setName(const QString& str);
194     void setPosition(const QPointF& pos);
195     void setElevation(qint32 val);
196     void setProximity(qreal val);
197     void setIcon(const QString& name);
198     void setComment(const QString& str)         override;
199     void setDescription(const QString& str)         override;
200     void setLinks(const QList<link_t>& links) override;
201     void setImages(const QList<image_t>& imgs);
202 
203     /**
204        @brief Silently append list of links
205 
206        Devices uses links to reference multimedia content attached to the waypoint.
207        These links have to be added to the list of normal links. See removeLinksByType()
208        on how to remove these links again.
209 
210        @param links  list of links.
211      */
appendLinks(const QList<link_t> & links)212     void appendLinks(const QList<link_t>& links)
213     {
214         wpt.links = links + wpt.links;
215     }
216 
217     /**
218        @brief Silently append list of images
219 
220        This is used to restore images from a device. As these images where part of the waypoint
221        object in the first place they have to be added to the waypoint again without creating
222        a new history entry.
223 
224        @param imgs  list of images
225      */
appendImages(const QList<image_t> & imgs)226     void appendImages(const QList<image_t>& imgs)
227     {
228         images += imgs;
229     }
230 
231     /**
232        @brief Append the list of images by a single image.
233        @param img   a single image
234      */
235     void addImage(const image_t& img);
236 
237 
getName()238     const QString& getName() const override
239     {
240         return wpt.name.isEmpty() ? noName : wpt.name;
241     }
242 
243     QString getInfo(quint32 feature) const override;
getPosition()244     QPointF getPosition() const
245     {
246         return QPointF(wpt.lon, wpt.lat);
247     }
248 
getElevation()249     qint32 getElevation() const
250     {
251         return wpt.ele;
252     }
getProximity()253     qreal getProximity() const
254     {
255         return proximity;
256     }
getTime()257     const QDateTime& getTime() const
258     {
259         return wpt.time;
260     }
getIconName()261     const QString& getIconName() const
262     {
263         return wpt.sym;
264     }
getComment()265     const QString& getComment() const override
266     {
267         return wpt.cmt;
268     }
getDescription()269     const QString& getDescription() const override
270     {
271         return wpt.desc;
272     }
getGeoCache()273     const geocache_t& getGeoCache() const
274     {
275         return geocache;
276     }
getLinks()277     const QList<link_t>& getLinks() const override
278     {
279         return wpt.links;
280     }
getImages()281     const QList<image_t>& getImages() const
282     {
283         return images;
284     }
285 
getTimestamp()286     QDateTime getTimestamp() const override
287     {
288         return wpt.time;
289     }
290 
291 
292     IScrOpt* getScreenOptions(const QPoint& origin, IMouse* mouse) override;
293 
294     QPointF getPointCloseBy(const QPoint& point) override;
295 
296     void drawItem(QPainter& p, const QPolygonF& viewport, QList<QRectF>& blockedAreas, CGisDraw* gis) override;
297     void drawItem(QPainter& p, const QRectF& viewport, CGisDraw* gis) override;
298     void drawLabel(QPainter& p, const QPolygonF& viewport, QList<QRectF>& blockedAreas, const QFontMetricsF& fm, CGisDraw* gis) override;
299     void drawHighlight(QPainter& p) override;
300     bool isCloseTo(const QPointF& pos) override;
301     bool isWithin(const QRectF& area, selflags_t flags) override;
302     void mouseMove(const QPointF& pos) override;
303     void mouseDragged(const QPoint& start, const QPoint& last, const QPoint& pos);
304     void dragFinished(const QPoint& pos);
305     void leftClicked(const QPoint& pos);
isGeocache()306     bool isGeocache()
307     {
308         return geocache.hasData;
309     }
310 
hasRadius()311     bool hasRadius()
312     {
313         return proximity < NOFLOAT;
314     }
315 
getRadius()316     qreal getRadius()
317     {
318         return radius;
319     }
320 
321     void gainUserFocus(bool yes) override;
322 
323     void edit() override;
324     void editInitial();
325 
326     /**
327        @brief Remove all links from the waypoint's link list with a given type
328 
329        This is used by devices that use links to attach multimedia items to a waypoint like images.
330        These links only make sense on the device. Therefor the links have to be removed after the
331        waypoint has been loaded from the device.
332 
333        @param type
334      */
335     void removeLinksByType(const QString& type);
336 
337     void toggleBubble();
hasBubble()338     bool hasBubble()
339     {
340         return bool(flags & eFlagWptBubble);
341     }
342 
setHideArea(bool hide)343     void setHideArea(bool hide)
344     {
345         hideArea = hide;
346     }
347 
348     void genKey() const override;
349     const searchValue_t getValueByKeyword(searchProperty_e keyword) override;
350 
351     static QString getLastName(const QString& name);
352     static void newWpt(const QPointF& pt, const QString& name, const QString& desc, IGisProject* project);
353     static void newWpt(const poi_t& poi, IGisProject* project, bool openEditWIndow = true);
354     static bool getIconAndName(QString& icon, QString& name);
355 
356     static void drawCircle(QPainter& p, const QPointF& pos, const qreal& r, const bool& avoid, const bool& selected);
357     static qreal calcRadius(const QPointF& posRad, const QPointF& posPx, const qreal& radiusRad, CGisDraw* gis);
358 
init()359     static void init()
360     {
361         keywordLambdaMap = initKeywordLambdaMap();
362         geocache_t::attributeMeaningsTranslated = geocache_t::initAttributeMeaningsTranslated();
363     }
364 
365 private:
366     void setIcon();
367     void setSymbol() override;
368     void readGpx(const QDomNode& xml);
369     void readTwoNav(const CTwoNavProject::wpt_t& tnvWpt);
370     void readWptFromFit(CFitStream& stream);
371     void readGcExt(const QDomNode& xmlCache);
372     void writeGcExt(QDomNode& xmlCache);
373     void drawBubble(QPainter& p);
374     QPolygonF makePolyline(const QPointF& anchor, const QRectF& r);
375     void processMouseOverBubble(const QPoint& pos);
376     void detBoundingRect();
377 
378     static key_t keyUserFocus;
379 
380     // --- start all waypoint data ----
381     wpt_t wpt;
382     qreal proximity = NOFLOAT;
383     qreal radius = NOFLOAT;
384     bool closeToRadius = false;
385     bool hideArea = false;
386     geocache_t geocache;
387     QList<image_t> images;
388 
389     QPointF focus;
390     QPointF posScreen = NOPOINTF;
391 
392     // additional data, common to all IGisItems, is found in IItem //
393 
394     // --- stop all waypoint data ----
395 
396     QPointer<CScrOptWpt> scrOptWpt;
397     QPointer<CScrOptWptRadius> scrOptRadius;
398 
399     bool doSpecialCursor = false;
400     bool doBubbleMove = false;
401     bool doBubbleSize = false;
402     bool mouseIsOverBubble = false;
403     QRect rectBubble;
404     QRect rectBubbleMove {0, 0, 16, 16};
405     QRect rectBubbleEdit {0, 0, 16, 16};
406     QRect rectBubbleSize {0, 0, 16, 16};
407 
408     QPoint offsetMouse;
409     QPoint offsetBubble {-320, -150};
410     quint32 widthBubble = 300;
411 
412     using fSearch = std::function<const searchValue_t (CGisItemWpt*)>;
413     static QMap<searchProperty_e, fSearch > keywordLambdaMap;
414     static QMap<searchProperty_e, fSearch > initKeywordLambdaMap();
415 };
416 
417 #endif // CGISITEMWPT_H
418 
419