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 #include "canvas/CCanvas.h"
22 #include "CMainWindow.h"
23 #include "gis/CGisDraw.h"
24 #include "gis/CGisListWks.h"
25 #include "gis/GeoMath.h"
26 #include "gis/prj/IGisProject.h"
27 #include "gis/Poi.h"
28 #include "gis/wpt/CDetailsGeoCache.h"
29 #include "gis/wpt/CDetailsWpt.h"
30 #include "gis/wpt/CGisItemWpt.h"
31 #include "gis/wpt/CScrOptWpt.h"
32 #include "gis/wpt/CScrOptWptRadius.h"
33 #include "gis/wpt/CSetupIconAndName.h"
34 #include "helpers/CDraw.h"
35 #include "helpers/CSettings.h"
36 #include "helpers/CWptIconManager.h"
37 #include "mouse/IMouse.h"
38 #include "units/IUnit.h"
39
40 #include <QPainterPath>
41 #include <QtWidgets>
42 #include <QtXml>
43 #include <QPainterPath>
44
45 IGisItem::key_t CGisItemWpt::keyUserFocus;
46 QMap<searchProperty_e, CGisItemWpt::fSearch> CGisItemWpt::keywordLambdaMap;
47 QList<QString> CGisItemWpt::geocache_t::attributeMeaningsTranslated;
48
49
CGisItemWpt(const QPointF & pos,qreal ele,const QDateTime & time,const QString & name,const QString & icon,IGisProject * project)50 CGisItemWpt::CGisItemWpt(const QPointF& pos, qreal ele, const QDateTime& time, const QString& name, const QString& icon, IGisProject* project)
51 : IGisItem(project, eTypeWpt, NOIDX)
52 {
53 wpt.name = name;
54 wpt.sym = icon;
55 wpt.lon = pos.x();
56 wpt.lat = pos.y();
57 wpt.ele = (ele == NOFLOAT) ? NOINT : qRound(ele);
58 wpt.time = time;
59
60 detBoundingRect();
61
62 setupHistory();
63 updateDecoration(eMarkNone, eMarkNone);
64 }
65
66 /// used to add a new waypoint
CGisItemWpt(const QPointF & pos,const QString & name,const QString & icon,IGisProject * project)67 CGisItemWpt::CGisItemWpt(const QPointF& pos, const QString& name, const QString& icon, IGisProject* project)
68 : CGisItemWpt(pos, NOFLOAT, QDateTime::currentDateTimeUtc(), name, icon, project)
69 {
70 flags = eFlagCreatedInQms | eFlagWriteAllowed;
71 qreal ele = CMainWindow::self().getElevationAt(pos * DEG_TO_RAD);
72 wpt.ele = (ele == NOFLOAT) ? NOINT : qRound(ele);
73
74 detBoundingRect();
75
76 setupHistory();
77 updateDecoration(eMarkChanged, eMarkNone);
78 }
79
80 /// used to move a copy of waypoint
CGisItemWpt(const QPointF & pos,const CGisItemWpt & parentWpt,IGisProject * project)81 CGisItemWpt::CGisItemWpt(const QPointF& pos, const CGisItemWpt& parentWpt, IGisProject* project)
82 : IGisItem(project, eTypeWpt, NOIDX)
83 {
84 *this = parentWpt;
85 wpt.lon = pos.x();
86 wpt.lat = pos.y();
87 wpt.time = QDateTime::currentDateTimeUtc();
88
89 key.clear();
90 history.events.clear();
91 flags = eFlagCreatedInQms | eFlagWriteAllowed;
92
93 qreal ele = CMainWindow::self().getElevationAt(pos * DEG_TO_RAD);
94 wpt.ele = (ele == NOFLOAT) ? NOINT : qRound(ele);
95
96 setNogoFlag(parentWpt.isNogo());
97
98 detBoundingRect();
99
100 setupHistory();
101 updateDecoration(eMarkChanged, eMarkNone);
102 }
103
104 /// used to create a copy of waypoint with new parent
CGisItemWpt(const CGisItemWpt & parentWpt,IGisProject * project,int idx,bool clone)105 CGisItemWpt::CGisItemWpt(const CGisItemWpt& parentWpt, IGisProject* project, int idx, bool clone)
106 : IGisItem(project, eTypeWpt, idx)
107 {
108 history = parentWpt.history;
109 loadHistory(history.histIdxCurrent);
110
111 if(clone)
112 {
113 wpt.name += tr("_Clone");
114 key.clear();
115 history.events.clear();
116 setupHistory();
117 }
118
119 if(parentWpt.isOnDevice() || !parentWpt.isReadOnly())
120 {
121 flags |= eFlagWriteAllowed;
122 }
123 else
124 {
125 flags &= ~eFlagWriteAllowed;
126 }
127
128 setNogoFlag(parentWpt.isNogo());
129
130 detBoundingRect();
131 updateDecoration(eMarkChanged, eMarkNone);
132 }
133
134 /// used to create waypoint from GPX file
CGisItemWpt(const QDomNode & xml,IGisProject * project)135 CGisItemWpt::CGisItemWpt(const QDomNode& xml, IGisProject* project)
136 : IGisItem(project, eTypeWpt, project->childCount())
137 {
138 readGpx(xml);
139 detBoundingRect();
140
141 CGisItemWpt::genKey();
142 setupHistory();
143 updateDecoration(eMarkNone, eMarkNone);
144 }
145
CGisItemWpt(const history_t & hist,const QString & dbHash,IGisProject * project)146 CGisItemWpt::CGisItemWpt(const history_t& hist, const QString& dbHash, IGisProject* project)
147 : IGisItem(project, eTypeWpt, project->childCount())
148 {
149 history = hist;
150 loadHistory(hist.histIdxCurrent);
151 detBoundingRect();
152 if(!dbHash.isEmpty())
153 {
154 lastDatabaseHash = dbHash;
155 }
156 }
157
CGisItemWpt(quint64 id,QSqlDatabase & db,IGisProject * project)158 CGisItemWpt::CGisItemWpt(quint64 id, QSqlDatabase& db, IGisProject* project)
159 : IGisItem(project, eTypeWpt, NOIDX)
160 {
161 loadFromDb(id, db);
162 detBoundingRect();
163 }
164
CGisItemWpt(const CTwoNavProject::wpt_t & tnvWpt,IGisProject * project)165 CGisItemWpt::CGisItemWpt(const CTwoNavProject::wpt_t& tnvWpt, IGisProject* project)
166 : IGisItem(project, eTypeWpt, NOIDX)
167 {
168 readTwoNav(tnvWpt);
169 detBoundingRect();
170
171 CGisItemWpt::genKey();
172 setupHistory();
173 updateDecoration(eMarkNone, eMarkNone);
174 }
175
CGisItemWpt(CFitStream & stream,IGisProject * project)176 CGisItemWpt::CGisItemWpt(CFitStream& stream, IGisProject* project)
177 : IGisItem(project, eTypeWpt, NOIDX)
178 , proximity(NOFLOAT)
179 , posScreen(NOPOINTF)
180 {
181 readWptFromFit(stream);
182 detBoundingRect();
183
184 CGisItemWpt::genKey();
185 setupHistory();
186 updateDecoration(eMarkNone, eMarkNone);
187 }
188
~CGisItemWpt()189 CGisItemWpt::~CGisItemWpt()
190 {
191 }
192
createClone()193 IGisItem* CGisItemWpt::createClone()
194 {
195 int idx = -1;
196 IGisProject* project = getParentProject();
197 if(project)
198 {
199 idx = project->indexOfChild(this);
200 }
201 return new CGisItemWpt(*this, project, idx, true);
202 }
203
204
setSymbol()205 void CGisItemWpt::setSymbol()
206 {
207 setIcon();
208 }
209
genKey() const210 void CGisItemWpt::genKey() const
211 {
212 if(geocache.hasData)
213 {
214 key.item = QString::number(geocache.id);
215 }
216 IGisItem::genKey();
217 }
218
getLastName(const QString & name)219 QString CGisItemWpt::getLastName(const QString& name)
220 {
221 SETTINGS;
222 QString lastName = name;
223
224 if(lastName.isEmpty())
225 {
226 lastName = cfg.value("Waypoint/lastName", "wpt").toString();
227 }
228
229 const int s = lastName.size();
230 if(s != 0)
231 {
232 int idx;
233 for(idx = s; idx > 0; idx--)
234 {
235 if(!lastName[idx - 1].isDigit())
236 {
237 break;
238 }
239 }
240
241 if(idx == 0)
242 {
243 lastName = QString::number(lastName.toInt() + 1);
244 }
245 else if(idx < s)
246 {
247 lastName = lastName.left(idx) + QString::number(lastName.midRef(idx).toInt() + 1);
248 }
249 }
250
251 cfg.setValue("Waypoint/lastName", lastName);
252 return lastName;
253 }
254
getIconAndName(QString & icon,QString & name)255 bool CGisItemWpt::getIconAndName(QString& icon, QString& name)
256 {
257 SETTINGS;
258 QString lastIcon = cfg.value("Waypoint/lastIcon", "Waypoint").toString();
259
260 if(name.isEmpty())
261 {
262 name = getLastName("");
263 }
264 icon = lastIcon;
265
266 CSetupIconAndName dlg(icon, name, CMainWindow::getBestWidgetForParent());
267 if(dlg.exec() != QDialog::Accepted)
268 {
269 return false;
270 }
271
272 cfg.setValue("Waypoint/lastName", name);
273 cfg.setValue("Waypoint/lastIcon", icon);
274
275 return true;
276 }
277
newWpt(const QPointF & pt,const QString & name,const QString & desc,IGisProject * project)278 void CGisItemWpt::newWpt(const QPointF& pt, const QString& name, const QString& desc, IGisProject* project)
279 {
280 SETTINGS;
281 const QString& _icon = cfg.value("Waypoint/lastIcon", "Waypoint").toString();
282 const QString& _name = name.isEmpty() ? getLastName("") : name;
283
284 CGisItemWpt* wpt = new CGisItemWpt(pt, _name, _icon, project);
285 if(!desc.isEmpty())
286 {
287 wpt->setDescription(desc);
288 }
289 wpt->editInitial();
290
291 cfg.setValue("Waypoint/lastName", wpt->getName());
292 cfg.setValue("Waypoint/lastIcon", wpt->getIconName());
293 }
294
newWpt(const poi_t & poi,IGisProject * project,bool openEditWIndow)295 void CGisItemWpt::newWpt(const poi_t& poi, IGisProject* project, bool openEditWIndow)
296 {
297 SETTINGS;
298 const QString& _icon = poi.icon.isEmpty() ? cfg.value("Waypoint/lastIcon", "Waypoint").toString() : poi.icon;
299 const QString& _name = poi.name.isEmpty() ? getLastName("") : poi.name;
300
301 CGisItemWpt* wpt = new CGisItemWpt(poi.pos * RAD_TO_DEG, _name, _icon, project);
302 if(!poi.desc.isEmpty())
303 {
304 wpt->setDescription(poi.desc);
305 }
306 if(!poi.links.isEmpty())
307 {
308 wpt->setLinks(poi.links);
309 }
310 if(poi.ele != NOINT)
311 {
312 wpt->setElevation(poi.ele);
313 }
314 if(openEditWIndow)
315 {
316 wpt->editInitial();
317 }
318
319 wpt->setReadOnlyMode(true);
320 cfg.setValue("Waypoint/lastName", wpt->getName());
321 cfg.setValue("Waypoint/lastIcon", wpt->getIconName());
322 }
323
getInfo(quint32 feature) const324 QString CGisItemWpt::getInfo(quint32 feature) const
325 {
326 QString str = "<div>";
327 qint32 initialSize = str.size();
328
329 if(feature & eFeatureShowName)
330 {
331 str = "<b>" + getName() + "</b>";
332 }
333
334 if(wpt.ele != NOINT)
335 {
336 if(str.size() > initialSize)
337 {
338 str += "<br/>\n";
339 }
340 QString val, unit;
341 IUnit::self().meter2elevation(wpt.ele, val, unit);
342 str += tr("Elevation: %1%2").arg(val, unit);
343 }
344
345 if(proximity != NOFLOAT)
346 {
347 if(str.size() > initialSize)
348 {
349 str += "<br/>\n";
350 }
351 QString val, unit;
352 IUnit::self().meter2distance(proximity, val, unit);
353 str += tr("Proximity: %1%2").arg(val, unit);
354 }
355
356 QString desc = removeHtml(wpt.desc).simplified();
357 if(geocache.hasData)
358 {
359 if(str.size() > initialSize)
360 {
361 str += "<br/>\n";
362 }
363
364 str += QString(" %4 (%1, D %2, T %3)")
365 .arg(geocache.container)
366 .arg(geocache.difficulty, 0, 'f', 1)
367 .arg(geocache.terrain, 0, 'f', 1)
368 .arg(geocache.name);
369
370 const QDateTime& lastFound = geocache.getLastFound();
371 if(lastFound.isValid())
372 {
373 str += "<br/>" + tr("Last found: %1")
374 .arg(IUnit::datetime2string(lastFound, false, wpt));
375 }
376
377 const IGisProject* project = getParentProject();
378 if(project != nullptr)
379 {
380 const QDateTime& projectDate = getParentProject()->getTime();
381 if(projectDate.isValid())
382 {
383 str += "<br/>" + tr("Project created: %1")
384 .arg(IUnit::datetime2string(projectDate, false, wpt));
385 }
386 }
387 }
388 else
389 {
390 if(desc.count())
391 {
392 if(str.size() > initialSize)
393 {
394 str += "<br/>\n";
395 }
396
397 if((feature & eFeatureShowFullText) || (desc.count() < 300))
398 {
399 str += desc;
400 }
401 else
402 {
403 str += desc.left(297) + "...";
404 }
405 }
406 }
407
408 QString cmt = removeHtml(wpt.cmt).simplified();
409 if((cmt != desc) && cmt.count())
410 {
411 if(str.size() > initialSize)
412 {
413 str += "<br/>\n";
414 }
415
416 if((feature & eFeatureShowFullText) || (cmt.count() < 300))
417 {
418 str += cmt;
419 }
420 else
421 {
422 str += cmt.left(297) + "...";
423 }
424 }
425 if(feature & eFeatureShowDateTime)
426 {
427 if(wpt.time.isValid())
428 {
429 if(str.size() > initialSize)
430 {
431 str += "<br/>\n";
432 }
433 str += tr("Created: %1").arg(IUnit::datetime2string(wpt.time, false, QPointF(wpt.lon * DEG_TO_RAD, wpt.lat * DEG_TO_RAD)));
434 }
435 }
436
437 if((feature & eFeatureShowLinks) && !wpt.links.isEmpty())
438 {
439 for(const link_t& link : wpt.links)
440 {
441 if(link.type.isEmpty() || (link.type == "text/html"))
442 {
443 str += "<br/>\n";
444 str += QString("<a href='%1'>%2</a>").arg(link.uri.toString(), link.text);
445 }
446 }
447 //Add logging link separately, since the link to the geocache site is extracted from the gpx file.
448 if(geocache.hasData && geocache.service == eGcCom)
449 {
450 str += " <a href='https://www.geocaching.com/play/geocache/" + wpt.name + "/log'>Log Geocache</a>";
451 }
452 }
453
454
455 str += getRatingKeywordInfo();
456
457 return str + "</div>";
458 }
459
getScreenOptions(const QPoint & origin,IMouse * mouse)460 IScrOpt* CGisItemWpt::getScreenOptions(const QPoint& origin, IMouse* mouse)
461 {
462 if (closeToRadius)
463 {
464 if(scrOptRadius.isNull())
465 {
466 scrOptRadius = new CScrOptWptRadius(this, origin, mouse);
467 }
468 return scrOptRadius;
469 }
470 else
471 {
472 if(scrOptWpt.isNull())
473 {
474 scrOptWpt = new CScrOptWpt(this, origin, mouse);
475 }
476 return scrOptWpt;
477 }
478 }
479
getPointCloseBy(const QPoint & point)480 QPointF CGisItemWpt::getPointCloseBy(const QPoint& point)
481 {
482 if (closeToRadius)
483 {
484 QPointF l = (QPointF(point) - posScreen);
485 return posScreen + l * (radius / sqrt(QPointF::dotProduct(l, l)));
486 }
487 else
488 {
489 return posScreen;
490 }
491 }
492
setIcon()493 void CGisItemWpt::setIcon()
494 {
495 if(geocache.hasData)
496 {
497 if(geocache.available)
498 {
499 IGisItem::setIcon(CWptIconManager::self().getWptIconByName(geocache.type, focus));
500 }
501 else
502 {
503 IGisItem::setIcon(CWptIconManager::self().getWptIconByName("gray_" + geocache.type, focus));
504 }
505 }
506 else
507 {
508 IGisItem::setIcon(CWptIconManager::self().getWptIconByName(wpt.sym, focus));
509 }
510 }
511
setName(const QString & str)512 void CGisItemWpt::setName(const QString& str)
513 {
514 SETTINGS;
515 cfg.setValue("Waypoint/lastName", str);
516
517 setText(CGisListWks::eColumnName, str);
518
519 wpt.name = str;
520 changed(tr("Changed name"), "://icons/48x48/EditText.png");
521 }
522
setPosition(const QPointF & pos)523 void CGisItemWpt::setPosition(const QPointF& pos)
524 {
525 wpt.lon = pos.x();
526 wpt.lat = pos.y();
527
528 detBoundingRect();
529
530 changed(tr("Changed position"), "://icons/48x48/WptMove.png");
531 }
532
setElevation(qint32 val)533 void CGisItemWpt::setElevation(qint32 val)
534 {
535 wpt.ele = val;
536 changed(tr("Changed elevation"), "://icons/48x48/SetEle.png");
537 }
538
setProximity(qreal val)539 void CGisItemWpt::setProximity(qreal val)
540 {
541 if (val == NOFLOAT)
542 {
543 proximity = NOFLOAT;
544 setNogoFlag(false);
545 changed(tr("Removed proximity"), "://icons/48x48/WptDelProx.png");
546 }
547 else
548 {
549 proximity = qRound(val);
550 changed(tr("Changed proximity"), "://icons/48x48/WptEditProx.png");
551 }
552
553 detBoundingRect();
554
555 radius = NOFLOAT; //radius is proximity in set on redraw
556 }
557
setIcon(const QString & name)558 void CGisItemWpt::setIcon(const QString& name)
559 {
560 SETTINGS;
561 cfg.setValue("Waypoint/lastIcon", name);
562
563 wpt.sym = name;
564
565 QPointF focus;
566 QString path;
567 CWptIconManager::self().getWptIconByName(name, focus, &path);
568
569 changed(tr("Changed icon"), path);
570 }
571
setComment(const QString & str)572 void CGisItemWpt::setComment(const QString& str)
573 {
574 wpt.cmt = str;
575 changed(tr("Changed comment"), "://icons/48x48/EditText.png");
576 }
577
setDescription(const QString & str)578 void CGisItemWpt::setDescription(const QString& str)
579 {
580 wpt.desc = str;
581 changed(tr("Changed description"), "://icons/48x48/EditText.png");
582 }
583
setLinks(const QList<link_t> & links)584 void CGisItemWpt::setLinks(const QList<link_t>& links)
585 {
586 wpt.links = links;
587 changed(tr("Changed links"), "://icons/48x48/Link.png");
588 }
589
setImages(const QList<image_t> & imgs)590 void CGisItemWpt::setImages(const QList<image_t>& imgs)
591 {
592 images = imgs;
593 changed(tr("Changed images"), "://icons/48x48/Image.png");
594 }
595
addImage(const image_t & img)596 void CGisItemWpt::addImage(const image_t& img)
597 {
598 images.append(img);
599 changed(tr("Add image"), "://icons/48x48/Image.png");
600 }
601
602
isCloseTo(const QPointF & pos)603 bool CGisItemWpt::isCloseTo(const QPointF& pos)
604 {
605 closeToRadius = false;
606
607 if(posScreen == NOPOINTF)
608 {
609 return false;
610 }
611
612 QPointF dist = (pos - posScreen);
613 if(dist.manhattanLength() < 22)
614 {
615 return true;
616 }
617 if (radius == NOFLOAT)
618 {
619 return false;
620 }
621
622 closeToRadius = abs(QPointF::dotProduct(dist, dist) / radius - radius) < 22;
623 return closeToRadius;
624 }
625
isWithin(const QRectF & area,selflags_t flags)626 bool CGisItemWpt::isWithin(const QRectF& area, selflags_t flags)
627 {
628 return (flags & eSelectionWpt) ? area.contains(QPointF(wpt.lon, wpt.lat)) : false;
629 }
630
631
gainUserFocus(bool yes)632 void CGisItemWpt::gainUserFocus(bool yes)
633 {
634 keyUserFocus = yes ? key : key_t();
635 }
636
edit()637 void CGisItemWpt::edit()
638 {
639 if(geocache.hasData)
640 {
641 CDetailsGeoCache dlg(*this, CMainWindow::getBestWidgetForParent());
642 dlg.exec();
643 }
644 else
645 {
646 CDetailsWpt dlg(*this, CMainWindow::getBestWidgetForParent());
647 dlg.exec();
648 }
649 }
650
editInitial()651 void CGisItemWpt::editInitial()
652 {
653 CDetailsWpt dlg(*this, CMainWindow::getBestWidgetForParent());
654 dlg.disableHistory();
655 dlg.exec();
656 squashHistory();
657 }
658
drawItem(QPainter & p,const QPolygonF & viewport,QList<QRectF> & blockedAreas,CGisDraw * gis)659 void CGisItemWpt::drawItem(QPainter& p, const QPolygonF& viewport, QList<QRectF>& blockedAreas, CGisDraw* gis)
660 {
661 posScreen = QPointF(wpt.lon * DEG_TO_RAD, wpt.lat * DEG_TO_RAD);
662
663 if (proximity == NOFLOAT || proximity == 0. ? !isVisible(posScreen, viewport, gis) : !isVisible(boundingRect, viewport, gis))
664 {
665 rectBubble = QRect();
666 posScreen = NOPOINTF;
667 return;
668 }
669
670 gis->convertRad2Px(posScreen);
671
672 if(proximity == NOFLOAT)
673 {
674 radius = NOFLOAT;
675 }
676 else
677 {
678 //remember radius for isCloseTo-method
679 radius = calcRadius(QPointF(wpt.lon * DEG_TO_RAD, wpt.lat * DEG_TO_RAD), posScreen, proximity, gis);
680
681 drawCircle(p, posScreen, radius, !hideArea && isNogo(), false);
682 }
683
684 drawBubble(p);
685
686 p.drawPixmap(posScreen - focus, icon);
687
688 blockedAreas << QRectF(posScreen - focus, icon.size());
689 }
690
drawItem(QPainter & p,const QRectF &,CGisDraw * gis)691 void CGisItemWpt::drawItem(QPainter& p, const QRectF& /*viewport*/, CGisDraw* gis)
692 {
693 if(mouseIsOverBubble && !doBubbleMove && !doBubbleSize && rectBubble.isValid() && !isReadOnly())
694 {
695 QPainterPath clip;
696 clip.addRoundedRect(rectBubble, RECT_RADIUS, RECT_RADIUS);
697 p.setClipPath(clip);
698
699 QRect barTop(rectBubble.topLeft(), QSize(rectBubble.width(), 26));
700 QRect barBottom(barTop);
701 barBottom.moveBottomLeft(rectBubble.bottomLeft());
702 barBottom.adjust(1, 0, -1, -1);
703 barTop.adjust(1, 1, -1, 0);
704
705 p.setPen(Qt::NoPen);
706 p.setBrush(QColor(200, 200, 255, 150));
707 p.drawRect(barTop);
708 p.drawRect(barBottom);
709
710 p.setBrush(Qt::white);
711 p.drawRoundedRect(rectBubbleMove.adjusted(-2, -2, 2, 2), RECT_RADIUS, RECT_RADIUS);
712 p.drawRoundedRect(rectBubbleEdit.adjusted(-2, -2, 2, 2), RECT_RADIUS, RECT_RADIUS);
713 p.drawRoundedRect(rectBubbleSize.adjusted(-2, -2, 2, 2), RECT_RADIUS, RECT_RADIUS);
714
715 p.drawPixmap(rectBubbleMove, QPixmap("://icons/32x32/MoveArrow.png"));
716 p.drawPixmap(rectBubbleEdit, QPixmap("://icons/32x32/EditDetails.png"));
717 p.drawPixmap(rectBubbleSize, QPixmap("://icons/32x32/SizeArrow.png"));
718 }
719 }
720
721
drawLabel(QPainter & p,const QPolygonF &,QList<QRectF> & blockedAreas,const QFontMetricsF & fm,CGisDraw *)722 void CGisItemWpt::drawLabel(QPainter& p, const QPolygonF& /*viewport*/, QList<QRectF>& blockedAreas, const QFontMetricsF& fm, CGisDraw*/*gis*/)
723 {
724 if(flags & eFlagWptBubble)
725 {
726 return;
727 }
728
729 if(posScreen == NOPOINTF)
730 {
731 return;
732 }
733
734 QPointF pt = posScreen - focus;
735
736 QRectF rect = fm.boundingRect(wpt.name);
737 rect.adjust(-2, -2, 2, 2);
738
739 // place label on top
740 rect.moveCenter(pt + QPointF(icon.width() / 2, -fm.height()));
741 if(CDraw::doesOverlap(blockedAreas, rect))
742 {
743 // place label on bottom
744 rect.moveCenter(pt + QPointF( icon.width() / 2, +fm.height() + icon.height()));
745 if(CDraw::doesOverlap(blockedAreas, rect))
746 {
747 // place label on right
748 rect.moveCenter(pt + QPointF( icon.width() + rect.width() / 2, +fm.height()));
749 if(CDraw::doesOverlap(blockedAreas, rect))
750 {
751 // place label on left
752 rect.moveCenter(pt + QPointF( -rect.width() / 2, +fm.height()));
753 if(CDraw::doesOverlap(blockedAreas, rect))
754 {
755 // failed to place label anywhere
756 return;
757 }
758 }
759 }
760 }
761
762 CDraw::text(wpt.name, p, rect.toRect(), Qt::darkBlue);
763 blockedAreas << rect;
764 }
765
drawHighlight(QPainter & p)766 void CGisItemWpt::drawHighlight(QPainter& p)
767 {
768 if(posScreen == NOPOINTF)
769 {
770 return;
771 }
772
773 if (closeToRadius)
774 {
775 drawCircle(p, posScreen, radius, false, true);
776 }
777 else
778 {
779 p.drawImage(posScreen - QPointF(31, 31), QImage("://cursors/wptHighlightRed.png"));
780 }
781 }
782
drawBubble(QPainter & p)783 void CGisItemWpt::drawBubble(QPainter& p)
784 {
785 if(!(flags & eFlagWptBubble))
786 {
787 return;
788 }
789
790 QString str = QString("<b>%1</b>").arg(getName());
791
792 if(!removeHtml(wpt.desc).simplified().isEmpty())
793 {
794 str += QString("<p>%1</p>").arg(wpt.desc);
795 }
796
797 if(!removeHtml(wpt.cmt).simplified().isEmpty())
798 {
799 str += QString("<p>%1</p>").arg(wpt.cmt);
800 }
801
802 QTextDocument doc;
803 doc.setHtml(str);
804 doc.setTextWidth(widthBubble);
805
806 rectBubble.setWidth(widthBubble);
807 rectBubble.setHeight(doc.size().height());
808
809 QPoint posBubble = posScreen.toPoint() + offsetBubble;
810 rectBubble.moveTopLeft(posBubble);
811
812 rectBubbleMove.moveTopLeft(rectBubble.topLeft() + QPoint(5, 5));
813 rectBubbleEdit.moveTopLeft(rectBubbleMove.topRight() + QPoint(7, 0));
814 rectBubbleSize.moveBottomRight(rectBubble.bottomRight() - QPoint(5, 5));
815
816 QPolygonF frame = makePolyline(posScreen, rectBubble);
817 p.setPen(CDraw::penBorderGray);
818 p.setBrush(CDraw::brushBackWhite);
819 p.drawPolygon(frame);
820
821 p.save();
822 p.translate(posBubble);
823 p.setPen(Qt::black);
824 doc.drawContents(&p);
825 p.restore();
826 }
827
drawCircle(QPainter & p,const QPointF & pos,const qreal & r,const bool & nogo,const bool & selected)828 void CGisItemWpt::drawCircle(QPainter& p, const QPointF& pos, const qreal& r, const bool& nogo, const bool& selected)
829 {
830 QRect circle(pos.x() - r - 1, pos.y() - r - 1, 2 * r + 1, 2 * r + 1);
831 p.save();
832 p.setBrush(Qt::NoBrush);
833 if (selected)
834 {
835 p.setPen(QPen(Qt::red, 3));
836 }
837 else
838 {
839 p.setPen(QPen(Qt::white, 3));
840 p.drawEllipse(circle);
841 p.setPen(QPen(Qt::red, 1));
842 }
843 p.drawEllipse(circle);
844 if (nogo)
845 {
846 p.setBrush(getNogoTextureBrush());
847 p.setPen(Qt::NoPen);
848 p.drawEllipse(circle);
849 }
850 p.restore();
851 }
852
calcRadius(const QPointF & posRad,const QPointF & posPx,const qreal & radiusRad,CGisDraw * gis)853 qreal CGisItemWpt::calcRadius(const QPointF& posRad, const QPointF& posPx, const qreal& radiusRad, CGisDraw* gis)
854 {
855 QPointF pt1 = posRad;
856 pt1 = GPS_Math_Wpt_Projection(pt1, radiusRad, 90 * DEG_TO_RAD);
857 gis->convertRad2Px(pt1);
858
859 return pt1.x() - posPx.x();
860 }
861
makePolyline(const QPointF & anchor,const QRectF & r)862 QPolygonF CGisItemWpt::makePolyline(const QPointF& anchor, const QRectF& r)
863 {
864 QPolygonF poly1, poly2;
865 poly1 << r.topLeft() << r.topRight() << r.bottomRight() << r.bottomLeft();
866
867 if(!r.contains(anchor))
868 {
869 qreal w = rectBubble.width() >> 1;
870 qreal h = rectBubble.height() >> 1;
871
872 if(w > 30)
873 {
874 w = 30;
875 }
876 if(h > 30)
877 {
878 h = 30;
879 }
880
881 w = h = qMin(w, h);
882
883 if(anchor.x() < r.left())
884 {
885 poly2 << anchor << (r.center() + QPoint(0, -h)) << (r.center() + QPoint(0, h)) << anchor;
886 }
887 else if(r.right() < anchor.x())
888 {
889 poly2 << anchor << (r.center() + QPoint(0, -h)) << (r.center() + QPoint(0, h)) << anchor;
890 }
891 else if(anchor.y() < r.top())
892 {
893 poly2 << anchor << (r.center() + QPoint(-w, 0)) << (r.center() + QPoint(w, 0)) << anchor;
894 }
895 else if(r.bottom() < anchor.y())
896 {
897 poly2 << anchor << (r.center() + QPoint(-w, 0)) << (r.center() + QPoint(w, 0)) << anchor;
898 }
899
900 QPainterPath path1;
901 path1.addRoundedRect(r, RECT_RADIUS, RECT_RADIUS);
902 QPainterPath path2;
903 path2.addPolygon(poly2);
904
905 path1 = path1.united(path2);
906
907 poly1 = path1.toFillPolygon();
908 }
909
910 return poly1;
911 }
912
913
removeLinksByType(const QString & type)914 void CGisItemWpt::removeLinksByType(const QString& type)
915 {
916 QList<IGisItem::link_t>::iterator link = wpt.links.begin();
917
918 while(link != wpt.links.end())
919 {
920 if(link->type == type)
921 {
922 link = wpt.links.erase(link);
923 continue;
924 }
925
926 ++link;
927 }
928 }
929
mouseMove(const QPointF & pos)930 void CGisItemWpt::mouseMove(const QPointF& pos)
931 {
932 if(!hasBubble() || isReadOnly())
933 {
934 return;
935 }
936 CCanvas* canvas = CMainWindow::self().getVisibleCanvas();
937 if(!canvas)
938 {
939 return;
940 }
941
942 if(mouseIsOverBubble)
943 {
944 processMouseOverBubble(pos.toPoint());
945 if(!rectBubble.contains(pos.toPoint()))
946 {
947 doBubbleMove = doBubbleSize = false;
948 canvas->resetMouse();
949 mouseIsOverBubble = false;
950 }
951 }
952 else
953 {
954 if(rectBubble.contains(pos.toPoint()))
955 {
956 doBubbleMove = doBubbleSize = false;
957 canvas->setMouseWptBubble(getKey());
958 mouseIsOverBubble = true;
959 }
960 }
961 }
962
mouseDragged(const QPoint &,const QPoint &,const QPoint & pos)963 void CGisItemWpt::mouseDragged(const QPoint& /*start*/, const QPoint& /*last*/, const QPoint& pos)
964 {
965 CCanvas* canvas = CMainWindow::self().getVisibleCanvas();
966 if(!canvas)
967 {
968 return;
969 }
970 if (!doBubbleMove && !doBubbleSize)
971 {
972 if(rectBubbleMove.contains(pos))
973 {
974 offsetMouse = pos - rectBubble.topLeft();
975 doBubbleMove = true;
976 }
977 else if(rectBubbleSize.contains(pos))
978 {
979 offsetMouse = pos - rectBubble.bottomRight();
980 doBubbleSize = true;
981 }
982 else
983 {
984 return;
985 }
986 }
987 if(doBubbleMove)
988 {
989 offsetBubble = pos - posScreen.toPoint();
990 offsetBubble -= offsetMouse;
991 }
992 else if(doBubbleSize)
993 {
994 qDebug() << offsetMouse;
995 int width = pos.x() - rectBubble.left() - offsetMouse.x();
996 if(width > 50)
997 {
998 widthBubble = width;
999 }
1000 }
1001 canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis);
1002 }
1003
dragFinished(const QPoint &)1004 void CGisItemWpt::dragFinished(const QPoint& /*pos*/)
1005 {
1006 updateHistory();
1007 doBubbleMove = doBubbleSize = false;
1008 }
1009
leftClicked(const QPoint & pos)1010 void CGisItemWpt::leftClicked(const QPoint& pos)
1011 {
1012 if(rectBubbleEdit.contains(pos))
1013 {
1014 CCanvas* canvas = CMainWindow::self().getVisibleCanvas();
1015 if(canvas)
1016 {
1017 doBubbleMove = doBubbleSize = false;
1018 canvas->resetMouse();
1019 }
1020 mouseIsOverBubble = false;
1021 edit();
1022 }
1023 }
1024
toggleBubble()1025 void CGisItemWpt::toggleBubble()
1026 {
1027 if(flags & eFlagWptBubble)
1028 {
1029 flags &= ~eFlagWptBubble;
1030 }
1031 else
1032 {
1033 flags |= eFlagWptBubble;
1034 }
1035 updateHistory();
1036 }
1037
getValueByKeyword(searchProperty_e keyword)1038 const searchValue_t CGisItemWpt::getValueByKeyword(searchProperty_e keyword)
1039 {
1040 if(keywordLambdaMap.contains(keyword))
1041 {
1042 return keywordLambdaMap.value(keyword)(this);
1043 }
1044 return searchValue_t();
1045 }
1046
processMouseOverBubble(const QPoint & pos)1047 void CGisItemWpt::processMouseOverBubble(const QPoint& pos)
1048 {
1049 if(rectBubbleMove.contains(pos) || rectBubbleEdit.contains(pos) || rectBubbleSize.contains(pos))
1050 {
1051 if(!doSpecialCursor)
1052 {
1053 CCanvas::setOverrideCursor(Qt::PointingHandCursor, "processMouseOverBubble");
1054 doSpecialCursor = true;
1055 }
1056 }
1057 else
1058 {
1059 if(doSpecialCursor)
1060 {
1061 CCanvas::restoreOverrideCursor("processMouseOverBubble");
1062 doSpecialCursor = false;
1063 }
1064 }
1065 }
1066
detBoundingRect()1067 void CGisItemWpt::detBoundingRect()
1068 {
1069 if(proximity == NOFLOAT)
1070 {
1071 boundingRect = QRectF(QPointF(wpt.lon, wpt.lat) * DEG_TO_RAD, QPointF(wpt.lon, wpt.lat) * DEG_TO_RAD);
1072 }
1073 else
1074 {
1075 qreal diag = proximity * 1.414213562;
1076 QPointF cent(wpt.lon* DEG_TO_RAD, wpt.lat* DEG_TO_RAD);
1077
1078 QPointF pt1 = GPS_Math_Wpt_Projection(cent, diag, 225 * DEG_TO_RAD);
1079 QPointF pt2 = GPS_Math_Wpt_Projection(cent, diag, 45 * DEG_TO_RAD);
1080
1081 boundingRect = QRectF(pt1, pt2);
1082 }
1083 }
1084
1085 const QList<QString> CGisItemWpt::geocache_t::attributeMeanings = {
1086 "QMS Attribute Flag", //Not to be serialized in GPX files
1087 "Dogs",
1088 "Access or parking fee",
1089 "Climbing gear",
1090 "Boat",
1091 "Scuba gear",
1092 "Recommended for kids",
1093 "Takes less than an hour",
1094 "Scenic view",
1095 "Significant hike",
1096 "Difficult climbing",
1097 "May require wading",
1098 "May require swimming",
1099 "Available at all times",
1100 "Recommended at night",
1101 "Available during winter",
1102 "",
1103 "Poison plants",
1104 "Dangerous Animals",
1105 "Ticks",
1106 "Abandoned mines",
1107 "Cliff / falling rocks",
1108 "Hunting",
1109 "Dangerous area",
1110 "Wheelchair accessible",
1111 "Parking available",
1112 "Public transportation",
1113 "Drinking water nearby",
1114 "Public restrooms nearby",
1115 "Telephone nearby",
1116 "Picnic tables nearby",
1117 "Camping available",
1118 "Bicycles",
1119 "Motorcycles",
1120 "Quads",
1121 "Off-road vehicles",
1122 "Snowmobiles",
1123 "Horses",
1124 "Campfires",
1125 "Thorns",
1126 "Stealth required",
1127 "Stroller accessible",
1128 "Needs maintenance",
1129 "Watch for livestock",
1130 "Flashlight required",
1131 "",
1132 "Truck Driver/RV",
1133 "Field Puzzle",
1134 "UV Light Required",
1135 "Snowshoes",
1136 "Cross Country Skis",
1137 "Special Tool Required",
1138 "Night Cache",
1139 "Park and Grab",
1140 "Abandoned Structure",
1141 "Short hike (less than 1km)",
1142 "Medium hike (1km-10km)",
1143 "Long Hike (+10km)",
1144 "Fuel Nearby",
1145 "Food Nearby",
1146 "Wireless Beacon",
1147 "Partnership cache",
1148 "Seasonal Access",
1149 "Tourist Friendly",
1150 "Tree Climbing",
1151 "Front Yard (Private Residence)",
1152 "Teamwork Required",
1153 "GeoTour"
1154 };
1155
initAttributeMeaningsTranslated()1156 QList<QString> CGisItemWpt::geocache_t::initAttributeMeaningsTranslated()
1157 {
1158 QList<QString> translated = {
1159 tr("QMS Attribute Flag"), //Not to be serialized in GPX files
1160 tr("Dogs"),
1161 tr("Access or parking fee"),
1162 tr("Climbing gear"),
1163 tr("Boat"),
1164 tr("Scuba gear"),
1165 tr("Recommended for kids"),
1166 tr("Takes less than an hour"),
1167 tr("Scenic view"),
1168 tr("Significant hike"),
1169 tr("Difficult climbing"),
1170 tr("May require wading"),
1171 tr("May require swimming"),
1172 tr("Available at all times"),
1173 tr("Recommended at night"),
1174 tr("Available during winter"),
1175 "",
1176 tr("Poison plants"),
1177 tr("Dangerous Animals"),
1178 tr("Ticks"),
1179 tr("Abandoned mines"),
1180 tr("Cliff / falling rocks"),
1181 tr("Hunting"),
1182 tr("Dangerous area"),
1183 tr("Wheelchair accessible"),
1184 tr("Parking available"),
1185 tr("Public transportation"),
1186 tr("Drinking water nearby"),
1187 tr("Public restrooms nearby"),
1188 tr("Telephone nearby"),
1189 tr("Picnic tables nearby"),
1190 tr("Camping available"),
1191 tr("Bicycles"),
1192 tr("Motorcycles"),
1193 tr("Quads"),
1194 tr("Off-road vehicles"),
1195 tr("Snowmobiles"),
1196 tr("Horses"),
1197 tr("Campfires"),
1198 tr("Thorns"),
1199 tr("Stealth required"),
1200 tr("Stroller accessible"),
1201 tr("Needs maintenance"),
1202 tr("Watch for livestock"),
1203 tr("Flashlight required"),
1204 "",
1205 tr("Truck Driver/RV"),
1206 tr("Field Puzzle"),
1207 tr("UV Light Required"),
1208 tr("Snowshoes"),
1209 tr("Cross Country Skis"),
1210 tr("Special Tool Required"),
1211 tr("Night Cache"),
1212 tr("Park and Grab"),
1213 tr("Abandoned Structure"),
1214 tr("Short hike (less than 1km)"),
1215 tr("Medium hike (1km-10km)"),
1216 tr("Long Hike (+10km)"),
1217 tr("Fuel Nearby"),
1218 tr("Food Nearby"),
1219 tr("Wireless Beacon"),
1220 tr("Partnership cache"),
1221 tr("Seasonal Access"),
1222 tr("Tourist Friendly"),
1223 tr("Tree Climbing"),
1224 tr("Front Yard (Private Residence)"),
1225 tr("Teamwork Required"),
1226 tr("GeoTour")
1227 };
1228 return translated;
1229 }
1230
getLastFound() const1231 QDateTime CGisItemWpt::geocache_t::getLastFound() const
1232 {
1233 QDateTime lastFound;
1234 for(const geocachelog_t& log : logs)
1235 {
1236 if(lastFound.isValid() == false || (log.type == "Found It" && log.date > lastFound))
1237 {
1238 lastFound = log.date;
1239 }
1240 }
1241 return lastFound;
1242 }
1243
getLogs() const1244 QString CGisItemWpt::geocache_t::getLogs() const
1245 {
1246 QString strLogs;
1247 for(const geocachelog_t& log : logs)
1248 {
1249 QString thislog = log.text;
1250 strLogs += "<p><b>"
1251 + log.date.date().toString(Qt::SystemLocaleShortDate)
1252 + ": "
1253 + log.type
1254 + tr(" by ")
1255 + log.finder
1256 + "</b></p><p>"
1257 + thislog.replace("\n", "<br/>")
1258 + "</p><hr>";
1259 }
1260 return strLogs;
1261 }
1262
initKeywordLambdaMap()1263 QMap<searchProperty_e, CGisItemWpt::fSearch> CGisItemWpt::initKeywordLambdaMap()
1264 {
1265 QMap<searchProperty_e, CGisItemWpt::fSearch> map;
1266 map.insert(eSearchPropertyGeneralName, [](CGisItemWpt* item){
1267 searchValue_t searchValue;
1268 if(item->geocache.hasData)
1269 {
1270 searchValue.str1 = item->geocache.name + " - " + item->getName();
1271 }
1272 else
1273 {
1274 searchValue.str1 = item->getName();
1275 }
1276 return searchValue;
1277 });
1278 map.insert(eSearchPropertyGeneralFullText, [](CGisItemWpt* item){
1279 searchValue_t searchValue;
1280 searchValue.str1 = item->getInfo(eFeatureShowFullText | eFeatureShowName);
1281 return searchValue;
1282 });
1283 map.insert(eSearchPropertyGeneralElevation, [](CGisItemWpt* item){
1284 searchValue_t searchValue;
1285 IUnit::self().meter2elevation(item->wpt.ele, searchValue.value1, searchValue.str1);
1286 return searchValue;
1287 });
1288 map.insert(eSearchPropertyGeneralDate, [](CGisItemWpt* item){
1289 searchValue_t searchValue;
1290 if(item->wpt.time.isValid())
1291 {
1292 searchValue.value1 = item->wpt.time.toSecsSinceEpoch();
1293 searchValue.str1 = "SsE"; //To differentiate Dates and Durations
1294 }
1295 return searchValue;
1296 });
1297 map.insert(eSearchPropertyGeneralComment, [](CGisItemWpt* item){
1298 searchValue_t searchValue;
1299 searchValue.str1 = item->getComment();
1300 return searchValue;
1301 });
1302 map.insert(eSearchPropertyGeneralDescription, [](CGisItemWpt* item){
1303 searchValue_t searchValue;
1304 searchValue.str1 = item->getDescription();
1305 return searchValue;
1306 });
1307 map.insert(eSearchPropertyGeneralRating, [](CGisItemWpt* item){
1308 searchValue_t searchValue;
1309 searchValue.value1 = item->getRating();
1310 return searchValue;
1311 });
1312 map.insert(eSearchPropertyGeneralKeywords, [](CGisItemWpt* item){
1313 searchValue_t searchValue;
1314 searchValue.str1 = QStringList(item->getKeywords().toList()).join(", ");
1315 return searchValue;
1316 });
1317 map.insert(eSearchPropertyGeneralType, [](CGisItemWpt* /*item*/){
1318 searchValue_t searchValue;
1319 searchValue.str1 = tr("waypoint");
1320 return searchValue;
1321 });
1322 //Geocache keywords
1323 map.insert(eSearchPropertyGeocacheDifficulty, [](CGisItemWpt* item){
1324 searchValue_t searchValue;
1325 searchValue.value1 = item->geocache.difficulty;
1326 return searchValue;
1327 });
1328 map.insert(eSearchPropertyGeocacheTerrain, [](CGisItemWpt* item){
1329 searchValue_t searchValue;
1330 searchValue.value1 = item->geocache.terrain;
1331 return searchValue;
1332 });
1333 map.insert(eSearchPropertyGeocachePositiveAttributes, [](CGisItemWpt* item){
1334 searchValue_t searchValue;
1335 const QList<quint8>& keys = item->geocache.attributes.keys();
1336 for(quint8 attr : keys)
1337 {
1338 if(attr >= item->geocache.attributeMeaningsTranslated.length())
1339 {
1340 continue;
1341 }
1342 if(!item->geocache.attributes[attr])// It is negated
1343 {
1344 continue;
1345 }
1346 searchValue.str1 += item->geocache.attributeMeaningsTranslated[attr] + ", ";
1347 }
1348 return searchValue;
1349 });
1350 map.insert(eSearchPropertyGeocacheNegatedAttributes, [](CGisItemWpt* item){
1351 searchValue_t searchValue;
1352 const QList<quint8>& keys = item->geocache.attributes.keys();
1353 for(quint8 attr : keys)
1354 {
1355 if(attr >= item->geocache.attributeMeaningsTranslated.length())
1356 {
1357 continue;
1358 }
1359 if(item->geocache.attributes[attr])// It is not negated
1360 {
1361 continue;
1362 }
1363 searchValue.str1 += item->geocache.attributeMeaningsTranslated[attr] + ", ";
1364 }
1365 return searchValue;
1366 });
1367 map.insert(eSearchPropertyGeocacheSize, [](CGisItemWpt* item){
1368 searchValue_t searchValue;
1369 searchValue.str1 = item->geocache.container;
1370 return searchValue;
1371 });
1372 map.insert(eSearchPropertyGeocacheGCCode, [](CGisItemWpt* item){
1373 searchValue_t searchValue;
1374 searchValue.str1 = item->getName();
1375 return searchValue;
1376 });
1377 map.insert(eSearchPropertyGeocacheGCName, [](CGisItemWpt* item){
1378 searchValue_t searchValue;
1379 searchValue.str1 = item->geocache.name;
1380 return searchValue;
1381 });
1382 map.insert(eSearchPropertyGeocacheStatus, [](CGisItemWpt* item){
1383 searchValue_t searchValue;
1384 if(!item->geocache.hasData)
1385 {
1386 return searchValue;
1387 }
1388
1389 if(item->geocache.archived)
1390 {
1391 searchValue.str1 = tr("archived");
1392 }
1393 else if(item->geocache.available)
1394 {
1395 searchValue.str1 = tr("available");
1396 }
1397 else
1398 {
1399 searchValue.str1 = tr("not available");
1400 }
1401
1402 return searchValue;
1403 });
1404 map.insert(eSearchPropertyGeocacheGCType, [](CGisItemWpt* item){
1405 searchValue_t searchValue;
1406 searchValue.str1 = item->geocache.type;
1407 return searchValue;
1408 });
1409 map.insert(eSearchPropertyGeocacheLoggedBy, [](CGisItemWpt* item){
1410 searchValue_t searchValue;
1411 for(const geocachelog_t& log : qAsConst(item->geocache.logs))
1412 {
1413 searchValue.str1 += log.finder + ", ";
1414 }
1415 return searchValue;
1416 });
1417 map.insert(eSearchPropertyGeocacheLastLogDate, [](CGisItemWpt* item){
1418 searchValue_t searchValue;
1419 if(item->geocache.logs.size() > 0)
1420 {
1421 searchValue.value1 = item->geocache.logs[0].date.toSecsSinceEpoch();
1422 searchValue.str1 = "SsE"; //To differentiate Dates and Durations
1423 }
1424 return searchValue;
1425 });
1426 map.insert(eSearchPropertyGeocacheLastLogType, [](CGisItemWpt* item){
1427 searchValue_t searchValue;
1428 if(item->geocache.logs.size() > 0)
1429 {
1430 searchValue.str1 = item->geocache.logs[0].type;
1431 }
1432 return searchValue;
1433 });
1434 map.insert(eSearchPropertyGeocacheLastLogBy, [](CGisItemWpt* item){
1435 searchValue_t searchValue;
1436 if(item->geocache.logs.size() > 0)
1437 {
1438 searchValue.str1 = item->geocache.logs[0].finder;
1439 }
1440 return searchValue;
1441 });
1442 map.insert(eSearchPropertyGeocacheGCOwner, [](CGisItemWpt* item){
1443 searchValue_t searchValue;
1444 searchValue.str1 = item->geocache.owner;
1445 return searchValue;
1446 });
1447 return map;
1448 }
1449
1450