7#include "locationdialog.h" 
   10#include "kstarsdata.h" 
   12#include "ksnotification.h" 
   13#include "kstars_debug.h" 
   19#include <QGeoPositionInfoSource> 
   22#include <QJsonDocument> 
   25#include <QNetworkAccessManager> 
   26#include <QNetworkReply> 
   29#include <QPlainTextEdit> 
   32LocationDialogUI::LocationDialogUI(
QWidget *parent) : 
QFrame(parent)
 
   44    SelectedCity = 
nullptr;
 
   45    ld           = 
new LocationDialogUI(
this);
 
   51    ld->MapView->setLocationDialog(
this);
 
   60    for (
int i = 0; i < 25; ++i)
 
   61        ld->TZBox->addItem(
QLocale().toString(
static_cast<double>(i - 12)));
 
   67            ld->DSTRuleBox->addItem(key);
 
   73    connect(ld->NewCityName, SIGNAL(textChanged(
QString)), 
this, SLOT(nameChanged()));
 
   74    connect(ld->NewProvinceName, SIGNAL(textChanged(
QString)), 
this, SLOT(nameChanged()));
 
   75    connect(ld->NewCountryName, SIGNAL(textChanged(
QString)), 
this, SLOT(nameChanged()));
 
   76    connect(ld->NewLong, SIGNAL(textChanged(
QString)), 
this, SLOT(dataChanged()));
 
   77    connect(ld->NewLat, SIGNAL(textChanged(
QString)), 
this, SLOT(dataChanged()));
 
   78    connect(ld->NewElev, SIGNAL(valueChanged(
double)), 
this, SLOT(dataChanged()));
 
   80    connect(ld->TZBox, SIGNAL(activated(
int)), 
this, SLOT(dataChanged()));
 
   81    connect(ld->DSTRuleBox, SIGNAL(activated(
int)), 
this, SLOT(dataChanged()));
 
   83    connect(ld->AddCityButton, SIGNAL(clicked()), 
this, SLOT(
addCity()));
 
   84    connect(ld->ClearFieldsButton, SIGNAL(clicked()), 
this, SLOT(clearFields()));
 
   92    qDebug() << Q_FUNC_INFO << 
"Last known position" << source->lastKnownPosition().coordinate();
 
   96    connect(source, SIGNAL(updateTimeout()), 
this, SLOT(positionUpdateTimeout()));
 
   98    connect(ld->GetLocationButton, SIGNAL(clicked()), 
this, SLOT(requestUpdate()));
 
  101    ld->DSTLabel->setText(
"<a href=\"showrules\">" + 
i18n(
"DST rule:") + 
"</a>");
 
  102    connect(ld->DSTLabel, SIGNAL(linkActivated(
QString)), 
this, SLOT(showTZRules()));
 
  104    dataModified = 
false;
 
  105    nameModified = 
false;
 
  106    ld->AddCityButton->setEnabled(
false);
 
  108    ld->errorLabel->setText(
QString());
 
 
  125        ld->GeoBox->addItem(loc->
fullName());
 
  126        filteredCityList.append(loc);
 
  129        if (loc->
TZ0() - 
int(loc->
TZ0()) && ld->TZBox->findText(
QLocale().toString(loc->
TZ0())) != -1)
 
  131            for (
int i = 0; i < ld->TZBox->count(); ++i)
 
  133                if (ld->TZBox->itemText(i).toDouble() > loc->
TZ0())
 
  135                    ld->TZBox->addItem(
QLocale().toString(loc->
TZ0()), i - 1);
 
  143    ld->GeoBox->sortItems();
 
  145    ld->CountLabel->setText(
 
  146        i18np(
"One city matches search criteria", 
"%1 cities match search criteria", ld->GeoBox->count()));
 
  149    ld->GeoBox->setCurrentItem(
nullptr);
 
  150    for (
int i = 0; i < ld->GeoBox->count(); i++)
 
  152        if (ld->GeoBox->item(i)->text() == data->
geo()->
fullName())
 
  154            ld->GeoBox->setCurrentRow(i);
 
 
  167        timer->setSingleShot(
true);
 
 
  178    while (!filteredCityList.isEmpty())
 
  179        filteredCityList.takeFirst();
 
  181    nameModified = 
false;
 
  182    dataModified = 
false;
 
  183    ld->AddCityButton->setEnabled(
false);
 
  184    ld->UpdateButton->setEnabled(
false);
 
  198            ld->GeoBox->addItem(loc->
fullName());
 
  199            filteredCityList.append(loc);
 
  203    ld->GeoBox->sortItems();
 
  205    ld->CountLabel->setText(
 
  206        i18np(
"One city matches search criteria", 
"%1 cities match search criteria", ld->GeoBox->count()));
 
  208    if (ld->GeoBox->count() > 0) 
 
  209        ld->GeoBox->setCurrentItem(ld->GeoBox->item(0));
 
  211    ld->MapView->repaint();
 
 
  219    SelectedCity = 
nullptr;
 
  220    if (ld->GeoBox->currentItem())
 
  222        for (
auto &loc : filteredCityList)
 
  224            if (loc->fullName() == ld->GeoBox->currentItem()->text())
 
  232    ld->MapView->repaint();
 
  237        ld->NewCityName->setText(SelectedCity->translatedName());
 
  238        if (SelectedCity->province().isEmpty())
 
  239            ld->NewProvinceName->setText(
QString());
 
  241            ld->NewProvinceName->setText(SelectedCity->translatedProvince());
 
  243        ld->NewCountryName->setText(SelectedCity->translatedCountry());
 
  244        ld->NewLong->show(SelectedCity->lng());
 
  245        ld->NewLat->show(SelectedCity->lat());
 
  246        ld->TZBox->setEditText(
QLocale().toString(SelectedCity->TZ0()));
 
  247        ld->NewElev->setValue(SelectedCity->elevation());
 
  250        for (
int i = 0; i < ld->DSTRuleBox->count(); ++i)
 
  253            if (tzr.
equals(SelectedCity->tzrule()))
 
  255                ld->DSTRuleBox->setCurrentIndex(i);
 
  260        ld->RemoveButton->setEnabled(SelectedCity->isReadOnly() == 
false);
 
  263    nameModified = 
false;
 
  264    dataModified = 
false;
 
  265    ld->AddCityButton->setEnabled(
false);
 
  266    ld->UpdateButton->setEnabled(
false);
 
 
  276    if (SelectedCity == 
nullptr)
 
 
  284    if (SelectedCity == 
nullptr)
 
 
  292    if (operation == CITY_REMOVE)
 
  298    else if (!nameModified && !dataModified)
 
  300        QString message = 
i18n(
"This city already exists in the database.");
 
  301        KSNotification::sorry(message, 
i18n(
"Error: Duplicate Entry"));
 
  305    bool latOk(
false), lngOk(
false), tzOk(
false);
 
  306    dms lat                = ld->NewLat->createDms(&latOk);
 
  307    dms lng                = ld->NewLong->createDms(&lngOk);
 
  308    QString TimeZoneString = ld->TZBox->lineEdit()->text();
 
  310    double TZ = TimeZoneString.
toDouble(&tzOk);
 
  311    double height          = ld->NewElev->value();
 
  313    if (ld->NewCityName->text().isEmpty() || ld->NewCountryName->text().isEmpty())
 
  315        QString message = 
i18n(
"All fields (except province) must be filled to add this location.");
 
  316        KSNotification::sorry(message, 
i18n(
"Fields are Empty"));
 
  319    else if (!latOk || !lngOk)
 
  321        QString message = 
i18n(
"Could not parse the Latitude/Longitude.");
 
  322        KSNotification::sorry(message, 
i18n(
"Bad Coordinates"));
 
  327        QString message = 
i18n(
"UTC Offset must be selected.");
 
  328        KSNotification::sorry(message, 
i18n(
"UTC Offset"));
 
  333    if (operation == CITY_ADD && !nameModified)
 
  334        operation = CITY_UPDATE;
 
  352        QString query(
"CREATE TABLE city ( " 
  353                      "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, " 
  354                      "Name TEXT DEFAULT NULL, " 
  355                      "Province TEXT DEFAULT NULL, " 
  356                      "Country TEXT DEFAULT NULL, " 
  357                      "Latitude TEXT DEFAULT NULL, " 
  358                      "Longitude TEXT DEFAULT NULL, " 
  359                      "TZ REAL DEFAULT NULL, " 
  360                      "TZRule TEXT DEFAULT NULL," 
  361                      "Elevation REAL NOT NULL DEFAULT -10 )");
 
  363        if (create_query.
exec(query) == 
false)
 
  365            qCWarning(KSTARS) << create_query.
lastError();
 
  369    else if (mycitydb.
open() == 
false)
 
  371        qCWarning(KSTARS) << mycitydb.
lastError();
 
  378    QString country  = ld->NewCountryName->text().trimmed();
 
  379    QString TZrule   = ld->DSTRuleBox->currentText();
 
  380    double Elevation = ld->NewElev->value();
 
  388            add_query.
prepare(
"INSERT INTO city(Name, Province, Country, Latitude, Longitude, TZ, TZRule, Elevation) " 
  389                              "VALUES(:Name, :Province, :Country, :Latitude, :Longitude, :TZ, :TZRule, :Elevation)");
 
  391            add_query.
bindValue(
":Province", province);
 
  392            add_query.
bindValue(
":Country", country);
 
  397            add_query.
bindValue(
":Elevation", Elevation);
 
  398            if (add_query.
exec() == 
false)
 
  400                qCWarning(KSTARS) << add_query.
lastError();
 
  405            g = 
new GeoLocation(lng, lat, name, province, country, TZ, &KStarsData::Instance()->Rulebook[TZrule], Elevation);
 
  406            KStarsData::Instance()->getGeoList().append(g);
 
  415            update_query.
prepare(
"UPDATE city SET Name = :newName, Province = :newProvince, Country = :newCountry, " 
  416                                 "Latitude = :Latitude, Longitude = :Longitude, TZ = :TZ, TZRule = :TZRule, Elevation = :Elevation WHERE " 
  417                                 "Name = :Name AND Province = :Province AND Country = :Country");
 
  418            update_query.
bindValue(
":newName", name);
 
  419            update_query.
bindValue(
":newProvince", province);
 
  420            update_query.
bindValue(
":newCountry", country);
 
  421            update_query.
bindValue(
":Name", SelectedCity->name());
 
  422            update_query.
bindValue(
":Province", SelectedCity->province());
 
  423            update_query.
bindValue(
":Country", SelectedCity->country());
 
  427            update_query.
bindValue(
":TZRule", TZrule);
 
  428            update_query.
bindValue(
":Elevation", Elevation);
 
  429            if (update_query.
exec() == 
false)
 
  431                qCWarning(KSTARS) << update_query.
lastError();
 
  441            g->
setTZRule(&KStarsData::Instance()->Rulebook[TZrule]);
 
  451            delete_query.
prepare(
"DELETE FROM city WHERE Name = :Name AND Province = :Province AND Country = :Country");
 
  453            delete_query.
bindValue(
":Province", province);
 
  454            delete_query.
bindValue(
":Country", country);
 
  455            if (delete_query.
exec() == 
false)
 
  457                qCWarning(KSTARS) << delete_query.
lastError();
 
  461            filteredCityList.removeOne(g);
 
  462            KStarsData::Instance()->getGeoList().removeOne(g);
 
  473    ld->GeoBox->setCurrentItem(
nullptr);
 
  474    if (g && ld->GeoBox->count())
 
  476        for (
int i = 0; i < ld->GeoBox->count(); i++)
 
  478            if (ld->GeoBox->item(i)->text() == g->
fullName())
 
  480                ld->GeoBox->setCurrentRow(i);
 
 
  498    while (!filteredCityList.isEmpty())
 
  499        filteredCityList.takeFirst();
 
  505            ld->GeoBox->addItem(loc->
fullName());
 
  506            filteredCityList.append(loc);
 
  510    ld->GeoBox->sortItems();
 
  511    ld->CountLabel->setText(
 
  512        i18np(
"One city matches search criteria", 
"%1 cities match search criteria", ld->GeoBox->count()));
 
  514    if (ld->GeoBox->count() > 0) 
 
  515        ld->GeoBox->setCurrentItem(ld->GeoBox->item(0));
 
 
  520bool LocationDialog::checkLongLat()
 
  522    if (ld->NewLong->text().isEmpty() || ld->NewLat->text().isEmpty())
 
  526    double lng = ld->NewLong->createDms(&ok).Degrees();
 
  529    double lat = ld->NewLat->createDms(&ok).Degrees();
 
  533    if (fabs(lng) > 180 || fabs(lat) > 90)
 
  539void LocationDialog::clearFields()
 
  541    ld->CityFilter->clear();
 
  542    ld->ProvinceFilter->clear();
 
  543    ld->CountryFilter->clear();
 
  544    ld->NewCityName->clear();
 
  545    ld->NewProvinceName->clear();
 
  546    ld->NewCountryName->clear();
 
  547    ld->NewLong->clearFields();
 
  548    ld->NewLat->clearFields();
 
  549    ld->NewElev->setValue(-10);
 
  550    ld->TZBox->setCurrentIndex(-1);
 
  553    ld->DSTRuleBox->setCurrentIndex(0);
 
  555    dataModified = 
false;
 
  558    ld->UpdateButton->setEnabled(
false);
 
  559    ld->NewCityName->setFocus();
 
  562void LocationDialog::showTZRules()
 
  566    if (KSUtils::openDataFile(file, 
"TZrules.dat") == 
false)
 
  569    QTextStream stream(&file);
 
  571    QString message = 
i18n(
"Daylight Saving Time Rules");
 
  573    QPointer<QDialog> tzd = 
new QDialog(
this);
 
  574    tzd->setWindowTitle(message);
 
  576    QPlainTextEdit *textEdit = 
new QPlainTextEdit(tzd);
 
  578    while (stream.atEnd() == 
false)
 
  580        QString line = stream.readLine();
 
  587    QVBoxLayout *mainLayout = 
new QVBoxLayout;
 
  594    tzd->setLayout(mainLayout);
 
  601void LocationDialog::nameChanged()
 
  608void LocationDialog::dataChanged()
 
  611    ld->AddCityButton->setEnabled(nameModified && !ld->NewCityName->text().isEmpty() &&
 
  612                                  !ld->NewCountryName->text().isEmpty() && checkLongLat() && ld->TZBox->currentIndex() != -1);
 
  614        ld->UpdateButton->setEnabled(SelectedCity->isReadOnly() == 
false && !ld->NewCityName->text().isEmpty() &&
 
  615                                     !ld->NewCountryName->text().isEmpty() && checkLongLat() && ld->TZBox->currentIndex() != -1);
 
  617    if (ld->AddCityButton->isEnabled() == 
false && ld->UpdateButton->isEnabled() == 
false)
 
  619        if (ld->NewCityName->text().isEmpty())
 
  621            ld->errorLabel->setText(
i18n(
"Cannot add new location -- city name blank"));
 
  623        else if (ld->NewCountryName->text().isEmpty())
 
  625            ld->errorLabel->setText(
i18n(
"Cannot add new location -- country name blank"));
 
  627        else if (!checkLongLat())
 
  629            ld->errorLabel->setText(
i18n(
"Cannot add new location -- invalid latitude / longitude"));
 
  631        else if (ld->TZBox->currentIndex() == -1)
 
  633            ld->errorLabel->setText(
i18n(
"Cannot add new location -- missing UTC Offset"));
 
  635        else if (SelectedCity && SelectedCity->isReadOnly())
 
  637            ld->errorLabel->setText(
i18n(
"City is Read Only. Change name to add new city."));
 
  642        ld->errorLabel->setText(QString());
 
  646void LocationDialog::slotOk()
 
  648    if (ld->AddCityButton->isEnabled())
 
  659void LocationDialog::getNameFromCoordinates(
double latitude, 
double longitude)
 
  663    QString latlng(lat + 
", " + lon);
 
  665    QUrl url(
"http://maps.googleapis.com/maps/api/geocode/json");
 
  667    query.addQueryItem(
"latlng", latlng);
 
  669    qDebug() << Q_FUNC_INFO << 
"submitting request";
 
  671    nam->get(QNetworkRequest(url));
 
  672    connect(nam, SIGNAL(
finished(QNetworkReply*)), 
this, SLOT(processLocationNameData(QNetworkReply*)));
 
  675void LocationDialog::processLocationNameData(
QNetworkReply *networkReply)
 
  680    if (!networkReply->
error())
 
  686            QJsonObject obj = document.
object();
 
  689            if (obj.
contains(QStringLiteral(
"results")))
 
  691                val = obj[
"results"];
 
  694                    val.
toArray()[0].toObject()[
"address_components"].toArray()[2].toObject()[
"long_name"].toString();
 
  696                    val.
toArray()[0].toObject()[
"address_components"].toArray()[3].toObject()[
"long_name"].toString();
 
  698                    val.
toArray()[0].toObject()[
"address_components"].toArray()[4].toObject()[
"long_name"].toString();
 
  710void LocationDialog::requestUpdate()
 
  712    source->requestUpdate(15000);
 
  717    qDebug() << Q_FUNC_INFO << 
"Position updated:" << info;
 
  722    qDebug() << Q_FUNC_INFO << 
"Position update error: " << 
error;
 
  725void LocationDialog::positionUpdateTimeout()
 
  727    qDebug() << Q_FUNC_INFO << 
"Timed out!";
 
  728    qDebug() << Q_FUNC_INFO << source->error();
 
Contains all relevant information for specifying a location on Earth: City Name, State/Province name,...
 
void setName(const QString &n)
Set City name according to argument.
 
const CachingDms * lat() const
 
const CachingDms * lng() const
 
QString translatedCountry() const
 
void setLong(const dms &l)
Set longitude according to dms argument.
 
void setLat(const dms &l)
Set latitude according to dms argument.
 
void setTZ0(double value)
Set Time zone.
 
void setElevation(double hg)
Set elevation above sea level.
 
void setTZRule(TimeZoneRule *value)
Set Time zone rule.
 
QString translatedName() const
 
void setProvince(const QString &n)
Set Province name according to argument.
 
QString translatedProvince() const
 
void setCountry(const QString &n)
Set Country name according to argument.
 
KStarsData is the backbone of KStars.
 
QList< GeoLocation * > & getGeoList()
 
const QMap< QString, TimeZoneRule > & getRulebook() const
Return map for daylight saving rules.
 
void enqueueFilterCity()
Filter by city / province / country only after a few milliseconds.
 
bool updateCity()
When the "Update City" QPushButton is clicked, update the city information in the user's custom city ...
 
QString selectedCityName() const
 
void filterCity()
When text is entered in the City/Province/Country Filter KLineEdits, the List of cities is trimmed to...
 
void initCityList(void)
Initialize list of cities.
 
LocationDialog(QWidget *parent)
Constructor.
 
void findCitiesNear(int longitude, int latitude)
Show only cities within 3 degrees of point specified by arguments.
 
bool addCity()
When the "Add new city" QPushButton is clicked, add the manually-entered city information to the user...
 
bool removeCity()
When the "Remove City" QPushButton is clicked, remove the city information from the user's custom cit...
 
void changeCity()
When the selected city in the QListBox changes, repaint the MapCanvas so that the crosshairs icon app...
 
This class provides the information needed to determine whether Daylight Savings Time (DST; a....
 
bool equals(TimeZoneRule *r)
 
An angle, stored as degrees, but expressible in many ways.
 
const QString toDMSString(const bool forceSign=false, const bool machineReadable=false, const bool highPrecision=false) const
 
const double & Degrees() const
 
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
 
QString i18nc(const char *context, const char *text, const TYPE &arg...)
 
QString i18n(const char *text, const TYPE &arg...)
 
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
 
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
 
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
 
KI18NLOCALEDATA_EXPORT KCountry country(const char *ianaId)
 
QDialog(QWidget *parent, Qt::WindowFlags f)
 
void finished(int result)
 
QString filePath(const QString &fileName) const const
 
bool exists(const QString &fileName)
 
SatellitePositioningMethods
 
QGeoPositionInfoSource * createDefaultSource(QObject *parent)
 
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
 
bool isObject() const const
 
QJsonObject object() const const
 
bool contains(QLatin1StringView key) const const
 
QJsonArray toArray() const const
 
QList< Key > keys() const const
 
T value(const Key &key, const T &defaultValue) const const
 
NetworkError error() const const
 
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
 
QObject * parent() const const
 
void appendPlainText(const QString &text)
 
void ensureCursorVisible()
 
void moveCursor(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode)
 
void setReadOnly(bool ro)
 
QSqlDatabase database(const QString &connectionName, bool open)
 
QSqlError lastError() const const
 
void setDatabaseName(const QString &name)
 
void bindValue(const QString &placeholder, const QVariant &val, QSql::ParamType paramType)
 
QSqlError lastError() const const
 
bool prepare(const QString &query)
 
bool isEmpty() const const
 
QString number(double n, char format, int precision)
 
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
 
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
 
double toDouble(bool *ok) const const
 
QString trimmed() const const