1 /**********************************************************************************************
2     Copyright (C) 2014 Oliver Eichler <oliver.eichler@gmx.de>
3     Copyright (C) 2019 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 #include "gis/wpt/CDetailsGeoCache.h"
21 #include "gis/wpt/CGisItemWpt.h"
22 #include "helpers/CWebPage.h"
23 
24 #include <QtNetwork>
25 #include <QtWidgets>
26 
27 #define HTTP_ATTR_WHAT      QNetworkRequest::Attribute(QNetworkRequest::User + 1)
28 #define HTTP_ATTR_INFO      QNetworkRequest::Attribute(QNetworkRequest::User + 2)
29 
30 
CDetailsGeoCache(CGisItemWpt & wpt,QWidget * parent)31 CDetailsGeoCache::CDetailsGeoCache(CGisItemWpt& wpt, QWidget* parent)
32     : QDialog(parent)
33     , wpt(wpt)
34 {
35     setupUi(this);
36     setWindowTitle(wpt.getName());
37 
38     QString strPos;
39     QPointF pos = wpt.getPosition();
40     IUnit::degToStr(pos.x(), pos.y(), strPos);
41 
42     const CGisItemWpt::geocache_t& geocache = wpt.getGeoCache();
43 
44     QString status;
45     if(geocache.archived)
46     {
47         status = tr("Archived");
48     }
49     else if(geocache.available)
50     {
51         status = tr("Available");
52     }
53     else
54     {
55         status = tr("Not Available");
56     }
57 
58     if(geocache.needsMaintenance)
59     {
60         status += ", " + tr("Needs Maintenance");
61     }
62 
63     labelName->setText(geocache.name + " - " + status);
64     labelPositon->setText(strPos);
65     labelOwner->setText(geocache.owner);
66     labelSize->setText(geocache.container);
67     labelHiddenDate->setText(wpt.getTime().date().toString(Qt::SystemLocaleShortDate));
68     //Last found is set below to only loop logs once
69 
70     qreal d = geocache.difficulty;
71     labelD1->setPixmap(QPixmap(d < 0.5 ? "://icons/cache/32x32/star_empty.png" : d < 1.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16, 16, Qt::KeepAspectRatio, Qt::SmoothTransformation));
72     labelD2->setPixmap(QPixmap(d < 1.5 ? "://icons/cache/32x32/star_empty.png" : d < 2.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16, 16, Qt::KeepAspectRatio, Qt::SmoothTransformation));
73     labelD3->setPixmap(QPixmap(d < 2.5 ? "://icons/cache/32x32/star_empty.png" : d < 3.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16, 16, Qt::KeepAspectRatio, Qt::SmoothTransformation));
74     labelD4->setPixmap(QPixmap(d < 3.5 ? "://icons/cache/32x32/star_empty.png" : d < 4.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16, 16, Qt::KeepAspectRatio, Qt::SmoothTransformation));
75     labelD5->setPixmap(QPixmap(d < 4.5 ? "://icons/cache/32x32/star_empty.png" : d < 5.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16, 16, Qt::KeepAspectRatio, Qt::SmoothTransformation));
76     qreal t = geocache.terrain;
77     labelT1->setPixmap(QPixmap(t < 0.5 ? "://icons/cache/32x32/star_empty.png" : t < 1.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16, 16, Qt::KeepAspectRatio, Qt::SmoothTransformation));
78     labelT2->setPixmap(QPixmap(t < 1.5 ? "://icons/cache/32x32/star_empty.png" : t < 2.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16, 16, Qt::KeepAspectRatio, Qt::SmoothTransformation));
79     labelT3->setPixmap(QPixmap(t < 2.5 ? "://icons/cache/32x32/star_empty.png" : t < 3.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16, 16, Qt::KeepAspectRatio, Qt::SmoothTransformation));
80     labelT4->setPixmap(QPixmap(t < 3.5 ? "://icons/cache/32x32/star_empty.png" : t < 4.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16, 16, Qt::KeepAspectRatio, Qt::SmoothTransformation));
81     labelT5->setPixmap(QPixmap(t < 4.5 ? "://icons/cache/32x32/star_empty.png" : t < 5.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16, 16, Qt::KeepAspectRatio, Qt::SmoothTransformation));
82 
83     checkHint->setEnabled(!geocache.hint.isEmpty());
84     labelHint->setText(geocache.hint.isEmpty() ? tr("none") : tr("???"));
85     toolIcon->setIcon(wpt.getIcon());
86     labelStatus->hide();
87 
88     if(!geocache.attributes.isEmpty())
89     {
90         const QList<quint8>& keys = geocache.attributes.keys();
91         for(quint8 attribute : keys)
92         {
93             QString filepath = "://icons/geocaching/attributes/";
94             if(!geocache.attributes[attribute])
95             {
96                 filepath.append('_');
97             }
98             filepath += QString::number(attribute) + ".png";
99 
100             QLabel* attrLabel = new QLabel(this);//To avoid memory leak
101             attrLabel->setAlignment(Qt::AlignLeft);
102             attrLabel->setPixmap(QPixmap(filepath));
103             attributeHorLayout->insertWidget(0, attrLabel);
104         }
105 
106         labelNoAttr->setHidden(true);
107     }
108 
109     QString desc;
110     if(geocache.shortDescIsHtml)
111     {
112         desc += geocache.shortDesc;
113     }
114     else
115     {
116         QString str = geocache.shortDesc;
117         desc += "<p>" + str.replace("\n", "<br/>") + "</p>";
118     }
119 
120     if(geocache.longDescIsHtml)
121     {
122         desc += geocache.longDesc;
123     }
124     else
125     {
126         QString str = geocache.longDesc;
127         desc += "<p>" + str.replace("\n", "<br/>") + "</p>";
128     }
129 
130     CWebPage* webDescPage = new CWebPage(webDesc);
131     webDesc->setPage(webDescPage);
132     webDesc->setHtml(desc);
133 
134     const QDateTime& lastFound = geocache.getLastFound();
135     labelLastFound->setText(lastFound.date().toString(Qt::SystemLocaleShortDate));
136 
137     CWebPage* webLogPage = new CWebPage(webLogs);
138     webLogs->setPage(webLogPage);
139     webLogs->setHtml(geocache.getLogs());
140 
141     timerDownload = new QTimer(this);
142     timerDownload->setSingleShot(true);
143     connect(timerDownload, &QTimer::timeout, this, &CDetailsGeoCache::slotDownloadDone);
144     connect(buttonVisitWebsite, &QPushButton::clicked, this, &CDetailsGeoCache::slotVisitWebsite);
145     connect(checkHint, &QCheckBox::toggled, this, &CDetailsGeoCache::slotHintChanged);
146     connect(webDescPage, &CWebPage::linkClicked, this, &CDetailsGeoCache::slotLinkClicked);
147     connect(toolUpdateSpoiler, &QToolButton::clicked, this, &CDetailsGeoCache::slotCollectSpoiler);
148 
149     networkManager = new QNetworkAccessManager(this);
150     connect(networkManager, &QNetworkAccessManager::finished, this, &CDetailsGeoCache::slotRequestFinished);
151 
152     const QList<CGisItemWpt::image_t>& images = wpt.getImages();
153     photoAlbum->reload(images);
154     if(images.isEmpty())
155     {
156         slotCollectSpoiler();
157         toolUpdateSpoiler->setEnabled(false);
158     }
159     else
160     {
161         toolUpdateSpoiler->setEnabled(true);
162     }
163     if(wpt.isOnDevice())
164     {
165         toolUpdateSpoiler->setEnabled(false);
166     }
167 
168     listHistory->setEnabled(false);
169     listHistory->setupHistory(wpt);
170 }
171 
~CDetailsGeoCache()172 CDetailsGeoCache::~CDetailsGeoCache()
173 {
174 }
175 
slotVisitWebsite()176 void CDetailsGeoCache::slotVisitWebsite()
177 {
178     QDesktopServices::openUrl("https://www.coord.info/" + wpt.getName());
179 }
180 
slotHintChanged(bool on)181 void CDetailsGeoCache::slotHintChanged(bool on)
182 {
183     if(on)
184     {
185         labelHint->setText(wpt.getGeoCache().hint);
186     }
187     else
188     {
189         labelHint->setText(tr("???"));
190     }
191 }
192 
slotLinkClicked(const QUrl & url)193 void CDetailsGeoCache::slotLinkClicked(const QUrl& url)
194 {
195     QDesktopServices::openUrl(url);
196 }
197 
slotCollectSpoiler()198 void CDetailsGeoCache::slotCollectSpoiler()
199 {
200     const QList<IGisItem::link_t>& links = wpt.getLinks();
201     if(links.isEmpty())
202     {
203         return;
204     }
205 
206     wpt.loadHistory(0);
207     photoAlbum->reload(wpt.getImages());
208     listHistory->setupHistory(wpt);
209 
210     QNetworkRequest request;
211     request.setUrl(links.first().uri);
212     networkManager->get(request);
213 
214     timerDownload->start(10000);
215     labelStatus->show();
216     labelStatus->setText(tr("Searching for images..."));
217 }
218 
slotRequestFinished(QNetworkReply * reply)219 void CDetailsGeoCache::slotRequestFinished(QNetworkReply* reply)
220 {
221     if(reply->error() != QNetworkReply::NoError)
222     {
223         qDebug() << reply->errorString();
224         reply->deleteLater();
225         return;
226     }
227 
228     qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
229     qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute);
230     qDebug() << reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
231 
232     qint32 httpStatusCodeAttribute = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
233     if(reply->property("whatfor") == "image")
234     {
235         QString info = reply->property("info").toString();
236 
237         if((httpStatusCodeAttribute == 301) || (httpStatusCodeAttribute == 302))
238         {
239             QNetworkRequest request;
240             request.setUrl(reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl());
241             QNetworkReply* reply = networkManager->get(request);
242             reply->setProperty("whatfor", "image");
243             reply->setProperty("info", info);
244         }
245         else if(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200)
246         {
247             CGisItemWpt::image_t image;
248             image.info = info;
249             image.pixmap.loadFromData(reply->readAll());
250             wpt.addImage(image);
251 
252             photoAlbum->reload(wpt.getImages());
253             listHistory->setupHistory(wpt);
254 
255             cntSpoiler--;
256             if(cntSpoiler == 0)
257             {
258                 slotDownloadDone();
259             }
260         }
261     }
262     else
263     {
264         if((httpStatusCodeAttribute == 301) || (httpStatusCodeAttribute == 302))
265         {
266             QNetworkRequest request;
267             request.setUrl(reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl());
268             networkManager->get(request);
269         }
270     }
271 
272 
273     QString asw = reply->readAll();
274     reply->deleteLater();
275 
276     if(asw.isEmpty())
277     {
278         return;
279     }
280 
281     QRegExp re1(".*CachePageImages.*");
282     QRegExp re2("(https://.*\\.jpg).*>(.*)</a>");
283     re2.setMinimal(true);
284 
285     bool watchOut = false;
286     QStringList lines = asw.split("\n");
287     for(const QString& line : qAsConst(lines))
288     {
289         if(!watchOut && re1.exactMatch(line))
290         {
291             watchOut = true;
292         }
293         else if(watchOut)
294         {
295             int pos = 0;
296             while ((pos = re2.indexIn(line, pos)) != NOIDX)
297             {
298                 QString url = re2.cap(1);
299                 QString info = re2.cap(2);
300 
301                 QNetworkRequest request;
302                 request.setUrl(url);
303                 QNetworkReply* reply = networkManager->get(request);
304                 reply->setProperty("whatfor", "image");
305                 reply->setProperty("info", info);
306                 cntSpoiler++;
307 
308                 pos += re2.matchedLength();
309             }
310 
311             watchOut = false;
312         }
313     }
314 }
315 
slotDownloadDone()316 void CDetailsGeoCache::slotDownloadDone()
317 {
318     timerDownload->stop();
319     cntSpoiler = 0;
320     if(wpt.getImages().isEmpty())
321     {
322         labelStatus->setText(tr("No images found"));
323     }
324     else
325     {
326         labelStatus->hide();
327     }
328 }
329