1 /*
2     SPDX-FileCopyrightText: 2001 Jason Harris <jharris@30doradus.org>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "locationdialog.h"
8 
9 #include "kspaths.h"
10 #include "kstarsdata.h"
11 #include "Options.h"
12 #include "ksnotification.h"
13 #include "kstars_debug.h"
14 #include "ksutils.h"
15 
16 #include <QSqlQuery>
17 
18 #ifdef HAVE_GEOCLUE2
19 #include <QGeoPositionInfoSource>
20 #endif
21 #include <QJsonArray>
22 #include <QJsonDocument>
23 #include <QJsonObject>
24 #include <QJsonValue>
25 #include <QNetworkAccessManager>
26 #include <QNetworkConfigurationManager>
27 #include <QNetworkReply>
28 #include <QNetworkSession>
29 #include <QQmlContext>
30 #include <QUrlQuery>
31 #include <QPlainTextEdit>
32 
33 
LocationDialogUI(QWidget * parent)34 LocationDialogUI::LocationDialogUI(QWidget *parent) : QFrame(parent)
35 {
36     setupUi(this);
37 }
38 
LocationDialog(QWidget * parent)39 LocationDialog::LocationDialog(QWidget *parent) : QDialog(parent), timer(nullptr)
40 {
41 #ifdef Q_OS_OSX
42     setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
43 #endif
44     KStarsData *data = KStarsData::Instance();
45 
46     SelectedCity = nullptr;
47     ld           = new LocationDialogUI(this);
48 
49     QVBoxLayout *mainLayout = new QVBoxLayout;
50     mainLayout->addWidget(ld);
51     setLayout(mainLayout);
52 
53     ld->MapView->setLocationDialog(this);
54 
55     setWindowTitle(i18nc("@title:window", "Set Geographic Location"));
56 
57     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
58     mainLayout->addWidget(buttonBox);
59     connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotOk()));
60     connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
61 
62     for (int i = 0; i < 25; ++i)
63         ld->TZBox->addItem(QLocale().toString(static_cast<double>(i - 12)));
64 
65     //Populate DSTRuleBox
66     foreach (const QString &key, data->getRulebook().keys())
67     {
68         if (!key.isEmpty())
69             ld->DSTRuleBox->addItem(key);
70     }
71 
72     connect(ld->CityFilter, SIGNAL(textChanged(QString)), this, SLOT(enqueueFilterCity()));
73     connect(ld->ProvinceFilter, SIGNAL(textChanged(QString)), this, SLOT(enqueueFilterCity()));
74     connect(ld->CountryFilter, SIGNAL(textChanged(QString)), this, SLOT(enqueueFilterCity()));
75     connect(ld->NewCityName, SIGNAL(textChanged(QString)), this, SLOT(nameChanged()));
76     connect(ld->NewProvinceName, SIGNAL(textChanged(QString)), this, SLOT(nameChanged()));
77     connect(ld->NewCountryName, SIGNAL(textChanged(QString)), this, SLOT(nameChanged()));
78     connect(ld->NewLong, SIGNAL(textChanged(QString)), this, SLOT(dataChanged()));
79     connect(ld->NewLat, SIGNAL(textChanged(QString)), this, SLOT(dataChanged()));
80     connect(ld->NewElev, SIGNAL(valueChanged(double)), this, SLOT(dataChanged()));
81 
82     connect(ld->TZBox, SIGNAL(activated(int)), this, SLOT(dataChanged()));
83     connect(ld->DSTRuleBox, SIGNAL(activated(int)), this, SLOT(dataChanged()));
84     connect(ld->GeoBox, SIGNAL(itemSelectionChanged()), this, SLOT(changeCity()));
85     connect(ld->AddCityButton, SIGNAL(clicked()), this, SLOT(addCity()));
86     connect(ld->ClearFieldsButton, SIGNAL(clicked()), this, SLOT(clearFields()));
87     connect(ld->RemoveButton, SIGNAL(clicked()), this, SLOT(removeCity()));
88     connect(ld->UpdateButton, SIGNAL(clicked()), this, SLOT(updateCity()));
89 
90     // FIXME Disable this until Qt5 works with Geoclue2
91 #ifdef HAVE_GEOCLUE_2
92     source = QGeoPositionInfoSource::createDefaultSource(this);
93     source->setPreferredPositioningMethods(QGeoPositionInfoSource::SatellitePositioningMethods);
94     qDebug() << "Last known position" << source->lastKnownPosition().coordinate();
95 
96     connect(source, SIGNAL(positionUpdated(QGeoPositionInfo)), this, SLOT(positionUpdated(QGeoPositionInfo)));
97     connect(source, SIGNAL(error(QGeoPositionInfoSource::Error)), this, SLOT(positionUpdateError(QGeoPositionInfoSource::Error)));
98     connect(source, SIGNAL(updateTimeout()), this, SLOT(positionUpdateTimeout()));
99 
100     connect(ld->GetLocationButton, SIGNAL(clicked()), this, SLOT(requestUpdate()));
101 #endif
102 
103     ld->DSTLabel->setText("<a href=\"showrules\">" + i18n("DST rule:") + "</a>");
104     connect(ld->DSTLabel, SIGNAL(linkActivated(QString)), this, SLOT(showTZRules()));
105 
106     dataModified = false;
107     nameModified = false;
108     ld->AddCityButton->setEnabled(false);
109 
110     ld->errorLabel->setText(QString());
111 
112     // FIXME Disable this until Qt5 works with Geoclue2
113 #ifdef HAVE_GEOCLUE_2
114     nam = new QNetworkAccessManager(this);
115     connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(processLocationNameData(QNetworkReply*)));
116 #endif
117 
118     initCityList();
119     resize(640, 480);
120 }
121 
initCityList()122 void LocationDialog::initCityList()
123 {
124     KStarsData *data = KStarsData::Instance();
125     foreach (GeoLocation *loc, data->getGeoList())
126     {
127         ld->GeoBox->addItem(loc->fullName());
128         filteredCityList.append(loc);
129 
130         //If TZ is not an even integer value, add it to listbox
131         if (loc->TZ0() - int(loc->TZ0()) && ld->TZBox->findText(QLocale().toString(loc->TZ0())) != -1)
132         {
133             for (int i = 0; i < ld->TZBox->count(); ++i)
134             {
135                 if (ld->TZBox->itemText(i).toDouble() > loc->TZ0())
136                 {
137                     ld->TZBox->addItem(QLocale().toString(loc->TZ0()), i - 1);
138                     break;
139                 }
140             }
141         }
142     }
143 
144     //Sort the list of Cities alphabetically...note that filteredCityList may now have a different ordering!
145     ld->GeoBox->sortItems();
146 
147     ld->CountLabel->setText(
148         i18np("One city matches search criteria", "%1 cities match search criteria", ld->GeoBox->count()));
149 
150     // attempt to highlight the current kstars location in the GeoBox
151     ld->GeoBox->setCurrentItem(nullptr);
152     for (int i = 0; i < ld->GeoBox->count(); i++)
153     {
154         if (ld->GeoBox->item(i)->text() == data->geo()->fullName())
155         {
156             ld->GeoBox->setCurrentRow(i);
157             break;
158         }
159     }
160 }
161 
enqueueFilterCity()162 void LocationDialog::enqueueFilterCity()
163 {
164     if (timer)
165         timer->stop();
166     else
167     {
168         timer = new QTimer(this);
169         timer->setSingleShot(true);
170         connect(timer, SIGNAL(timeout()), this, SLOT(filterCity()));
171     }
172     timer->start(500);
173 }
174 
filterCity()175 void LocationDialog::filterCity()
176 {
177     KStarsData *data = KStarsData::Instance();
178     ld->GeoBox->clear();
179     //Do NOT delete members of filteredCityList!
180     while (!filteredCityList.isEmpty())
181         filteredCityList.takeFirst();
182 
183     nameModified = false;
184     dataModified = false;
185     ld->AddCityButton->setEnabled(false);
186     ld->UpdateButton->setEnabled(false);
187 
188     foreach (GeoLocation *loc, data->getGeoList())
189     {
190         QString sc(loc->translatedName());
191         QString ss(loc->translatedCountry());
192         QString sp = "";
193         if (!loc->province().isEmpty())
194             sp = loc->translatedProvince();
195 
196         if (sc.startsWith(ld->CityFilter->text(), Qt::CaseInsensitive) &&
197                 sp.startsWith(ld->ProvinceFilter->text(), Qt::CaseInsensitive) &&
198                 ss.startsWith(ld->CountryFilter->text(), Qt::CaseInsensitive))
199         {
200             ld->GeoBox->addItem(loc->fullName());
201             filteredCityList.append(loc);
202         }
203     }
204 
205     ld->GeoBox->sortItems();
206 
207     ld->CountLabel->setText(
208         i18np("One city matches search criteria", "%1 cities match search criteria", ld->GeoBox->count()));
209 
210     if (ld->GeoBox->count() > 0) // set first item in list as selected
211         ld->GeoBox->setCurrentItem(ld->GeoBox->item(0));
212 
213     ld->MapView->repaint();
214 }
215 
changeCity()216 void LocationDialog::changeCity()
217 {
218     KStarsData *data = KStarsData::Instance();
219 
220     //when the selected city changes, set newCity, and redraw map
221     SelectedCity = nullptr;
222     if (ld->GeoBox->currentItem())
223     {
224         for (auto &loc : filteredCityList)
225         {
226             if (loc->fullName() == ld->GeoBox->currentItem()->text())
227             {
228                 SelectedCity = loc;
229                 break;
230             }
231         }
232     }
233 
234     ld->MapView->repaint();
235 
236     //Fill the fields at the bottom of the window with the selected city's data.
237     if (SelectedCity)
238     {
239         ld->NewCityName->setText(SelectedCity->translatedName());
240         if (SelectedCity->province().isEmpty())
241             ld->NewProvinceName->setText(QString());
242         else
243             ld->NewProvinceName->setText(SelectedCity->translatedProvince());
244 
245         ld->NewCountryName->setText(SelectedCity->translatedCountry());
246         ld->NewLong->showInDegrees(SelectedCity->lng());
247         ld->NewLat->showInDegrees(SelectedCity->lat());
248         ld->TZBox->setEditText(QLocale().toString(SelectedCity->TZ0()));
249         ld->NewElev->setValue(SelectedCity->elevation());
250 
251         //Pick the City's rule from the rulebook
252         for (int i = 0; i < ld->DSTRuleBox->count(); ++i)
253         {
254             TimeZoneRule tzr = data->getRulebook().value(ld->DSTRuleBox->itemText(i));
255             if (tzr.equals(SelectedCity->tzrule()))
256             {
257                 ld->DSTRuleBox->setCurrentIndex(i);
258                 break;
259             }
260         }
261 
262         ld->RemoveButton->setEnabled(SelectedCity->isReadOnly() == false);
263     }
264 
265     nameModified = false;
266     dataModified = false;
267     ld->AddCityButton->setEnabled(false);
268     ld->UpdateButton->setEnabled(false);
269 }
270 
addCity()271 bool LocationDialog::addCity()
272 {
273     return updateCity(CITY_ADD);
274 }
275 
updateCity()276 bool LocationDialog::updateCity()
277 {
278     if (SelectedCity == nullptr)
279         return false;
280 
281     return updateCity(CITY_UPDATE);
282 }
283 
removeCity()284 bool LocationDialog::removeCity()
285 {
286     if (SelectedCity == nullptr)
287         return false;
288 
289     return updateCity(CITY_REMOVE);
290 }
291 
updateCity(CityOperation operation)292 bool LocationDialog::updateCity(CityOperation operation)
293 {
294     if (operation == CITY_REMOVE)
295     {
296         QString message = i18n("Are you sure you want to remove %1?", selectedCityName());
297         if (KMessageBox::questionYesNo(nullptr, message, i18n("Remove City?")) == KMessageBox::No)
298             return false; //user answered No.
299     }
300     else if (!nameModified && !dataModified)
301     {
302         QString message = i18n("This city already exists in the database.");
303         KSNotification::sorry(message, i18n("Error: Duplicate Entry"));
304         return false;
305     }
306 
307     bool latOk(false), lngOk(false), tzOk(false);
308     dms lat                = ld->NewLat->createDms(true, &latOk);
309     dms lng                = ld->NewLong->createDms(true, &lngOk);
310     QString TimeZoneString = ld->TZBox->lineEdit()->text();
311     TimeZoneString.replace(QLocale().decimalPoint(), ".");
312     double TZ = TimeZoneString.toDouble(&tzOk);
313     double height          = ld->NewElev->value();
314 
315     if (ld->NewCityName->text().isEmpty() || ld->NewCountryName->text().isEmpty())
316     {
317         QString message = i18n("All fields (except province) must be filled to add this location.");
318         KSNotification::sorry(message, i18n("Fields are Empty"));
319         return false;
320     }
321     else if (!latOk || !lngOk)
322     {
323         QString message = i18n("Could not parse the Latitude/Longitude.");
324         KSNotification::sorry(message, i18n("Bad Coordinates"));
325         return false;
326     }
327     else if (!tzOk)
328     {
329         QString message = i18n("UTC Offset must be selected.");
330         KSNotification::sorry(message, i18n("UTC Offset"));
331         return false;
332     }
333 
334     // If name is still the same then it's an update operation
335     if (operation == CITY_ADD && !nameModified)
336         operation = CITY_UPDATE;
337 
338     /*if ( !nameModified )
339     {
340         QString message = i18n( "Really override original data for this city?" );
341         if ( KMessageBox::questionYesNo( 0, message, i18n( "Override Existing Data?" ), KGuiItem(i18n("Override Data")), KGuiItem(i18n("Do Not Override"))) == KMessageBox::No )
342             return false; //user answered No.
343     }*/
344 
345     QSqlDatabase mycitydb = QSqlDatabase::database("mycitydb");
346     QString dbfile        = QDir(KSPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("mycitydb.sqlite");
347 
348     // If it doesn't exist, create it
349     if (QFile::exists(dbfile) == false)
350     {
351         mycitydb.setDatabaseName(dbfile);
352         mycitydb.open();
353         QSqlQuery create_query(mycitydb);
354         QString query("CREATE TABLE city ( "
355                       "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
356                       "Name TEXT DEFAULT NULL, "
357                       "Province TEXT DEFAULT NULL, "
358                       "Country TEXT DEFAULT NULL, "
359                       "Latitude TEXT DEFAULT NULL, "
360                       "Longitude TEXT DEFAULT NULL, "
361                       "TZ REAL DEFAULT NULL, "
362                       "TZRule TEXT DEFAULT NULL,"
363                       "Elevation REAL NOT NULL DEFAULT -10 )");
364 
365         if (create_query.exec(query) == false)
366         {
367             qCWarning(KSTARS) << create_query.lastError();
368             return false;
369         }
370     }
371     else if (mycitydb.open() == false)
372     {
373         qCWarning(KSTARS) << mycitydb.lastError();
374         return false;
375     }
376 
377     //Strip off white space
378     QString name     = ld->NewCityName->text().trimmed();
379     QString province = ld->NewProvinceName->text().trimmed();
380     QString country  = ld->NewCountryName->text().trimmed();
381     QString TZrule   = ld->DSTRuleBox->currentText();
382     double Elevation = ld->NewElev->value();
383     GeoLocation *g   = nullptr;
384 
385     switch (operation)
386     {
387         case CITY_ADD:
388         {
389             QSqlQuery add_query(mycitydb);
390             add_query.prepare("INSERT INTO city(Name, Province, Country, Latitude, Longitude, TZ, TZRule, Elevation) "
391                               "VALUES(:Name, :Province, :Country, :Latitude, :Longitude, :TZ, :TZRule, :Elevation)");
392             add_query.bindValue(":Name", name);
393             add_query.bindValue(":Province", province);
394             add_query.bindValue(":Country", country);
395             add_query.bindValue(":Latitude", lat.toDMSString());
396             add_query.bindValue(":Longitude", lng.toDMSString());
397             add_query.bindValue(":TZ", TZ);
398             add_query.bindValue(":TZRule", TZrule);
399             add_query.bindValue(":Elevation", Elevation);
400             if (add_query.exec() == false)
401             {
402                 qCWarning(KSTARS) << add_query.lastError();
403                 return false;
404             }
405 
406             //Add city to geoList...don't need to insert it alphabetically, since we always sort GeoList
407             g = new GeoLocation(lng, lat, name, province, country, TZ, &KStarsData::Instance()->Rulebook[TZrule], Elevation);
408             KStarsData::Instance()->getGeoList().append(g);
409         }
410         break;
411 
412         case CITY_UPDATE:
413         {
414             g = SelectedCity;
415 
416             QSqlQuery update_query(mycitydb);
417             update_query.prepare("UPDATE city SET Name = :newName, Province = :newProvince, Country = :newCountry, "
418                                  "Latitude = :Latitude, Longitude = :Longitude, TZ = :TZ, TZRule = :TZRule, Elevation = :Elevation WHERE "
419                                  "Name = :Name AND Province = :Province AND Country = :Country");
420             update_query.bindValue(":newName", name);
421             update_query.bindValue(":newProvince", province);
422             update_query.bindValue(":newCountry", country);
423             update_query.bindValue(":Name", SelectedCity->name());
424             update_query.bindValue(":Province", SelectedCity->province());
425             update_query.bindValue(":Country", SelectedCity->country());
426             update_query.bindValue(":Latitude", lat.toDMSString());
427             update_query.bindValue(":Longitude", lng.toDMSString());
428             update_query.bindValue(":TZ", TZ);
429             update_query.bindValue(":TZRule", TZrule);
430             update_query.bindValue(":Elevation", Elevation);
431             if (update_query.exec() == false)
432             {
433                 qCWarning(KSTARS) << update_query.lastError();
434                 return false;
435             }
436 
437             g->setName(name);
438             g->setProvince(province);
439             g->setCountry(country);
440             g->setLat(lat);
441             g->setLong(lng);
442             g->setTZ0(TZ);
443             g->setTZRule(&KStarsData::Instance()->Rulebook[TZrule]);
444             g->setElevation(height);
445 
446         }
447         break;
448 
449         case CITY_REMOVE:
450         {
451             g = SelectedCity;
452             QSqlQuery delete_query(mycitydb);
453             delete_query.prepare("DELETE FROM city WHERE Name = :Name AND Province = :Province AND Country = :Country");
454             delete_query.bindValue(":Name", name);
455             delete_query.bindValue(":Province", province);
456             delete_query.bindValue(":Country", country);
457             if (delete_query.exec() == false)
458             {
459                 qCWarning(KSTARS) << delete_query.lastError();
460                 return false;
461             }
462 
463             filteredCityList.removeOne(g);
464             KStarsData::Instance()->getGeoList().removeOne(g);
465             delete g;
466             g = nullptr;
467         }
468         break;
469     }
470 
471     //(possibly) insert new city into GeoBox by running filterCity()
472     filterCity();
473 
474     //Attempt to highlight new city in list
475     ld->GeoBox->setCurrentItem(nullptr);
476     if (g && ld->GeoBox->count())
477     {
478         for (int i = 0; i < ld->GeoBox->count(); i++)
479         {
480             if (ld->GeoBox->item(i)->text() == g->fullName())
481             {
482                 ld->GeoBox->setCurrentRow(i);
483                 break;
484             }
485         }
486     }
487 
488     mycitydb.commit();
489     mycitydb.close();
490 
491     return true;
492 }
493 
findCitiesNear(int lng,int lat)494 void LocationDialog::findCitiesNear(int lng, int lat)
495 {
496     KStarsData *data = KStarsData::Instance();
497     //find all cities within 3 degrees of (lng, lat); list them in GeoBox
498     ld->GeoBox->clear();
499     //Remember, do NOT delete members of filteredCityList
500     while (!filteredCityList.isEmpty())
501         filteredCityList.takeFirst();
502 
503     foreach (GeoLocation *loc, data->getGeoList())
504     {
505         if ((abs(lng - int(loc->lng()->Degrees())) < 3) && (abs(lat - int(loc->lat()->Degrees())) < 3))
506         {
507             ld->GeoBox->addItem(loc->fullName());
508             filteredCityList.append(loc);
509         }
510     }
511 
512     ld->GeoBox->sortItems();
513     ld->CountLabel->setText(
514         i18np("One city matches search criteria", "%1 cities match search criteria", ld->GeoBox->count()));
515 
516     if (ld->GeoBox->count() > 0) // set first item in list as selected
517         ld->GeoBox->setCurrentItem(ld->GeoBox->item(0));
518 
519     repaint();
520 }
521 
checkLongLat()522 bool LocationDialog::checkLongLat()
523 {
524     if (ld->NewLong->text().isEmpty() || ld->NewLat->text().isEmpty())
525         return false;
526 
527     bool ok;
528     double lng = ld->NewLong->createDms(true, &ok).Degrees();
529     if (!ok)
530         return false;
531     double lat = ld->NewLat->createDms(true, &ok).Degrees();
532     if (!ok)
533         return false;
534 
535     if (fabs(lng) > 180 || fabs(lat) > 90)
536         return false;
537 
538     return true;
539 }
540 
clearFields()541 void LocationDialog::clearFields()
542 {
543     ld->CityFilter->clear();
544     ld->ProvinceFilter->clear();
545     ld->CountryFilter->clear();
546     ld->NewCityName->clear();
547     ld->NewProvinceName->clear();
548     ld->NewCountryName->clear();
549     ld->NewLong->clearFields();
550     ld->NewLat->clearFields();
551     ld->NewElev->setValue(-10);
552     ld->TZBox->setCurrentIndex(-1);
553     // JM 2017-09-16: No, let's not assume it is 0. User have to explicitly set TZ so avoid mistakes.
554     //ld->TZBox->lineEdit()->setText(QLocale().toString(0.0));
555     ld->DSTRuleBox->setCurrentIndex(0);
556     nameModified = true;
557     dataModified = false;
558 
559     ld->AddCityButton->setEnabled(false);
560     ld->UpdateButton->setEnabled(false);
561     ld->NewCityName->setFocus();
562 }
563 
showTZRules()564 void LocationDialog::showTZRules()
565 {
566     QFile file;
567 
568     if (KSUtils::openDataFile(file, "TZrules.dat") == false)
569         return;
570 
571     QTextStream stream(&file);
572 
573     QString message = i18n("Daylight Saving Time Rules");
574 
575     QPointer<QDialog> tzd = new QDialog(this);
576     tzd->setWindowTitle(message);
577 
578     QPlainTextEdit *textEdit = new QPlainTextEdit(tzd);
579     textEdit->setReadOnly(true);
580     while (stream.atEnd() == false)
581     {
582         QString line = stream.readLine();
583         if (line.startsWith('#'))
584             textEdit->appendPlainText(line);
585     }
586     textEdit->moveCursor(QTextCursor::Start);
587     textEdit->ensureCursorVisible();
588 
589     QVBoxLayout *mainLayout = new QVBoxLayout;
590     mainLayout->addWidget(textEdit);
591 
592     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
593     mainLayout->addWidget(buttonBox);
594     connect(buttonBox, SIGNAL(rejected()), tzd, SLOT(reject()));
595 
596     tzd->setLayout(mainLayout);
597 
598     tzd->exec();
599 
600     delete tzd;
601 }
602 
nameChanged()603 void LocationDialog::nameChanged()
604 {
605     nameModified = true;
606     dataChanged();
607 }
608 
609 //do not enable Add button until all data are present and valid.
dataChanged()610 void LocationDialog::dataChanged()
611 {
612     dataModified = true;
613     ld->AddCityButton->setEnabled(nameModified && !ld->NewCityName->text().isEmpty() &&
614                                   !ld->NewCountryName->text().isEmpty() && checkLongLat() && ld->TZBox->currentIndex() != -1);
615     if (SelectedCity)
616         ld->UpdateButton->setEnabled(SelectedCity->isReadOnly() == false && !ld->NewCityName->text().isEmpty() &&
617                                      !ld->NewCountryName->text().isEmpty() && checkLongLat() && ld->TZBox->currentIndex() != -1);
618 
619     if (ld->AddCityButton->isEnabled() == false && ld->UpdateButton->isEnabled() == false)
620     {
621         if (ld->NewCityName->text().isEmpty())
622         {
623             ld->errorLabel->setText(i18n("Cannot add new location -- city name blank"));
624         }
625         else if (ld->NewCountryName->text().isEmpty())
626         {
627             ld->errorLabel->setText(i18n("Cannot add new location -- country name blank"));
628         }
629         else if (!checkLongLat())
630         {
631             ld->errorLabel->setText(i18n("Cannot add new location -- invalid latitude / longitude"));
632         }
633         else if (ld->TZBox->currentIndex() == -1)
634         {
635             ld->errorLabel->setText(i18n("Cannot add new location -- missing UTC Offset"));
636         }
637         else if (SelectedCity && SelectedCity->isReadOnly())
638         {
639             ld->errorLabel->setText(i18n("City is Read Only. Change name to add new city."));
640         }
641     }
642     else
643     {
644         ld->errorLabel->setText(QString());
645     }
646 }
647 
slotOk()648 void LocationDialog::slotOk()
649 {
650     if (ld->AddCityButton->isEnabled())
651     {
652         if (addCity())
653             accept();
654     }
655     else
656         accept();
657 }
658 
659 // FIXME Disable this until Qt5 works with Geoclue2
660 #ifdef HAVE_GEOCLUE_2
getNameFromCoordinates(double latitude,double longitude)661 void LocationDialog::getNameFromCoordinates(double latitude, double longitude)
662 {
663     QString lat = QString::number(latitude);
664     QString lon = QString::number(longitude);
665     QString latlng(lat + ", " + lon);
666 
667     QUrl url("http://maps.googleapis.com/maps/api/geocode/json");
668     QUrlQuery query;
669     query.addQueryItem("latlng", latlng);
670     url.setQuery(query);
671     qDebug() << "submitting request";
672 
673     nam->get(QNetworkRequest(url));
674     connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(processLocationNameData(QNetworkReply*)));
675 }
676 
processLocationNameData(QNetworkReply * networkReply)677 void LocationDialog::processLocationNameData(QNetworkReply *networkReply)
678 {
679     if (!networkReply)
680         return;
681 
682     if (!networkReply->error())
683     {
684         QJsonDocument document = QJsonDocument::fromJson(networkReply->readAll());
685 
686         if (document.isObject())
687         {
688             QJsonObject obj = document.object();
689             QJsonValue val;
690 
691             if (obj.contains(QStringLiteral("results")))
692             {
693                 val = obj["results"];
694 
695                 QString city =
696                     val.toArray()[0].toObject()["address_components"].toArray()[2].toObject()["long_name"].toString();
697                 QString region =
698                     val.toArray()[0].toObject()["address_components"].toArray()[3].toObject()["long_name"].toString();
699                 QString country =
700                     val.toArray()[0].toObject()["address_components"].toArray()[4].toObject()["long_name"].toString();
701 
702                 //emit newNameFromCoordinates(city, region, country);
703             }
704             else
705             {
706             }
707         }
708     }
709     networkReply->deleteLater();
710 }
711 
requestUpdate()712 void LocationDialog::requestUpdate()
713 {
714     source->requestUpdate(15000);
715 }
716 
positionUpdated(const QGeoPositionInfo & info)717 void LocationDialog::positionUpdated(const QGeoPositionInfo &info)
718 {
719     qDebug() << "Position updated:" << info;
720 }
721 
positionUpdateError(QGeoPositionInfoSource::Error error)722 void LocationDialog::positionUpdateError(QGeoPositionInfoSource::Error error)
723 {
724     qDebug() << "Position update error: " << error;
725 }
726 
positionUpdateTimeout()727 void LocationDialog::positionUpdateTimeout()
728 {
729     qDebug() << "Timed out!";
730     qDebug() << source->error();
731 }
732 #endif
733