diff --git a/kstars/auxiliary/geolocation.h b/kstars/auxiliary/geolocation.h index 41c8c6fc5..091e9ac0e 100644 --- a/kstars/auxiliary/geolocation.h +++ b/kstars/auxiliary/geolocation.h @@ -1,283 +1,283 @@ /*************************************************************************** geolocation.h - K Desktop Planetarium ------------------- begin : Sun Feb 11 2001 copyright : (C) 2001-2005 by Jason Harris email : jharris@30doradus.org copyright : (C) 2003-2005 by Pablo de Vicente email : p.devicente@wanadoo.es ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #pragma once #include #include "cachingdms.h" #include "timezonerule.h" #include "kstarsdatetime.h" /** * @class GeoLocation * Contains all relevant information for specifying a location * on Earth: City Name, State/Province name, Country Name, * Longitude, Latitude, Elevation, Time Zone, and Daylight Savings * Time rule. * * @short Relevant data about an observing location on Earth. * @author Jason Harris * @version 1.0 */ class GeoLocation { public: /** Constructor using dms objects to specify longitude and latitude. * @param lng the longitude * @param lat the latitude * @param name the name of the city/town/location * @param province the name of the province/US state * @param country the name of the country * @param TZ the base time zone offset from Greenwich, UK * @param TZrule pointer to the daylight savings time rule * @param elevation the elevation above sea level (in meters) * @param readOnly whether the location is read only or updatable. * @param iEllips type of geodetic ellipsoid model */ GeoLocation(const dms &lng, const dms &lat, const QString &name = "Nowhere", const QString &province = "Nowhere", const QString &country = "Nowhere", double TZ = 0, TimeZoneRule *TZrule = nullptr, double elevation = -10, bool readOnly = false, int iEllips = 4); /** Constructor using doubles to specify X, Y and Z referred to the center of the Earth. * @param x the x-position, in m * @param y the y-position, in m * @param z the z-position, in m * @param name the name of the city/town/location * @param province the name of the province/US state * @param country the name of the country * @param TZ the base time zone offset from Greenwich, UK * @param TZrule pointer to the daylight savings time rule * @param elevation the elevation above sea level (in meters) * @param readOnly whether the location is read only or updatable. * @param iEllips type of geodetic ellipsoid model */ GeoLocation(double x, double y, double z, const QString &name = "Nowhere", const QString &province = "Nowhere", const QString &country = "Nowhere", double TZ = 0, TimeZoneRule *TZrule = nullptr, double elevation = -10, bool readOnly = false, int iEllips = 4); /** @return pointer to the longitude dms object */ const CachingDms *lng() const { return &Longitude; } /** @return pointer to the latitude dms object */ const CachingDms *lat() const { return &Latitude; } /** @return elevation above seal level (meters) */ double elevation() const { return Elevation; } /** @return X position in m */ double xPos() const { return PosCartX; } /** @return Y position in m */ double yPos() const { return PosCartY; } /** @return Z position in m */ double zPos() const { return PosCartZ; } /** @return index identifying the geodetic ellipsoid model */ int ellipsoid() const { return indexEllipsoid; } /** @return untranslated City name */ QString name() const { return Name; } /** @return translated City name */ QString translatedName() const; /** @return untranslated Province name */ QString province() const { return Province; } /** @return translated Province name */ QString translatedProvince() const; /** @return untranslated Country name */ QString country() const { return Country; } /** @return translated Country name */ QString translatedCountry() const; /** @return comma-separated city, province, country names (each localized) */ QString fullName() const; /** @return time zone without DST correction */ double TZ0() const { return TimeZone; } /** @return time zone, including any DST correction. */ double TZ() const { if (TZrule) return TimeZone + TZrule->deltaTZ(); return TimeZone; } /** @return pointer to time zone rule object */ TimeZoneRule *tzrule() { return TZrule; } /** Set Time zone. * @param value the new time zone */ - void setTZ(double value) { TimeZone = value; } + void setTZ0(double value) { TimeZone = value; } /** Set Time zone rule. * @param value pointer to the new time zone rule */ void setTZRule(TimeZoneRule *value) { TZrule = value; } /** Set longitude according to dms argument. * @param l the new longitude */ void setLong(const dms &l) { Longitude = l; geodToCart(); } /** Set latitude according to dms argument. * @param l the new latitude */ void setLat(const dms &l) { Latitude = l; geodToCart(); } /** Set elevation above sea level * @param hg the new elevation (meters) */ void setElevation(double hg) { Elevation = hg; geodToCart(); } /** Set X * @param x the new x-position (meters) */ void setXPos(double x) { PosCartX = x; cartToGeod(); } /** Set Y * @param y the new y-position (meters) */ void setYPos(double y) { PosCartY = y; cartToGeod(); } /** Set Z * @param z the new z-position (meters) */ void setZPos(double z) { PosCartZ = z; cartToGeod(); } /** Update Latitude, Longitude and Height according to new ellipsoid. These are * computed from XYZ which do NOT change on changing the ellipsoid. * @param i index to identify the ellipsoid */ void changeEllipsoid(int i); /** Set City name according to argument. * @param n new city name */ void setName(const QString &n) { Name = n; } /** Set Province name according to argument. * @param n new province name */ void setProvince(const QString &n) { Province = n; } /** Set Country name according to argument. * @param n new country name */ void setCountry(const QString &n) { Country = n; } /** Converts from cartesian coordinates in meters to longitude, * latitude and height on a standard geoid for the Earth. The * geoid is characterized by two parameters: the semimajor axis * and the flattening. * * @note The astronomical zenith is defined as the perpendicular to * the real geoid. The geodetic zenith is the perpendicular to the * standard geoid. Both zeniths differ due to local gravitational * anomalies. * * Algorithm is from "GPS Satellite Surveying", A. Leick, page 184. */ void cartToGeod(); /** Converts from longitude, latitude and height on a standard * geoid of the Earth to cartesian coordinates in meters. The geoid * is characterized by two parameters: the semimajor axis and the * flattening. * * @note The astronomical zenith is defined as the perpendicular to * the real geoid. The geodetic zenith is the perpendicular to the * standard geoid. Both zeniths differ due to local gravitational * anomalies. * * Algorithm is from "GPS Satellite Surveying", A. Leick, page 184. */ void geodToCart(); /** The geoid is an elliposid which fits the shape of the Earth. It is * characterized by two parameters: the semimajor axis and the * flattening. * * @param i is the index which allows to identify the parameters for the * chosen elliposid. 1="IAU76", 2="GRS80", 3="MERIT83", 4="WGS84", * 5="IERS89". */ void setEllipsoid(int i); dms GSTtoLST(const dms &gst) const { return dms(gst.Degrees() + Longitude.Degrees()); } dms LSTtoGST(const dms &lst) const { return dms(lst.Degrees() - Longitude.Degrees()); } KStarsDateTime UTtoLT(const KStarsDateTime &ut) const; KStarsDateTime LTtoUT(const KStarsDateTime <) const; /** Computes the velocity in km/s of an observer on the surface of the Earth * referred to a system whose origin is the center of the Earth. The X and * Y axis are contained in the equator and the X axis is towards the nodes * line. The Z axis is along the poles. * * @param vtopo[] Topocentric velocity. The resultant velocity is available * in this array. * @param gt Greenwich sideral time for which we want to compute the topocentric velocity. */ void TopocentricVelocity(double vtopo[], const dms >); /** @Return Local Mean Sidereal Time. * @param jd Julian date */ double LMST(double jd); bool isReadOnly() const; void setReadOnly(bool value); private: CachingDms Longitude, Latitude; QString Name, Province, Country; TimeZoneRule *TZrule; double TimeZone, Elevation; double axis, flattening; long double PosCartX, PosCartY, PosCartZ; int indexEllipsoid; bool ReadOnly; }; diff --git a/kstars/dialogs/locationdialog.cpp b/kstars/dialogs/locationdialog.cpp index e027b222d..10579b1d8 100644 --- a/kstars/dialogs/locationdialog.cpp +++ b/kstars/dialogs/locationdialog.cpp @@ -1,746 +1,746 @@ /*************************************************************************** locationdialog.cpp - K Desktop Planetarium ------------------- begin : Sun Feb 11 2001 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "locationdialog.h" #include "kspaths.h" #include "kstarsdata.h" #include "kstars_debug.h" #include "ksutils.h" #include #ifdef HAVE_GEOCLUE2 #include #endif #include #include #include #include #include #include #include #include #include #include #include #include LocationDialogUI::LocationDialogUI(QWidget *parent) : QFrame(parent) { setupUi(this); } LocationDialog::LocationDialog(QWidget *parent) : QDialog(parent), timer(nullptr) { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif KStarsData *data = KStarsData::Instance(); SelectedCity = nullptr; ld = new LocationDialogUI(this); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(ld); setLayout(mainLayout); ld->MapView->setLocationDialog(this); setWindowTitle(i18n("Set Geographic Location")); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotOk())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); for (int i = 0; i < 25; ++i) ld->TZBox->addItem(QLocale().toString((double)(i - 12))); //Populate DSTRuleBox foreach (const QString &key, data->getRulebook().keys()) { if (!key.isEmpty()) ld->DSTRuleBox->addItem(key); } ld->AddCityButton->setIcon(QIcon::fromTheme("list-add")); ld->RemoveButton->setIcon(QIcon::fromTheme("list-remove")); ld->UpdateButton->setIcon(QIcon::fromTheme("svn-update")); connect(ld->CityFilter, SIGNAL(textChanged(QString)), this, SLOT(enqueueFilterCity())); connect(ld->ProvinceFilter, SIGNAL(textChanged(QString)), this, SLOT(enqueueFilterCity())); connect(ld->CountryFilter, SIGNAL(textChanged(QString)), this, SLOT(enqueueFilterCity())); connect(ld->NewCityName, SIGNAL(textChanged(QString)), this, SLOT(nameChanged())); connect(ld->NewProvinceName, SIGNAL(textChanged(QString)), this, SLOT(nameChanged())); connect(ld->NewCountryName, SIGNAL(textChanged(QString)), this, SLOT(nameChanged())); connect(ld->NewLong, SIGNAL(textChanged(QString)), this, SLOT(dataChanged())); connect(ld->NewLat, SIGNAL(textChanged(QString)), this, SLOT(dataChanged())); connect(ld->NewElev, SIGNAL(valueChanged(double)), this, SLOT(dataChanged())); connect(ld->TZBox, SIGNAL(activated(int)), this, SLOT(dataChanged())); connect(ld->DSTRuleBox, SIGNAL(activated(int)), this, SLOT(dataChanged())); connect(ld->GeoBox, SIGNAL(itemSelectionChanged()), this, SLOT(changeCity())); connect(ld->AddCityButton, SIGNAL(clicked()), this, SLOT(addCity())); connect(ld->ClearFieldsButton, SIGNAL(clicked()), this, SLOT(clearFields())); connect(ld->RemoveButton, SIGNAL(clicked()), this, SLOT(removeCity())); connect(ld->UpdateButton, SIGNAL(clicked()), this, SLOT(updateCity())); // FIXME Disable this until Qt5 works with Geoclue2 #ifdef HAVE_GEOCLUE_2 source = QGeoPositionInfoSource::createDefaultSource(this); source->setPreferredPositioningMethods(QGeoPositionInfoSource::SatellitePositioningMethods); qDebug() << "Last known position" << source->lastKnownPosition().coordinate(); connect(source, SIGNAL(positionUpdated(QGeoPositionInfo)), this, SLOT(positionUpdated(QGeoPositionInfo))); connect(source, SIGNAL(error(QGeoPositionInfoSource::Error)), this, SLOT(positionUpdateError(QGeoPositionInfoSource::Error))); connect(source, SIGNAL(updateTimeout()), this, SLOT(positionUpdateTimeout())); connect(ld->GetLocationButton, SIGNAL(clicked()), this, SLOT(requestUpdate())); #endif ld->DSTLabel->setText("" + i18n("DST Rule:") + ""); connect(ld->DSTLabel, SIGNAL(linkActivated(QString)), this, SLOT(showTZRules())); dataModified = false; nameModified = false; ld->AddCityButton->setEnabled(false); ld->errorLabel->setText(QString()); // FIXME Disable this until Qt5 works with Geoclue2 #ifdef HAVE_GEOCLUE_2 nam = new QNetworkAccessManager(this); connect(nam, SIGNAL(finished(QNetworkReply *)), this, SLOT(processLocationNameData(QNetworkReply *))); #endif initCityList(); resize(640, 480); } void LocationDialog::initCityList() { KStarsData *data = KStarsData::Instance(); foreach (GeoLocation *loc, data->getGeoList()) { ld->GeoBox->addItem(loc->fullName()); filteredCityList.append(loc); //If TZ is not an even integer value, add it to listbox if (loc->TZ0() - int(loc->TZ0()) && ld->TZBox->findText(QLocale().toString(loc->TZ0())) != -1) { for (int i = 0; i < ld->TZBox->count(); ++i) { if (ld->TZBox->itemText(i).toDouble() > loc->TZ0()) { ld->TZBox->addItem(QLocale().toString(loc->TZ0()), i - 1); break; } } } } //Sort the list of Cities alphabetically...note that filteredCityList may now have a different ordering! ld->GeoBox->sortItems(); ld->CountLabel->setText( i18np("One city matches search criteria", "%1 cities match search criteria", ld->GeoBox->count())); // attempt to highlight the current kstars location in the GeoBox ld->GeoBox->setCurrentItem(nullptr); for (int i = 0; i < ld->GeoBox->count(); i++) { if (ld->GeoBox->item(i)->text() == data->geo()->fullName()) { ld->GeoBox->setCurrentRow(i); break; } } } void LocationDialog::enqueueFilterCity() { if (timer) timer->stop(); else { timer = new QTimer(this); timer->setSingleShot(true); connect(timer, SIGNAL(timeout()), this, SLOT(filterCity())); } timer->start(500); } void LocationDialog::filterCity() { KStarsData *data = KStarsData::Instance(); ld->GeoBox->clear(); //Do NOT delete members of filteredCityList! while (!filteredCityList.isEmpty()) filteredCityList.takeFirst(); nameModified = false; dataModified = false; ld->AddCityButton->setEnabled(false); ld->UpdateButton->setEnabled(false); foreach (GeoLocation *loc, data->getGeoList()) { QString sc(loc->translatedName()); QString ss(loc->translatedCountry()); QString sp = ""; if (!loc->province().isEmpty()) sp = loc->translatedProvince(); if (sc.startsWith(ld->CityFilter->text(), Qt::CaseInsensitive) && sp.startsWith(ld->ProvinceFilter->text(), Qt::CaseInsensitive) && ss.startsWith(ld->CountryFilter->text(), Qt::CaseInsensitive)) { ld->GeoBox->addItem(loc->fullName()); filteredCityList.append(loc); } } ld->GeoBox->sortItems(); ld->CountLabel->setText( i18np("One city matches search criteria", "%1 cities match search criteria", ld->GeoBox->count())); if (ld->GeoBox->count() > 0) // set first item in list as selected ld->GeoBox->setCurrentItem(ld->GeoBox->item(0)); ld->MapView->repaint(); } void LocationDialog::changeCity() { KStarsData *data = KStarsData::Instance(); //when the selected city changes, set newCity, and redraw map SelectedCity = nullptr; if (ld->GeoBox->currentItem()) { for (int i = 0; i < filteredCityList.size(); ++i) { GeoLocation *loc = filteredCityList.at(i); if (loc->fullName() == ld->GeoBox->currentItem()->text()) { SelectedCity = loc; break; } } } ld->MapView->repaint(); //Fill the fields at the bottom of the window with the selected city's data. if (SelectedCity) { ld->NewCityName->setText(SelectedCity->translatedName()); if (SelectedCity->province().isEmpty()) ld->NewProvinceName->setText(QString()); else ld->NewProvinceName->setText(SelectedCity->translatedProvince()); ld->NewCountryName->setText(SelectedCity->translatedCountry()); ld->NewLong->showInDegrees(SelectedCity->lng()); ld->NewLat->showInDegrees(SelectedCity->lat()); ld->TZBox->setEditText(QLocale().toString(SelectedCity->TZ0())); ld->NewElev->setValue(SelectedCity->elevation()); //Pick the City's rule from the rulebook for (int i = 0; i < ld->DSTRuleBox->count(); ++i) { TimeZoneRule tzr = data->getRulebook().value(ld->DSTRuleBox->itemText(i)); if (tzr.equals(SelectedCity->tzrule())) { ld->DSTRuleBox->setCurrentIndex(i); break; } } ld->RemoveButton->setEnabled(SelectedCity->isReadOnly() == false); } nameModified = false; dataModified = false; ld->AddCityButton->setEnabled(false); ld->UpdateButton->setEnabled(false); } bool LocationDialog::addCity() { return updateCity(CITY_ADD); } bool LocationDialog::updateCity() { if (SelectedCity == nullptr) return false; return updateCity(CITY_UPDATE); } bool LocationDialog::removeCity() { if (SelectedCity == nullptr) return false; return updateCity(CITY_REMOVE); } bool LocationDialog::updateCity(CityOperation operation) { if (operation == CITY_REMOVE) { QString message = i18n("Are you sure you want to remove %1?", selectedCityName()); if (KMessageBox::questionYesNo(nullptr, message, i18n("Remove City?")) == KMessageBox::No) return false; //user answered No. } else if (!nameModified && !dataModified) { QString message = i18n("This city already exists in the database."); KMessageBox::sorry(nullptr, message, i18n("Error: Duplicate Entry")); return false; } bool latOk(false), lngOk(false), tzOk(false); dms lat = ld->NewLat->createDms(true, &latOk); dms lng = ld->NewLong->createDms(true, &lngOk); QString TimeZoneString = ld->TZBox->lineEdit()->text(); TimeZoneString.replace(QLocale().decimalPoint(), "."); double TZ = TimeZoneString.toDouble(&tzOk); double height = ld->NewElev->value(); if (ld->NewCityName->text().isEmpty() || ld->NewCountryName->text().isEmpty()) { QString message = i18n("All fields (except province) must be filled to add this location."); KMessageBox::sorry(nullptr, message, i18n("Fields are Empty")); return false; } else if (!latOk || !lngOk) { QString message = i18n("Could not parse the Latitude/Longitude."); KMessageBox::sorry(nullptr, message, i18n("Bad Coordinates")); return false; } else if (!tzOk) { QString message = i18n("UTC Offset must be selected."); KMessageBox::sorry(nullptr, message, i18n("UTC Offset")); return false; } // If name is still the same then it's an update operation if (operation == CITY_ADD && !nameModified) operation = CITY_UPDATE; /*if ( !nameModified ) { QString message = i18n( "Really override original data for this city?" ); if ( KMessageBox::questionYesNo( 0, message, i18n( "Override Existing Data?" ), KGuiItem(i18n("Override Data")), KGuiItem(i18n("Do Not Override"))) == KMessageBox::No ) return false; //user answered No. }*/ QSqlDatabase mycitydb = QSqlDatabase::database("mycitydb"); QString dbfile = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "mycitydb.sqlite"; // If it doesn't exist, create it if (QFile::exists(dbfile) == false) { mycitydb.setDatabaseName(dbfile); mycitydb.open(); QSqlQuery create_query(mycitydb); QString query("CREATE TABLE city ( " "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, " "Name TEXT DEFAULT NULL, " "Province TEXT DEFAULT NULL, " "Country TEXT DEFAULT NULL, " "Latitude TEXT DEFAULT NULL, " "Longitude TEXT DEFAULT NULL, " "TZ REAL DEFAULT NULL, " "TZRule TEXT DEFAULT NULL," "Elevation REAL NOT NULL DEFAULT -10 )"); if (create_query.exec(query) == false) { qCWarning(KSTARS) << create_query.lastError(); return false; } } else if (mycitydb.open() == false) { qCWarning(KSTARS) << mycitydb.lastError(); return false; } //Strip off white space QString name = ld->NewCityName->text().trimmed(); QString province = ld->NewProvinceName->text().trimmed(); QString country = ld->NewCountryName->text().trimmed(); QString TZrule = ld->DSTRuleBox->currentText(); double Elevation = ld->NewElev->value(); GeoLocation *g = nullptr; switch (operation) { case CITY_ADD: { QSqlQuery add_query(mycitydb); add_query.prepare("INSERT INTO city(Name, Province, Country, Latitude, Longitude, TZ, TZRule, Elevation) " "VALUES(:Name, :Province, :Country, :Latitude, :Longitude, :TZ, :TZRule, :Elevation)"); add_query.bindValue(":Name", name); add_query.bindValue(":Province", province); add_query.bindValue(":Country", country); add_query.bindValue(":Latitude", lat.toDMSString()); add_query.bindValue(":Longitude", lng.toDMSString()); add_query.bindValue(":TZ", TZ); add_query.bindValue(":TZRule", TZrule); add_query.bindValue(":Elevation", Elevation); if (add_query.exec() == false) { qCWarning(KSTARS) << add_query.lastError(); return false; } //Add city to geoList...don't need to insert it alphabetically, since we always sort GeoList g = new GeoLocation(lng, lat, name, province, country, TZ, &KStarsData::Instance()->Rulebook[TZrule], Elevation); KStarsData::Instance()->getGeoList().append(g); } break; case CITY_UPDATE: { g = SelectedCity; QSqlQuery update_query(mycitydb); update_query.prepare("UPDATE city SET Name = :newName, Province = :newProvince, Country = :newCountry, " "Latitude = :Latitude, Longitude = :Longitude, TZ = :TZ, TZRule = :TZRule, Elevation = :Elevation WHERE " "Name = :Name AND Province = :Province AND Country = :Country"); update_query.bindValue(":newName", name); update_query.bindValue(":newProvince", province); update_query.bindValue(":newCountry", country); update_query.bindValue(":Name", SelectedCity->name()); update_query.bindValue(":Province", SelectedCity->province()); update_query.bindValue(":Country", SelectedCity->country()); update_query.bindValue(":Latitude", lat.toDMSString()); update_query.bindValue(":Longitude", lng.toDMSString()); update_query.bindValue(":TZ", TZ); update_query.bindValue(":TZRule", TZrule); update_query.bindValue(":Elevation", Elevation); if (update_query.exec() == false) { qCWarning(KSTARS) << update_query.lastError() << endl; return false; } g->setName(name); g->setProvince(province); g->setCountry(country); g->setLat(lat); g->setLong(lng); - g->setTZ(TZ); + g->setTZ0(TZ); g->setTZRule(&KStarsData::Instance()->Rulebook[TZrule]); g->setElevation(height); } break; case CITY_REMOVE: { g = SelectedCity; QSqlQuery delete_query(mycitydb); delete_query.prepare("DELETE FROM city WHERE Name = :Name AND Province = :Province AND Country = :Country"); delete_query.bindValue(":Name", name); delete_query.bindValue(":Province", province); delete_query.bindValue(":Country", country); if (delete_query.exec() == false) { qCWarning(KSTARS) << delete_query.lastError() << endl; return false; } filteredCityList.removeOne(g); KStarsData::Instance()->getGeoList().removeOne(g); delete g; g = nullptr; } break; } //(possibly) insert new city into GeoBox by running filterCity() filterCity(); //Attempt to highlight new city in list ld->GeoBox->setCurrentItem(nullptr); if (g && ld->GeoBox->count()) { for (int i = 0; i < ld->GeoBox->count(); i++) { if (ld->GeoBox->item(i)->text() == g->fullName()) { ld->GeoBox->setCurrentRow(i); break; } } } mycitydb.commit(); mycitydb.close(); return true; } void LocationDialog::findCitiesNear(int lng, int lat) { KStarsData *data = KStarsData::Instance(); //find all cities within 3 degrees of (lng, lat); list them in GeoBox ld->GeoBox->clear(); //Remember, do NOT delete members of filteredCityList while (!filteredCityList.isEmpty()) filteredCityList.takeFirst(); foreach (GeoLocation *loc, data->getGeoList()) { if ((abs(lng - int(loc->lng()->Degrees())) < 3) && (abs(lat - int(loc->lat()->Degrees())) < 3)) { ld->GeoBox->addItem(loc->fullName()); filteredCityList.append(loc); } } ld->GeoBox->sortItems(); ld->CountLabel->setText( i18np("One city matches search criteria", "%1 cities match search criteria", ld->GeoBox->count())); if (ld->GeoBox->count() > 0) // set first item in list as selected ld->GeoBox->setCurrentItem(ld->GeoBox->item(0)); repaint(); } bool LocationDialog::checkLongLat() { if (ld->NewLong->text().isEmpty() || ld->NewLat->text().isEmpty()) return false; bool ok; double lng = ld->NewLong->createDms(true, &ok).Degrees(); if (!ok) return false; double lat = ld->NewLat->createDms(true, &ok).Degrees(); if (!ok) return false; if (fabs(lng) > 180 || fabs(lat) > 90) return false; return true; } void LocationDialog::clearFields() { ld->CityFilter->clear(); ld->ProvinceFilter->clear(); ld->CountryFilter->clear(); ld->NewCityName->clear(); ld->NewProvinceName->clear(); ld->NewCountryName->clear(); ld->NewLong->clearFields(); ld->NewLat->clearFields(); ld->NewElev->setValue(-10); ld->TZBox->setCurrentIndex(-1); // JM 2017-09-16: No, let's not assume it is 0. User have to explicitly set TZ so avoid mistakes. //ld->TZBox->lineEdit()->setText(QLocale().toString(0.0)); ld->DSTRuleBox->setCurrentIndex(0); nameModified = true; dataModified = false; ld->AddCityButton->setEnabled(false); ld->UpdateButton->setEnabled(false); ld->NewCityName->setFocus(); } void LocationDialog::showTZRules() { QFile file; if (KSUtils::openDataFile(file, "TZrules.dat") == false) return; QTextStream stream(&file); QString message = i18n("Daylight Saving Time Rules"); QPointer tzd = new QDialog(this); tzd->setWindowTitle(message); QPlainTextEdit *textEdit = new QPlainTextEdit(tzd); textEdit->setReadOnly(true); while (stream.atEnd() == false) { QString line = stream.readLine(); if (line.startsWith("#")) textEdit->appendPlainText(line); } textEdit->moveCursor(QTextCursor::Start); textEdit->ensureCursorVisible(); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(textEdit); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); mainLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(rejected()), tzd, SLOT(reject())); tzd->setLayout(mainLayout); tzd->exec(); delete tzd; } void LocationDialog::nameChanged() { nameModified = true; dataChanged(); } //do not enable Add button until all data are present and valid. void LocationDialog::dataChanged() { dataModified = true; ld->AddCityButton->setEnabled(nameModified && !ld->NewCityName->text().isEmpty() && !ld->NewCountryName->text().isEmpty() && checkLongLat() && ld->TZBox->currentIndex() != -1); if (SelectedCity) ld->UpdateButton->setEnabled(SelectedCity->isReadOnly() == false && !ld->NewCityName->text().isEmpty() && !ld->NewCountryName->text().isEmpty() && checkLongLat() && ld->TZBox->currentIndex() != -1); if (ld->AddCityButton->isEnabled() == false && ld->UpdateButton->isEnabled() == false) { if (ld->NewCityName->text().isEmpty()) { ld->errorLabel->setText(i18n("Cannot add new location -- city name blank")); } else if (ld->NewCountryName->text().isEmpty()) { ld->errorLabel->setText(i18n("Cannot add new location -- country name blank")); } else if (!checkLongLat()) { ld->errorLabel->setText(i18n("Cannot add new location -- invalid latitude / longitude")); } else if (ld->TZBox->currentIndex() == -1) { ld->errorLabel->setText(i18n("Cannot add new location -- missing UTC Offset")); } else if (SelectedCity->isReadOnly()) { ld->errorLabel->setText(i18n("City is Read Only. Change name to add new city.")); } } else { ld->errorLabel->setText(QString()); } } void LocationDialog::slotOk() { if (ld->AddCityButton->isEnabled()) { if (addCity()) accept(); } else accept(); } // FIXME Disable this until Qt5 works with Geoclue2 #ifdef HAVE_GEOCLUE_2 void LocationDialog::getNameFromCoordinates(double latitude, double longitude) { QString lat = QString::number(latitude); QString lon = QString::number(longitude); QString latlng(lat + ", " + lon); QUrl url("http://maps.googleapis.com/maps/api/geocode/json"); QUrlQuery query; query.addQueryItem("latlng", latlng); url.setQuery(query); qDebug() << "submitting request"; nam->get(QNetworkRequest(url)); connect(nam, SIGNAL(finished(QNetworkReply *)), this, SLOT(processLocationNameData(QNetworkReply *))); } void LocationDialog::processLocationNameData(QNetworkReply *networkReply) { if (!networkReply) return; if (!networkReply->error()) { QJsonDocument document = QJsonDocument::fromJson(networkReply->readAll()); if (document.isObject()) { QJsonObject obj = document.object(); QJsonValue val; if (obj.contains(QStringLiteral("results"))) { val = obj["results"]; QString city = val.toArray()[0].toObject()["address_components"].toArray()[2].toObject()["long_name"].toString(); QString region = val.toArray()[0].toObject()["address_components"].toArray()[3].toObject()["long_name"].toString(); QString country = val.toArray()[0].toObject()["address_components"].toArray()[4].toObject()["long_name"].toString(); //emit newNameFromCoordinates(city, region, country); } else { } } } networkReply->deleteLater(); } void LocationDialog::requestUpdate() { source->requestUpdate(15000); } void LocationDialog::positionUpdated(const QGeoPositionInfo &info) { qDebug() << "Position updated:" << info; } void LocationDialog::positionUpdateError(QGeoPositionInfoSource::Error error) { qDebug() << "Position update error: " << error; } void LocationDialog::positionUpdateTimeout() { qDebug() << "Timed out!"; qDebug() << source->error(); } #endif diff --git a/kstars/indi/indistd.cpp b/kstars/indi/indistd.cpp index 3843c9809..91b87df6f 100644 --- a/kstars/indi/indistd.cpp +++ b/kstars/indi/indistd.cpp @@ -1,1015 +1,1026 @@ /* INDI STD Copyright (C) 2012 Jasem Mutlaq (mutlaqja@ikarustech.com) This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Handle INDI Standard properties. */ #include "indistd.h" #include "clientmanager.h" #include "driverinfo.h" #include "deviceinfo.h" #include "imageviewer.h" #include "kstars.h" #include "kstarsdata.h" #include "Options.h" #include "skymap.h" #include #include #include "indi_debug.h" namespace ISD { GDSetCommand::GDSetCommand(INDI_PROPERTY_TYPE inPropertyType, const QString &inProperty, const QString &inElement, QVariant qValue, QObject *parent) : QObject(parent) { propType = inPropertyType; indiProperty = inProperty; indiElement = inElement; elementValue = qValue; } GenericDevice::GenericDevice(DeviceInfo &idv) { deviceInfo = &idv; driverInfo = idv.getDriverInfo(); baseDevice = idv.getBaseDevice(); clientManager = driverInfo->getClientManager(); dType = KSTARS_UNKNOWN; } GenericDevice::~GenericDevice() { } const char *GenericDevice::getDeviceName() { return baseDevice->getDeviceName(); } void GenericDevice::registerProperty(INDI::Property *prop) { foreach (INDI::Property *pp, properties) { if (pp == prop) return; } properties.append(prop); emit propertyDefined(prop); // In case driver already started if (!strcmp(prop->getName(), "CONNECTION")) { ISwitchVectorProperty *svp = prop->getSwitch(); if (svp == nullptr) return; // Still connecting/disconnecting... if (svp->s == IPS_BUSY) return; ISwitch *conSP = IUFindSwitch(svp, "CONNECT"); if (conSP == nullptr) return; if (svp->s == IPS_OK && conSP->s == ISS_ON) { connected = true; emit Connected(); createDeviceInit(); } } if (!strcmp(prop->getName(), "DRIVER_INFO")) { ITextVectorProperty *tvp = prop->getText(); if (tvp) { IText *tp = IUFindText(tvp, "DRIVER_INTERFACE"); if (tp) driverInterface = static_cast(atoi(tp->text)); } } else if (!strcmp(prop->getName(), "TIME_UTC") && Options::useTimeUpdate() && Options::useKStarsSource()) { ITextVectorProperty *tvp = prop->getText(); if (tvp && tvp->p != IP_RO) updateTime(); } else if (!strcmp(prop->getName(), "GEOGRAPHIC_COORD") && Options::useGeographicUpdate() && Options::useKStarsSource()) { INumberVectorProperty *nvp = prop->getNumber(); if (nvp && nvp->p != IP_RO) updateLocation(); } else if (!strcmp(prop->getName(), "WATCHDOG_HEARTBEAT")) { INumberVectorProperty *nvp = prop->getNumber(); if (nvp) { if (watchDogTimer == nullptr) { watchDogTimer = new QTimer(this); connect(watchDogTimer, SIGNAL(timeout()), this, SLOT(resetWatchdog())); } if (connected && nvp->np[0].value > 0) { // Send immediately a heart beat clientManager->sendNewNumber(nvp); //watchDogTimer->start(0); } } } } void GenericDevice::removeProperty(INDI::Property *prop) { properties.removeOne(prop); emit propertyDeleted(prop); } void GenericDevice::processSwitch(ISwitchVectorProperty *svp) { if (!strcmp(svp->name, "CONNECTION")) { ISwitch *conSP = IUFindSwitch(svp, "CONNECT"); if (conSP == nullptr) return; // Still connecting/disconnecting... if (svp->s == IPS_BUSY) return; if (svp->s == IPS_OK && conSP->s == ISS_ON) { connected = true; emit Connected(); createDeviceInit(); if (watchDogTimer != nullptr) { INumberVectorProperty *nvp = baseDevice->getNumber("WATCHDOG_HEARTBEAT"); if (nvp && nvp->np[0].value > 0) { // Send immediately clientManager->sendNewNumber(nvp); //watchDogTimer->start(0); } } } else { connected = false; emit Disconnected(); } } emit switchUpdated(svp); } void GenericDevice::processNumber(INumberVectorProperty *nvp) { QString deviceName = getDeviceName(); uint32_t interface = getDriverInterface(); Q_UNUSED(interface); if (!strcmp(nvp->name, "GEOGRAPHIC_COORD") && nvp->s == IPS_OK && ( (Options::useMountSource() && (getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE)) || (Options::useGPSSource() && (getDriverInterface() & INDI::BaseDevice::GPS_INTERFACE)))) { // Update KStars Location once we receive update from INDI, if the source is set to DEVICE dms lng, lat; double elev=0; INumber *np = nullptr; np = IUFindNumber(nvp, "LONG"); if (!np) return; // INDI Longitude convention is 0 to 360. We need to turn it back into 0 to 180 EAST, 0 to -180 WEST if (np->value < 180) lng.setD(np->value); else lng.setD(np->value - 360.0); np = IUFindNumber(nvp, "LAT"); if (!np) return; lat.setD(np->value); // Double check we have valid values if (lng.Degrees() == 0 && lat.Degrees() ==0) { qCWarning(KSTARS_INDI) << "Ignoring invalid device coordinates."; return; } np = IUFindNumber(nvp, "ELEV"); if (np) elev = np->value; GeoLocation *geo = KStars::Instance()->data()->geo(); - if (geo->name() != i18n("GPS Location")) + QString newLocationName; + if (getDriverInterface() & INDI::BaseDevice::GPS_INTERFACE) + newLocationName = i18n("GPS Location"); + else + newLocationName = i18n("Mount Location"); + + if (geo->name() != newLocationName) { - double TZ = geo->TZ(); + double TZ0 = geo->TZ0(); TimeZoneRule *rule = geo->tzrule(); - geo = new GeoLocation(lng, lat, i18n("GPS Location"), "", "", TZ, rule, elev); + geo = new GeoLocation(lng, lat, newLocationName, "", "", TZ0, rule, elev); } else { geo->setLong(lng); geo->setLat(lat); } qCInfo(KSTARS_INDI) << "Setting location from device:" << getDeviceName() << "Longitude:" << lng.toDMSString() << "Latitude:" << lat.toDMSString(); KStars::Instance()->data()->setLocation(*geo); } else if (!strcmp(nvp->name, "WATCHDOG_HEARTBEAT")) { if (watchDogTimer == nullptr) { watchDogTimer = new QTimer(this); connect(watchDogTimer, SIGNAL(timeout()), this, SLOT(resetWatchdog())); } if (connected && nvp->np[0].value > 0) { // Reset timer 15 seconds before it is due watchDogTimer->start(nvp->np[0].value * 60 * 1000 - 15 * 1000); } else if (nvp->np[0].value == 0) watchDogTimer->stop(); } emit numberUpdated(nvp); } void GenericDevice::processText(ITextVectorProperty *tvp) { // Update KStars time once we receive update from INDI, if the source is set to DEVICE if (!strcmp(tvp->name, "TIME_UTC") && tvp->s == IPS_OK && ( (Options::useMountSource() && (getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE)) || (Options::useGPSSource() && (getDriverInterface() & INDI::BaseDevice::GPS_INTERFACE)))) { IText *tp = nullptr; int d, m, y, min, sec, hour; float utcOffset; QDate indiDate; QTime indiTime; KStarsDateTime indiDateTime; tp = IUFindText(tvp, "UTC"); if (!tp) return; sscanf(tp->text, "%d%*[^0-9]%d%*[^0-9]%dT%d%*[^0-9]%d%*[^0-9]%d", &y, &m, &d, &hour, &min, &sec); indiDate.setDate(y, m, d); indiTime.setHMS(hour, min, sec); indiDateTime.setDate(indiDate); indiDateTime.setTime(indiTime); tp = IUFindText(tvp, "OFFSET"); if (!tp) return; sscanf(tp->text, "%f", &utcOffset); qCInfo(KSTARS_INDI) << "Setting UTC time from device:" << getDeviceName() << indiDateTime.toString(); KStars::Instance()->data()->changeDateTime(indiDateTime); KStars::Instance()->data()->syncLST(); GeoLocation *geo = KStars::Instance()->data()->geo(); - geo->setTZ(utcOffset); + if (geo->tzrule()) + utcOffset -= geo->tzrule()->deltaTZ(); + + // TZ0 is the timezone WTIHOUT any DST offsets. Above, we take INDI UTC Offset (with DST already included) + // and subtract from it the deltaTZ from the current TZ rule. + geo->setTZ0(utcOffset); } emit textUpdated(tvp); } void GenericDevice::processLight(ILightVectorProperty *lvp) { emit lightUpdated(lvp); } void GenericDevice::processMessage(int messageID) { emit messageUpdated(messageID); } void GenericDevice::processBLOB(IBLOB *bp) { // Ignore write-only BLOBs since we only receive it for state-change if (bp->bvp->p == IP_WO) return; QFile *data_file = nullptr; INDIDataTypes dataType; if (!strcmp(bp->format, ".ascii")) dataType = DATA_ASCII; else dataType = DATA_OTHER; QString currentDir = Options::fitsDir(); int nr, n = 0; if (currentDir.endsWith('/')) currentDir.truncate(sizeof(currentDir) - 1); QString filename(currentDir + '/'); QString ts = QDateTime::currentDateTime().toString("yyyy-MM-ddThh-mm-ss"); filename += QString("%1_").arg(bp->label) + ts + QString(bp->format).trimmed(); strncpy(BLOBFilename, filename.toLatin1(), MAXINDIFILENAME); bp->aux2 = BLOBFilename; if (dataType == DATA_ASCII) { if (bp->aux0 == nullptr) { bp->aux0 = new int(); QFile *ascii_data_file = new QFile(); ascii_data_file->setFileName(filename); if (!ascii_data_file->open(QIODevice::WriteOnly)) { qCCritical(KSTARS_INDI) << "GenericDevice Error: Unable to open " << ascii_data_file->fileName() << endl; return; } bp->aux1 = ascii_data_file; } data_file = (QFile *)bp->aux1; QDataStream out(data_file); for (nr = 0; nr < (int)bp->size; nr += n) n = out.writeRawData(static_cast(bp->blob) + nr, bp->size - nr); out.writeRawData((const char *)"\n", 1); data_file->flush(); } else { QFile fits_temp_file(filename); if (!fits_temp_file.open(QIODevice::WriteOnly)) { qCCritical(KSTARS_INDI) << "GenericDevice Error: Unable to open " << fits_temp_file.fileName() << endl; return; } QDataStream out(&fits_temp_file); for (nr = 0; nr < (int)bp->size; nr += n) n = out.writeRawData(static_cast(bp->blob) + nr, bp->size - nr); fits_temp_file.close(); QByteArray fmt = QString(bp->format).toLower().remove('.').toUtf8(); if (QImageReader::supportedImageFormats().contains(fmt)) { QUrl url(filename); url.setScheme("file"); ImageViewer *iv = new ImageViewer(url, QString(), KStars::Instance()); if (iv) iv->show(); } } if (dataType == DATA_OTHER) KStars::Instance()->statusBar()->showMessage(i18n("Data file saved to %1", filename), 0); emit BLOBUpdated(bp); } bool GenericDevice::setConfig(INDIConfig tConfig) { ISwitchVectorProperty *svp = baseDevice->getSwitch("CONFIG_PROCESS"); if (svp == nullptr) return false; ISwitch *sp = nullptr; IUResetSwitch(svp); switch (tConfig) { case LOAD_LAST_CONFIG: sp = IUFindSwitch(svp, "CONFIG_LOAD"); if (sp == nullptr) return false; IUResetSwitch(svp); sp->s = ISS_ON; break; case SAVE_CONFIG: sp = IUFindSwitch(svp, "CONFIG_SAVE"); if (sp == nullptr) return false; IUResetSwitch(svp); sp->s = ISS_ON; break; case LOAD_DEFAULT_CONFIG: sp = IUFindSwitch(svp, "CONFIG_DEFAULT"); if (sp == nullptr) return false; IUResetSwitch(svp); sp->s = ISS_ON; break; } clientManager->sendNewSwitch(svp); return true; } void GenericDevice::createDeviceInit() { if (Options::showINDIMessages()) KStars::Instance()->statusBar()->showMessage(i18n("%1 is online.", baseDevice->getDeviceName()), 0); KStars::Instance()->map()->forceUpdateNow(); } /*********************************************************************************/ /* Update the Driver's Time */ /*********************************************************************************/ void GenericDevice::updateTime() { QString offset, isoTS; offset = QString().setNum(KStars::Instance()->data()->geo()->TZ(), 'g', 2); //QTime newTime( KStars::Instance()->data()->ut().time()); //QDate newDate( KStars::Instance()->data()->ut().date()); //isoTS = QString("%1-%2-%3T%4:%5:%6").arg(newDate.year()).arg(newDate.month()).arg(newDate.day()).arg(newTime.hour()).arg(newTime.minute()).arg(newTime.second()); isoTS = KStars::Instance()->data()->ut().toString(Qt::ISODate).remove('Z'); /* Update Date/Time */ ITextVectorProperty *timeUTC = baseDevice->getText("TIME_UTC"); if (timeUTC) { IText *timeEle = IUFindText(timeUTC, "UTC"); if (timeEle) IUSaveText(timeEle, isoTS.toLatin1().constData()); IText *offsetEle = IUFindText(timeUTC, "OFFSET"); if (offsetEle) IUSaveText(offsetEle, offset.toLatin1().constData()); if (timeEle && offsetEle) clientManager->sendNewText(timeUTC); } } /*********************************************************************************/ /* Update the Driver's Geographical Location */ /*********************************************************************************/ void GenericDevice::updateLocation() { GeoLocation *geo = KStars::Instance()->data()->geo(); double longNP; if (geo->lng()->Degrees() >= 0) longNP = geo->lng()->Degrees(); else longNP = dms(geo->lng()->Degrees() + 360.0).Degrees(); INumberVectorProperty *nvp = baseDevice->getNumber("GEOGRAPHIC_COORD"); if (nvp == nullptr) return; INumber *np = IUFindNumber(nvp, "LONG"); if (np == nullptr) return; np->value = longNP; np = IUFindNumber(nvp, "LAT"); if (np == nullptr) return; np->value = geo->lat()->Degrees(); np = IUFindNumber(nvp, "ELEV"); if (np == nullptr) return; np->value = geo->elevation(); clientManager->sendNewNumber(nvp); } bool GenericDevice::Connect() { return runCommand(INDI_CONNECT, nullptr); } bool GenericDevice::Disconnect() { return runCommand(INDI_DISCONNECT, nullptr); } bool GenericDevice::runCommand(int command, void *ptr) { switch (command) { case INDI_CONNECT: clientManager->connectDevice(baseDevice->getDeviceName()); break; case INDI_DISCONNECT: clientManager->disconnectDevice(baseDevice->getDeviceName()); break; case INDI_SET_PORT: { if (ptr == nullptr) return false; ITextVectorProperty *tvp = baseDevice->getText("DEVICE_PORT"); if (tvp == nullptr) return false; IText *tp = IUFindText(tvp, "PORT"); IUSaveText(tp, (static_cast(ptr))->toLatin1().constData()); clientManager->sendNewText(tvp); } break; // We do it here because it could be either CCD or FILTER interfaces, so no need to duplicate code case INDI_SET_FILTER: { if (ptr == nullptr) return false; INumberVectorProperty *nvp = baseDevice->getNumber("FILTER_SLOT"); if (nvp == nullptr) return false; int requestedFilter = *((int *)ptr); if (requestedFilter == nvp->np[0].value) break; nvp->np[0].value = requestedFilter; clientManager->sendNewNumber(nvp); } break; // We do it here because it could be either FOCUSER or ROTATOR interfaces, so no need to duplicate code case INDI_SET_ROTATOR_ANGLE: { if (ptr == nullptr) return false; INumberVectorProperty *nvp = baseDevice->getNumber("ABS_ROTATOR_ANGLE"); if (nvp == nullptr) return false; double requestedAngle = *((double *)ptr); if (requestedAngle == nvp->np[0].value) break; nvp->np[0].value = requestedAngle; clientManager->sendNewNumber(nvp); } break; // We do it here because it could be either FOCUSER or ROTATOR interfaces, so no need to duplicate code case INDI_SET_ROTATOR_TICKS: { if (ptr == nullptr) return false; INumberVectorProperty *nvp = baseDevice->getNumber("ABS_ROTATOR_POSITION"); if (nvp == nullptr) return false; int32_t requestedTicks = *((int32_t *)ptr); if (requestedTicks == nvp->np[0].value) break; nvp->np[0].value = requestedTicks; clientManager->sendNewNumber(nvp); } break; } return true; } bool GenericDevice::setProperty(QObject *setPropCommand) { GDSetCommand *indiCommand = static_cast(setPropCommand); //qDebug() << "We are trying to set value for property " << indiCommand->indiProperty << " and element" << indiCommand->indiElement << " and value " << indiCommand->elementValue << endl; INDI::Property *pp = baseDevice->getProperty(indiCommand->indiProperty.toLatin1().constData()); if (pp == nullptr) return false; switch (indiCommand->propType) { case INDI_SWITCH: { ISwitchVectorProperty *svp = pp->getSwitch(); if (svp == nullptr) return false; ISwitch *sp = IUFindSwitch(svp, indiCommand->indiElement.toLatin1().constData()); if (sp == nullptr) return false; if (svp->r == ISR_1OFMANY || svp->r == ISR_ATMOST1) IUResetSwitch(svp); sp->s = indiCommand->elementValue.toInt() == 0 ? ISS_OFF : ISS_ON; //qDebug() << "Sending switch " << sp->name << " with status " << ((sp->s == ISS_ON) ? "On" : "Off") << endl; clientManager->sendNewSwitch(svp); return true; } break; case INDI_NUMBER: { INumberVectorProperty *nvp = pp->getNumber(); if (nvp == nullptr) return false; INumber *np = IUFindNumber(nvp, indiCommand->indiElement.toLatin1().constData()); if (np == nullptr) return false; double value = indiCommand->elementValue.toDouble(); if (value == np->value) return true; np->value = value; //qDebug() << "Sending switch " << sp->name << " with status " << ((sp->s == ISS_ON) ? "On" : "Off") << endl; clientManager->sendNewNumber(nvp); } break; // TODO: Add set property for other types of properties default: break; } return true; } bool GenericDevice::getMinMaxStep(const QString &propName, const QString &elementName, double *min, double *max, double *step) { INumberVectorProperty *nvp = baseDevice->getNumber(propName.toLatin1()); if (nvp == nullptr) return false; INumber *np = IUFindNumber(nvp, elementName.toLatin1()); if (np == nullptr) return false; *min = np->min; *max = np->max; *step = np->step; return true; } IPState GenericDevice::getState(const QString &propName) { return baseDevice->getPropertyState(propName.toLatin1().constData()); } IPerm GenericDevice::getPermission(const QString &propName) { return baseDevice->getPropertyPermission(propName.toLatin1().constData()); } INDI::Property *GenericDevice::getProperty(const QString &propName) { for (auto &oneProp : properties) { if (propName == QString(oneProp->getName())) return oneProp; } return nullptr; } void GenericDevice::resetWatchdog() { INumberVectorProperty *nvp = baseDevice->getNumber("WATCHDOG_HEARTBEAT"); if (nvp) // Send heartbeat to driver clientManager->sendNewNumber(nvp); } DeviceDecorator::DeviceDecorator(GDInterface *iPtr) { interfacePtr = iPtr; connect(iPtr, SIGNAL(Connected()), this, SIGNAL(Connected())); connect(iPtr, SIGNAL(Disconnected()), this, SIGNAL(Disconnected())); connect(iPtr, SIGNAL(propertyDefined(INDI::Property*)), this, SIGNAL(propertyDefined(INDI::Property*))); connect(iPtr, SIGNAL(propertyDeleted(INDI::Property*)), this, SIGNAL(propertyDeleted(INDI::Property*))); connect(iPtr, SIGNAL(messageUpdated(int)), this, SIGNAL(messageUpdated(int))); connect(iPtr, SIGNAL(switchUpdated(ISwitchVectorProperty*)), this, SIGNAL(switchUpdated(ISwitchVectorProperty*))); connect(iPtr, SIGNAL(numberUpdated(INumberVectorProperty*)), this, SIGNAL(numberUpdated(INumberVectorProperty*))); connect(iPtr, SIGNAL(textUpdated(ITextVectorProperty*)), this, SIGNAL(textUpdated(ITextVectorProperty*))); connect(iPtr, SIGNAL(BLOBUpdated(IBLOB*)), this, SIGNAL(BLOBUpdated(IBLOB*))); connect(iPtr, SIGNAL(lightUpdated(ILightVectorProperty*)), this, SIGNAL(lightUpdated(ILightVectorProperty*))); baseDevice = interfacePtr->getBaseDevice(); clientManager = interfacePtr->getDriverInfo()->getClientManager(); } DeviceDecorator::~DeviceDecorator() { delete (interfacePtr); } bool DeviceDecorator::runCommand(int command, void *ptr) { return interfacePtr->runCommand(command, ptr); } bool DeviceDecorator::setProperty(QObject *setPropCommand) { return interfacePtr->setProperty(setPropCommand); } void DeviceDecorator::processBLOB(IBLOB *bp) { interfacePtr->processBLOB(bp); } void DeviceDecorator::processLight(ILightVectorProperty *lvp) { interfacePtr->processLight(lvp); } void DeviceDecorator::processNumber(INumberVectorProperty *nvp) { interfacePtr->processNumber(nvp); } void DeviceDecorator::processSwitch(ISwitchVectorProperty *svp) { interfacePtr->processSwitch(svp); } void DeviceDecorator::processText(ITextVectorProperty *tvp) { interfacePtr->processText(tvp); } void DeviceDecorator::processMessage(int messageID) { interfacePtr->processMessage(messageID); } void DeviceDecorator::registerProperty(INDI::Property *prop) { interfacePtr->registerProperty(prop); } void DeviceDecorator::removeProperty(INDI::Property *prop) { interfacePtr->removeProperty(prop); } bool DeviceDecorator::setConfig(INDIConfig tConfig) { return interfacePtr->setConfig(tConfig); } DeviceFamily DeviceDecorator::getType() { return interfacePtr->getType(); } DriverInfo *DeviceDecorator::getDriverInfo() { return interfacePtr->getDriverInfo(); } DeviceInfo *DeviceDecorator::getDeviceInfo() { return interfacePtr->getDeviceInfo(); } const char *DeviceDecorator::getDeviceName() { return interfacePtr->getDeviceName(); } INDI::BaseDevice *DeviceDecorator::getBaseDevice() { return interfacePtr->getBaseDevice(); } uint32_t DeviceDecorator::getDriverInterface() { return interfacePtr->getDriverInterface(); } QList DeviceDecorator::getProperties() { return interfacePtr->getProperties(); } INDI::Property *DeviceDecorator::getProperty(const QString &propName) { return interfacePtr->getProperty(propName); } bool DeviceDecorator::isConnected() { return interfacePtr->isConnected(); } bool DeviceDecorator::Connect() { return interfacePtr->Connect(); } bool DeviceDecorator::Disconnect() { return interfacePtr->Disconnect(); } bool DeviceDecorator::getMinMaxStep(const QString &propName, const QString &elementName, double *min, double *max, double *step) { return interfacePtr->getMinMaxStep(propName, elementName, min, max, step); } IPState DeviceDecorator::getState(const QString &propName) { return interfacePtr->getState(propName); } IPerm DeviceDecorator::getPermission(const QString &propName) { return interfacePtr->getPermission(propName); } ST4::ST4(INDI::BaseDevice *bdv, ClientManager *cm) { baseDevice = bdv; clientManager = cm; } ST4::~ST4() { } const char *ST4::getDeviceName() { return baseDevice->getDeviceName(); } void ST4::setDECSwap(bool enable) { swapDEC = enable; } bool ST4::doPulse(GuideDirection ra_dir, int ra_msecs, GuideDirection dec_dir, int dec_msecs) { bool raOK = false, decOK = false; raOK = doPulse(ra_dir, ra_msecs); decOK = doPulse(dec_dir, dec_msecs); if (raOK && decOK) return true; else return false; } bool ST4::doPulse(GuideDirection dir, int msecs) { INumberVectorProperty *raPulse = baseDevice->getNumber("TELESCOPE_TIMED_GUIDE_WE"); INumberVectorProperty *decPulse = baseDevice->getNumber("TELESCOPE_TIMED_GUIDE_NS"); INumberVectorProperty *npulse = nullptr; INumber *dirPulse = nullptr; if (raPulse == nullptr || decPulse == nullptr) return false; if (dir == RA_INC_DIR || dir == RA_DEC_DIR) raPulse->np[0].value = raPulse->np[1].value = 0; else decPulse->np[0].value = decPulse->np[1].value = 0; switch (dir) { case RA_INC_DIR: dirPulse = IUFindNumber(raPulse, "TIMED_GUIDE_W"); if (dirPulse == nullptr) return false; npulse = raPulse; break; case RA_DEC_DIR: dirPulse = IUFindNumber(raPulse, "TIMED_GUIDE_E"); if (dirPulse == nullptr) return false; npulse = raPulse; break; case DEC_INC_DIR: if (swapDEC) dirPulse = IUFindNumber(decPulse, "TIMED_GUIDE_S"); else dirPulse = IUFindNumber(decPulse, "TIMED_GUIDE_N"); if (dirPulse == nullptr) return false; npulse = decPulse; break; case DEC_DEC_DIR: if (swapDEC) dirPulse = IUFindNumber(decPulse, "TIMED_GUIDE_N"); else dirPulse = IUFindNumber(decPulse, "TIMED_GUIDE_S"); if (dirPulse == nullptr) return false; npulse = decPulse; break; default: return false; } dirPulse->value = msecs; clientManager->sendNewNumber(npulse); //qDebug() << "Sending pulse for " << npulse->name << " in direction " << dirPulse->name << " for " << msecs << " ms " << endl; return true; } } diff --git a/kstars/kstarsinit.cpp b/kstars/kstarsinit.cpp index 22e74701a..3e5fd5540 100644 --- a/kstars/kstarsinit.cpp +++ b/kstars/kstarsinit.cpp @@ -1,925 +1,928 @@ /*************************************************************************** kstarsinit.cpp - K Desktop Planetarium ------------------- begin : Mon Feb 25 2002 copyright : (C) 2002 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kstars.h" +#include "kstars_debug.h" #include "fov.h" #include "kspaths.h" #include "kstarsdata.h" #include "Options.h" #include "skymap.h" #include "texturemanager.h" #include "projections/projector.h" #include "skycomponents/skymapcomposite.h" #include "skyobjects/ksplanetbase.h" #include "widgets/timespinbox.h" #include "widgets/timestepbox.h" #include "hips/hipsmanager.h" #include "auxiliary/thememanager.h" #ifdef HAVE_INDI #include "indi/drivermanager.h" #include "indi/guimanager.h" #include "ekos/ekosmanager.h" #endif #include #include #include #include #include #include #include #include //This file contains functions that kstars calls at startup (except constructors). //These functions are declared in kstars.h namespace { // A lot of QAction is defined there. In order to decrease amount // of boilerplate code a trick with << operator overloading is used. // This makes code more concise and readable. // // When data type could not used directly. Either because of // overloading rules or because one data type have different // semantics its wrapped into struct. // // Downside is unfamiliar syntax and really unhelpful error // messages due to general abuse of << overloading // Set QAction text QAction *operator<<(QAction *ka, QString text) { ka->setText(text); return ka; } // Set icon for QAction QAction *operator<<(QAction *ka, const QIcon &icon) { ka->setIcon(icon); return ka; } // Set keyboard shortcut QAction *operator<<(QAction *ka, const QKeySequence sh) { KStars::Instance()->actionCollection()->setDefaultShortcut(ka, sh); //ka->setShortcut(sh); return ka; } // Add action to group. AddToGroup struct acts as newtype wrapper // in order to allow overloading. struct AddToGroup { QActionGroup *grp; AddToGroup(QActionGroup *g) : grp(g) {} }; QAction *operator<<(QAction *ka, AddToGroup g) { g.grp->addAction(ka); return ka; } // Set checked property. Checked is newtype wrapper. struct Checked { bool flag; Checked(bool f) : flag(f) {} }; QAction *operator<<(QAction *ka, Checked chk) { ka->setCheckable(true); ka->setChecked(chk.flag); return ka; } // Set tool tip. ToolTip is used as newtype wrapper. struct ToolTip { QString tip; ToolTip(QString msg) : tip(msg) {} }; QAction *operator<<(QAction *ka, const ToolTip &tool) { ka->setToolTip(tool.tip); return ka; } // Create new KToggleAction and connect slot to toggled(bool) signal QAction *newToggleAction(KActionCollection *col, QString name, QString text, QObject *receiver, const char *member) { QAction *ka = col->add(name) << text; QObject::connect(ka, SIGNAL(toggled(bool)), receiver, member); return ka; } } void KStars::initActions() { // Check if we have this specific Breeze icon. If not, try to set the theme search path and if appropriate, the icon theme rcc file // in each OS if (!QIcon::hasThemeIcon(QLatin1String("kstars_flag"))) KSTheme::Manager::instance()->setIconTheme(KSTheme::Manager::BREEZE_DARK_THEME); QAction *ka; // ==== File menu ================ ka = KNS3::standardAction(i18n("Download New Data..."), this, SLOT(slotDownload()), actionCollection(), "get_data") << QKeySequence(Qt::CTRL + Qt::Key_N); ka->setIcon(QIcon::fromTheme("favorites")); ka->setWhatsThis(i18n("Downloads new data")); ka->setToolTip(ka->whatsThis()); ka->setStatusTip(ka->whatsThis()); #ifdef HAVE_CFITSIO actionCollection()->addAction("open_file", this, SLOT(slotOpenFITS())) << i18n("Open FITS...") << QIcon::fromTheme("document-open") << QKeySequence(Qt::CTRL + Qt::Key_O); #endif actionCollection()->addAction("export_image", this, SLOT(slotExportImage())) << i18n("&Save Sky Image...") << QIcon::fromTheme("document-export-image"); // 2017-09-17 Jasem: FIXME! Scripting does not work properly under non UNIX systems. // It must be updated to use DBus session bus from Qt (like scheduler) #ifndef Q_OS_WIN actionCollection()->addAction("run_script", this, SLOT(slotRunScript())) << i18n("&Run Script...") << QIcon::fromTheme("system-run") << QKeySequence(Qt::CTRL + Qt::Key_R); #endif actionCollection()->addAction("printing_wizard", this, SLOT(slotPrintingWizard())) << i18nc("start Printing Wizard", "Printing &Wizard"); ka = actionCollection()->addAction(KStandardAction::Print, "print", this, SLOT(slotPrint())); ka->setIcon(QIcon::fromTheme("document-print")); //actionCollection()->addAction( KStandardAction::Quit, "quit", this, SLOT(close) ); ka = actionCollection()->addAction(KStandardAction::Quit, "quit", qApp, SLOT(closeAllWindows())); ka->setIcon(QIcon::fromTheme("application-exit")); // ==== Time Menu ================ actionCollection()->addAction("time_to_now", this, SLOT(slotSetTimeToNow())) << i18n("Set Time to &Now") << QKeySequence(Qt::CTRL + Qt::Key_E) << QIcon::fromTheme("clock"); actionCollection()->addAction("time_dialog", this, SLOT(slotSetTime())) << i18nc("set Clock to New Time", "&Set Time...") << QKeySequence(Qt::CTRL + Qt::Key_S) << QIcon::fromTheme("clock"); ka = actionCollection()->add("clock_startstop") << i18n("Stop &Clock") << QIcon::fromTheme("media-playback-pause"); if (!StartClockRunning) ka->toggle(); QObject::connect(ka, SIGNAL(triggered()), this, SLOT(slotToggleTimer())); //QObject::connect(data()->clock(), SIGNAL(clockToggled(bool)), ka, SLOT(setChecked(bool))); QObject::connect(data()->clock(), &SimClock::clockToggled, [=](bool toggled) { QAction *a = actionCollection()->action("clock_startstop"); if (a) { a->setChecked(toggled); // Many users forget to unpause KStars, so we are using run-build-install-root icon which is red // and stands out from the rest of the icons so users are aware when KStars is paused visually a->setIcon(toggled ? QIcon::fromTheme("run-build-install-root") : QIcon::fromTheme("media-playback-pause")); a->setToolTip(toggled ? i18n("Resume Clock") : i18n("Stop Clock")); } }); //UpdateTime() if clock is stopped (so hidden objects get drawn) QObject::connect(data()->clock(), SIGNAL(clockToggled(bool)), this, SLOT(updateTime())); actionCollection()->addAction("time_step_forward", this, SLOT(slotStepForward())) << i18n("Advance one step forward in time") << QIcon::fromTheme("media-skip-forward") << QKeySequence(Qt::Key_Greater); actionCollection()->addAction("time_step_backward", this, SLOT(slotStepBackward())) << i18n("Advance one step backward in time") << QIcon::fromTheme("media-skip-backward") << QKeySequence(Qt::Key_Less); // ==== Pointing Menu ================ actionCollection()->addAction("zenith", this, SLOT(slotPointFocus())) << i18n("&Zenith") << QKeySequence("Z"); actionCollection()->addAction("north", this, SLOT(slotPointFocus())) << i18n("&North") << QKeySequence("N"); actionCollection()->addAction("east", this, SLOT(slotPointFocus())) << i18n("&East") << QKeySequence("E"); actionCollection()->addAction("south", this, SLOT(slotPointFocus())) << i18n("&South") << QKeySequence("S"); actionCollection()->addAction("west", this, SLOT(slotPointFocus())) << i18n("&West") << QKeySequence("W"); actionCollection()->addAction("find_object", this, SLOT(slotFind())) << i18n("&Find Object...") << QIcon::fromTheme("edit-find") << QKeySequence(Qt::CTRL + Qt::Key_F); actionCollection()->addAction("track_object", this, SLOT(slotTrack())) << i18n("Engage &Tracking") << QIcon::fromTheme("object-locked") << QKeySequence(Qt::CTRL + Qt::Key_T); actionCollection()->addAction("manual_focus", this, SLOT(slotManualFocus())) << i18n("Set Coordinates &Manually...") << QKeySequence(Qt::CTRL + Qt::Key_M); QAction *action; // ==== View Menu ================ action = actionCollection()->addAction(KStandardAction::ZoomIn, "zoom_in", map(), SLOT(slotZoomIn())); action->setIcon(QIcon::fromTheme("zoom-in")); action = actionCollection()->addAction(KStandardAction::ZoomOut, "zoom_out", map(), SLOT(slotZoomOut())); action->setIcon(QIcon::fromTheme("zoom-out")); actionCollection()->addAction("zoom_default", map(), SLOT(slotZoomDefault())) << i18n("&Default Zoom") << QIcon::fromTheme("zoom-fit-best") << QKeySequence(Qt::CTRL + Qt::Key_Z); actionCollection()->addAction("zoom_set", this, SLOT(slotSetZoom())) << i18n("&Zoom to Angular Size...") << QIcon::fromTheme("zoom-original") << QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Z); action = actionCollection()->addAction(KStandardAction::FullScreen, this, SLOT(slotFullScreen())); action->setIcon(QIcon::fromTheme("view-fullscreen")); actionCollection()->addAction("coordsys", this, SLOT(slotCoordSys())) << (Options::useAltAz() ? i18n("Switch to star globe view (Equatorial &Coordinates)") : i18n("Switch to horizonal view (Horizontal &Coordinates)")) << QKeySequence("Space"); actionCollection()->addAction("project_lambert", this, SLOT(slotMapProjection())) << i18n("&Lambert Azimuthal Equal-area") << QKeySequence("F5") << AddToGroup(projectionGroup) << Checked(Options::projection() == Projector::Lambert); actionCollection()->addAction("project_azequidistant", this, SLOT(slotMapProjection())) << i18n("&Azimuthal Equidistant") << QKeySequence("F6") << AddToGroup(projectionGroup) << Checked(Options::projection() == Projector::AzimuthalEquidistant); actionCollection()->addAction("project_orthographic", this, SLOT(slotMapProjection())) << i18n("&Orthographic") << QKeySequence("F7") << AddToGroup(projectionGroup) << Checked(Options::projection() == Projector::Orthographic); actionCollection()->addAction("project_equirectangular", this, SLOT(slotMapProjection())) << i18n("&Equirectangular") << QKeySequence("F8") << AddToGroup(projectionGroup) << Checked(Options::projection() == Projector::Equirectangular); actionCollection()->addAction("project_stereographic", this, SLOT(slotMapProjection())) << i18n("&Stereographic") << QKeySequence("F9") << AddToGroup(projectionGroup) << Checked(Options::projection() == Projector::Stereographic); actionCollection()->addAction("project_gnomonic", this, SLOT(slotMapProjection())) << i18n("&Gnomonic") << QKeySequence("F10") << AddToGroup(projectionGroup) << Checked(Options::projection() == Projector::Gnomonic); //Settings Menu: //Info Boxes option actions QAction *kaBoxes = actionCollection()->add("show_boxes") << i18nc("Show the information boxes", "Show &Info Boxes") << Checked(Options::showInfoBoxes()); connect(kaBoxes, SIGNAL(toggled(bool)), map(), SLOT(slotToggleInfoboxes(bool))); kaBoxes->setChecked(Options::showInfoBoxes()); ka = actionCollection()->add("show_time_box") << i18nc("Show time-related info box", "Show &Time Box"); connect(kaBoxes, SIGNAL(toggled(bool)), ka, SLOT(setEnabled(bool))); connect(ka, SIGNAL(toggled(bool)), map(), SLOT(slotToggleTimeBox(bool))); ka->setChecked(Options::showTimeBox()); ka->setEnabled(Options::showInfoBoxes()); ka = actionCollection()->add("show_focus_box") << i18nc("Show focus-related info box", "Show &Focus Box"); connect(kaBoxes, SIGNAL(toggled(bool)), ka, SLOT(setEnabled(bool))); connect(ka, SIGNAL(toggled(bool)), map(), SLOT(slotToggleFocusBox(bool))); ka->setChecked(Options::showFocusBox()); ka->setEnabled(Options::showInfoBoxes()); ka = actionCollection()->add("show_location_box") << i18nc("Show location-related info box", "Show &Location Box"); connect(kaBoxes, SIGNAL(toggled(bool)), ka, SLOT(setEnabled(bool))); connect(ka, SIGNAL(toggled(bool)), map(), SLOT(slotToggleGeoBox(bool))); ka->setChecked(Options::showGeoBox()); ka->setEnabled(Options::showInfoBoxes()); //Toolbar options newToggleAction(actionCollection(), "show_mainToolBar", i18n("Show Main Toolbar"), toolBar("kstarsToolBar"), SLOT(setVisible(bool))); newToggleAction(actionCollection(), "show_viewToolBar", i18n("Show View Toolbar"), toolBar("viewToolBar"), SLOT(setVisible(bool))); //Statusbar view options newToggleAction(actionCollection(), "show_statusBar", i18n("Show Statusbar"), this, SLOT(slotShowGUIItem(bool))); newToggleAction(actionCollection(), "show_sbAzAlt", i18n("Show Az/Alt Field"), this, SLOT(slotShowGUIItem(bool))); newToggleAction(actionCollection(), "show_sbRADec", i18n("Show RA/Dec Field"), this, SLOT(slotShowGUIItem(bool))); newToggleAction(actionCollection(), "show_sbJ2000RADec", i18n("Show J2000.0 RA/Dec Field"), this, SLOT(slotShowGUIItem(bool))); populateThemes(); //Color scheme actions. These are added to the "colorschemes" KActionMenu. colorActionMenu = actionCollection()->add("colorschemes"); colorActionMenu->setText(i18n("C&olor Schemes")); addColorMenuItem(i18n("&Classic"), "cs_classic"); addColorMenuItem(i18n("&Star Chart"), "cs_chart"); addColorMenuItem(i18n("&Night Vision"), "cs_night"); addColorMenuItem(i18n("&Moonless Night"), "cs_moonless-night"); //Add any user-defined color schemes: QFile file(KSPaths::locate(QStandardPaths::GenericDataLocation, "colors.dat")); //determine filename in local user KDE directory tree. if (file.exists() && file.open(QIODevice::ReadOnly)) { QTextStream stream(&file); while (!stream.atEnd()) { QString line = stream.readLine(); QString schemeName = line.left(line.indexOf(':')); QString actionname = "cs_" + line.mid(line.indexOf(':') + 1, line.indexOf('.') - line.indexOf(':') - 1); addColorMenuItem(i18n(schemeName.toLocal8Bit()), actionname.toLocal8Bit()); } file.close(); } //Add FOV Symbol actions fovActionMenu = actionCollection()->add("fovsymbols"); fovActionMenu->setText(i18n("&FOV Symbols")); fovActionMenu->setDelayed(false); fovActionMenu->setIcon(QIcon::fromTheme("crosshairs")); FOVManager::readFOVs(); repopulateFOV(); //Add HIPS Sources actions hipsActionMenu = actionCollection()->add("hipssources"); hipsActionMenu->setText(i18n("HiPS All Sky Overlay (Experimental)")); hipsActionMenu->setDelayed(false); hipsActionMenu->setIcon(QIcon::fromTheme("view-preview")); HIPSManager::Instance()->readSources(); repopulateHIPS(); actionCollection()->addAction("geolocation", this, SLOT(slotGeoLocator())) << i18nc("Location on Earth", "&Geographic...") << QIcon::fromTheme("kstars_xplanet") << QKeySequence(Qt::CTRL + Qt::Key_G); // Configure Notifications #ifdef HAVE_NOTIFYCONFIG KStandardAction::configureNotifications(this, SLOT(slotConfigureNotifications()), actionCollection()); #endif ka = actionCollection()->addAction(KStandardAction::Preferences, "configure", this, SLOT(slotViewOps())); //I am not sure what icon preferences is supposed to be. //ka->setIcon( QIcon::fromTheme("")); actionCollection()->addAction("startwizard", this, SLOT(slotWizard())) << i18n("Startup Wizard...") << QIcon::fromTheme("tools-wizard"); // Manual data entry actionCollection()->addAction("manual_add_dso", this, SLOT(slotAddDeepSkyObject())) << i18n("Manually add a deep-sky object"); // Updates actions actionCollection()->addAction("update_comets", this, SLOT(slotUpdateComets())) << i18n("Update comets orbital elements"); actionCollection()->addAction("update_asteroids", this, SLOT(slotUpdateAsteroids())) << i18n("Update asteroids orbital elements"); actionCollection()->addAction("update_supernovae", this, SLOT(slotUpdateSupernovae())) << i18n("Update Recent Supernovae data"); actionCollection()->addAction("update_satellites", this, SLOT(slotUpdateSatellites())) << i18n("Update satellites orbital elements"); //Tools Menu: actionCollection()->addAction("astrocalculator", this, SLOT(slotCalculator())) << i18n("Calculator") << QIcon::fromTheme("accessories-calculator") << QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_C); /* FIXME Enable once port to KF5 is complete for moonphasetool actionCollection()->addAction("moonphasetool", this, SLOT( slotMoonPhaseTool() ) ) << i18n("Moon Phase Calendar"); */ actionCollection()->addAction("obslist", this, SLOT(slotObsList())) << i18n("Observation Planner") << QKeySequence(Qt::CTRL + Qt::Key_L); actionCollection()->addAction("altitude_vs_time", this, SLOT(slotAVT())) << i18n("Altitude vs. Time") << QKeySequence(Qt::CTRL + Qt::Key_A); actionCollection()->addAction("whats_up_tonight", this, SLOT(slotWUT())) << i18n("What's up Tonight") << QKeySequence(Qt::CTRL + Qt::Key_U); //FIXME Port to QML2 //#if 0 actionCollection()->addAction("whats_interesting", this, SLOT(slotToggleWIView())) << i18n("What's Interesting...") << QKeySequence(Qt::CTRL + Qt::Key_W); //#endif actionCollection()->addAction("skycalendar", this, SLOT(slotCalendar())) << i18n("Sky Calendar"); #ifdef HAVE_INDI ka = actionCollection()->addAction("ekos", this, SLOT(slotEkos())) << i18n("Ekos") << QKeySequence(Qt::CTRL + Qt::Key_K); ka->setShortcutContext(Qt::ApplicationShortcut); #endif //FIXME: implement glossary // ka = actionCollection()->addAction("glossary"); // ka->setText( i18n("Glossary...") ); // ka->setShortcuts( QKeySequence(Qt::CTRL+Qt::Key_K ) ); // connect( ka, SIGNAL( triggered() ), this, SLOT( slotGlossary() ) ); // 2017-09-17 Jasem: FIXME! Scripting does not work properly under non UNIX systems. // It must be updated to use DBus session bus from Qt (like scheduler) #ifndef Q_OS_WIN actionCollection()->addAction("scriptbuilder", this, SLOT(slotScriptBuilder())) << i18n("Script Builder") << QKeySequence(Qt::CTRL + Qt::Key_B); #endif actionCollection()->addAction("solarsystem", this, SLOT(slotSolarSystem())) << i18n("Solar System") << QKeySequence(Qt::CTRL + Qt::Key_Y); // Disabled until fixed later /*actionCollection()->addAction("jmoontool", this, SLOT( slotJMoonTool() ) ) << i18n("Jupiter's Moons") << QKeySequence(Qt::CTRL+Qt::Key_J );*/ actionCollection()->addAction("flagmanager", this, SLOT(slotFlagManager())) << i18n("Flags"); actionCollection()->addAction("equipmentwriter", this, SLOT(slotEquipmentWriter())) << i18n("List your &Equipment...") << QKeySequence(Qt::CTRL + Qt::Key_0); actionCollection()->addAction("manageobserver", this, SLOT(slotObserverManager())) << i18n("Manage Observer...") << QKeySequence(Qt::CTRL + Qt::Key_1); //TODO only enable it when finished actionCollection()->addAction("artificialhorizon", this, SLOT(slotHorizonManager())) << i18n("Artificial Horizon..."); // ==== observation menu - execute ================ ka = actionCollection()->addAction("execute", this, SLOT(slotExecute())) << i18n("Execute the session Plan...") << QKeySequence(Qt::CTRL + Qt::Key_2); // ==== observation menu - polaris hour angle ================ ka = actionCollection()->addAction("polaris_hour_angle", this, SLOT(slotPolarisHourAngle())) << i18n("Polaris Hour Angle..."); // ==== devices Menu ================ #ifdef HAVE_INDI #ifndef Q_OS_WIN actionCollection()->addAction("telescope_wizard", this, SLOT(slotTelescopeWizard())) << i18n("Telescope Wizard...") << QIcon::fromTheme("tools-wizard"); #endif actionCollection()->addAction("device_manager", this, SLOT(slotINDIDriver())) << i18n("Device Manager...") << QIcon::fromTheme("network-server") << QKeySequence(Qt::SHIFT + Qt::META + Qt::Key_D); ka = actionCollection()->addAction("indi_cpl", this, SLOT(slotINDIPanel())) << i18n("INDI Control Panel...") << QKeySequence(Qt::CTRL + Qt::Key_I); ka->setShortcutContext(Qt::ApplicationShortcut); ka->setEnabled(false); #else //FIXME need to disable/hide devices submenu in the tools menu. It is created from the kstarsui.rc file //but I don't know how to hide/disable it yet. menuBar()->findChildren() does not return any children that I can //iterate over. Anyway to resolve this? #endif //Help Menu: ka = actionCollection()->addAction(KStandardAction::TipofDay, "help_tipofday", this, SLOT(slotTipOfDay())); ka->setWhatsThis(i18n("Displays the Tip of the Day")); ka->setIcon(QIcon::fromTheme("help-hint")); // KStandardAction::help(this, SLOT( appHelpActivated() ), actionCollection(), "help_contents" ); //Add timestep widget for toolbar m_TimeStepBox = new TimeStepBox(toolBar("kstarsToolBar")); // Add a tool tip to TimeStep describing the weird nature of time steps QString TSBToolTip = i18nc("Tooltip describing the nature of the time step control", "Use this to set the rate at which time in the simulation flows.\nFor time step \'X\' " "up to 10 minutes, time passes at the rate of \'X\' per second.\nFor time steps larger " "than 10 minutes, frames are displayed at an interval of \'X\'."); m_TimeStepBox->setToolTip(TSBToolTip); m_TimeStepBox->tsbox()->setToolTip(TSBToolTip); QWidgetAction *wa = new QWidgetAction(this); wa->setDefaultWidget(m_TimeStepBox); ka = actionCollection()->addAction("timestep_control", wa) << i18n("Time step control"); // ==== viewToolBar actions ================ actionCollection()->add("show_stars", this, SLOT(slotViewToolBar())) << i18nc("Toggle Stars in the display", "Stars") << QIcon::fromTheme("kstars_stars") << ToolTip(i18n("Toggle stars")); actionCollection()->add("show_deepsky", this, SLOT(slotViewToolBar())) << i18nc("Toggle Deep Sky Objects in the display", "Deep Sky") << QIcon::fromTheme("kstars_deepsky") << ToolTip(i18n("Toggle deep sky objects")); actionCollection()->add("show_planets", this, SLOT(slotViewToolBar())) << i18nc("Toggle Solar System objects in the display", "Solar System") << QIcon::fromTheme("kstars_planets") << ToolTip(i18n("Toggle Solar system objects")); actionCollection()->add("show_clines", this, SLOT(slotViewToolBar())) << i18nc("Toggle Constellation Lines in the display", "Const. Lines") << QIcon::fromTheme("kstars_clines") << ToolTip(i18n("Toggle constellation lines")); actionCollection()->add("show_cnames", this, SLOT(slotViewToolBar())) << i18nc("Toggle Constellation Names in the display", "Const. Names") << QIcon::fromTheme("kstars_cnames") << ToolTip(i18n("Toggle constellation names")); actionCollection()->add("show_cbounds", this, SLOT(slotViewToolBar())) << i18nc("Toggle Constellation Boundaries in the display", "C. Boundaries") << QIcon::fromTheme("kstars_cbound") << ToolTip(i18n("Toggle constellation boundaries")); actionCollection()->add("show_constellationart", this, SLOT(slotViewToolBar())) << xi18nc("Toggle Constellation Art in the display", "C. Art (BETA)") << QIcon::fromTheme("kstars_constellationart") << ToolTip(xi18n("Toggle constellation art (BETA)")); actionCollection()->add("show_mw", this, SLOT(slotViewToolBar())) << i18nc("Toggle Milky Way in the display", "Milky Way") << QIcon::fromTheme("kstars_mw") << ToolTip(i18n("Toggle milky way")); actionCollection()->add("show_equatorial_grid", this, SLOT(slotViewToolBar())) << i18nc("Toggle Equatorial Coordinate Grid in the display", "Equatorial coord. grid") << QIcon::fromTheme("kstars_grid") << ToolTip(i18n("Toggle equatorial coordinate grid")); actionCollection()->add("show_horizontal_grid", this, SLOT(slotViewToolBar())) << i18nc("Toggle Horizontal Coordinate Grid in the display", "Horizontal coord. grid") << QIcon::fromTheme("kstars_hgrid") << ToolTip(i18n("Toggle horizontal coordinate grid")); actionCollection()->add("show_horizon", this, SLOT(slotViewToolBar())) << i18nc("Toggle the opaque fill of the ground polygon in the display", "Ground") << QIcon::fromTheme("kstars_horizon") << ToolTip(i18n("Toggle opaque ground")); actionCollection()->add("show_flags", this, SLOT(slotViewToolBar())) << i18nc("Toggle flags in the display", "Flags") << QIcon::fromTheme("kstars_flag") << ToolTip(i18n("Toggle flags")); actionCollection()->add("show_satellites", this, SLOT(slotViewToolBar())) << i18nc("Toggle satellites in the display", "Satellites") << QIcon::fromTheme("kstars_satellites") << ToolTip(i18n("Toggle satellites")); actionCollection()->add("show_supernovae", this, SLOT(slotViewToolBar())) << i18nc("Toggle supernovae in the display", "Supernovae") << QIcon::fromTheme("kstars_supernovae") << ToolTip(i18n("Toggle supernovae")); actionCollection()->add("show_whatsinteresting", this, SLOT(slotToggleWIView())) << i18nc("Toggle What's Interesting", "What's Interesting") << QIcon::fromTheme("view-list-details") << ToolTip(i18n("Toggle What's Interesting")); #ifdef HAVE_INDI // ==== INDIToolBar actions ================ actionCollection()->add("show_ekos", this, SLOT(slotINDIToolBar())) << i18nc("Toggle Ekos in the display", "Ekos") << QIcon::fromTheme("kstars_ekos") << ToolTip(i18n("Toggle Ekos")); ka = actionCollection()->add("show_control_panel", this, SLOT(slotINDIToolBar())) << i18nc("Toggle the INDI Control Panel in the display", "INDI Control Panel") << QIcon::fromTheme("kstars_indi") << ToolTip(i18n("Toggle INDI Control Panel")); ka->setEnabled(false); ka = actionCollection()->add("show_fits_viewer", this, SLOT(slotINDIToolBar())) << i18nc("Toggle the FITS Viewer in the display", "FITS Viewer") << QIcon::fromTheme("kstars_fitsviewer") << ToolTip(i18n("Toggle FITS Viewer")); ka->setEnabled(false); ka = actionCollection()->add("show_sensor_fov", this, SLOT(slotINDIToolBar())) << i18nc("Toggle the sensor Field of View", "Sensor FOV") << QIcon::fromTheme("archive-extract") << ToolTip(i18n("Toggle Sensor FOV")); ka->setEnabled(false); ka->setChecked(Options::showSensorFOV()); ka = actionCollection()->add("show_mount_box", this, SLOT(slotINDIToolBar())) << i18nc("Toggle the Mount Control Panel", "Mount Control") << QIcon::fromTheme("draw-text") << ToolTip(i18n("Toggle Mount Control Panel")); telescopeGroup->addAction(ka); ka = actionCollection()->add("lock_telescope", this, SLOT(slotINDIToolBar())) << i18nc("Toggle the telescope center lock in display", "Center Telescope") << QIcon::fromTheme("center_telescope", QIcon(":/icons/center_telescope.svg")) << ToolTip(i18n("Toggle Lock Telescope Center")); telescopeGroup->addAction(ka); ka = actionCollection()->add("telescope_track", this, SLOT(slotINDITelescopeTrack())) << i18n("Toggle Telescope Tracking") << QIcon::fromTheme("object-locked"); telescopeGroup->addAction(ka); ka = actionCollection()->addAction("telescope_slew", this, SLOT(slotINDITelescopeSlew())) << i18n("Slew telescope to the focused object") << QIcon::fromTheme("object-rotate-right"); telescopeGroup->addAction(ka); ka = actionCollection()->addAction("telescope_sync", this, SLOT(slotINDITelescopeSync())) << i18n("Sync telescope to the focused object") << QIcon::fromTheme("media-record"); telescopeGroup->addAction(ka); ka = actionCollection()->addAction("telescope_abort", this, SLOT(slotINDITelescopeAbort())) << i18n("Abort telescope motions") << QIcon::fromTheme("process-stop"); ka->setShortcutContext(Qt::ApplicationShortcut); telescopeGroup->addAction(ka); ka = actionCollection()->addAction("telescope_park", this, SLOT(slotINDITelescopePark())) << i18n("Park telescope") << QIcon::fromTheme("flag-red"); telescopeGroup->addAction(ka); ka = actionCollection()->addAction("telescope_unpark", this, SLOT(slotINDITelescopeUnpark())) << i18n("Unpark telescope") << QIcon::fromTheme("flag-green"); ka->setShortcutContext(Qt::ApplicationShortcut); telescopeGroup->addAction(ka); ka = actionCollection()->addAction("telescope_slew_mouse", this, SLOT(slotINDITelescopeSlewMousePointer())) << i18n("Slew the telescope to the mouse pointer position"); ka = actionCollection()->addAction("telescope_sync_mouse", this, SLOT(slotINDITelescopeSyncMousePointer())) << i18n("Sync the telescope to the mouse pointer position"); // Disable all telescope actions by default telescopeGroup->setEnabled(false); // Dome Actions ka = actionCollection()->addAction("dome_park", this, SLOT(slotINDIDomePark())) << i18n("Park dome") << QIcon::fromTheme("dome-park", QIcon(":/icons/dome-park.svg")); domeGroup->addAction(ka); ka = actionCollection()->addAction("dome_unpark", this, SLOT(slotINDIDomeUnpark())) << i18n("Unpark dome") << QIcon::fromTheme("dome-unpark", QIcon(":/icons/dome-unpark.svg")); ka->setShortcutContext(Qt::ApplicationShortcut); domeGroup->addAction(ka); domeGroup->setEnabled(false); #endif } void KStars::repopulateFOV() { // Read list of all FOVs //qDeleteAll( data()->availFOVs ); data()->availFOVs = FOVManager::getFOVs(); data()->syncFOV(); // Iterate through FOVs fovActionMenu->menu()->clear(); foreach (FOV *fov, data()->availFOVs) { KToggleAction *kta = actionCollection()->add(fov->name()); kta->setText(fov->name()); if (Options::fOVNames().contains(fov->name())) { kta->setChecked(true); } fovActionMenu->addAction(kta); connect(kta, SIGNAL(toggled(bool)), this, SLOT(slotTargetSymbol(bool))); } // Add menu bottom QAction *ka = actionCollection()->addAction("edit_fov", this, SLOT(slotFOVEdit())) << i18n("Edit FOV Symbols..."); fovActionMenu->addSeparator(); fovActionMenu->addAction(ka); } void KStars::repopulateHIPS() { // Iterate through actions hipsActionMenu->menu()->clear(); hipsGroup->actions().clear(); QAction *ka = actionCollection()->addAction(i18n("None"), this, SLOT(slotHIPSSource())) << i18n("None") << AddToGroup(hipsGroup) << Checked(Options::hIPSSource() == "None"); hipsActionMenu->addAction(ka); hipsActionMenu->addSeparator(); for (QMap source : HIPSManager::Instance()->getHIPSSources()) { QString title = source.value("obs_title"); QAction *ka = actionCollection()->addAction(title, this, SLOT(slotHIPSSource())) << title << AddToGroup(hipsGroup) << Checked(Options::hIPSSource() == title); hipsActionMenu->addAction(ka); } // Hips settings ka = actionCollection()->addAction("hipssettings", HIPSManager::Instance(), SLOT(showSettings())) << i18n("HiPS Settings..."); hipsActionMenu->addSeparator(); hipsActionMenu->addAction(ka); } void KStars::initStatusBar() { statusBar()->showMessage(i18n(" Welcome to KStars ")); QString s = "000d 00m 00s, +00d 00\' 00\""; //only need this to set the width if (Options::showAltAzField()) { AltAzField.setText(s); statusBar()->insertPermanentWidget(0, &AltAzField); } if (Options::showRADecField()) { RADecField.setText(s); statusBar()->insertPermanentWidget(1, &RADecField); } if (Options::showJ2000RADecField()) { J2000RADecField.setText(s); statusBar()->insertPermanentWidget(1, &J2000RADecField); } if (!Options::showStatusBar()) statusBar()->hide(); } void KStars::datainitFinished() { //Time-related connections connect(data()->clock(), SIGNAL(timeAdvanced()), this, SLOT(updateTime())); connect(data()->clock(), SIGNAL(timeChanged()), this, SLOT(updateTime())); //Add GUI elements to main window buildGUI(); connect(data()->clock(), SIGNAL(scaleChanged(float)), map(), SLOT(slotClockSlewing())); connect(data(), SIGNAL(skyUpdate(bool)), map(), SLOT(forceUpdateNow())); connect(m_TimeStepBox, SIGNAL(scaleChanged(float)), data(), SLOT(setTimeDirection(float))); connect(m_TimeStepBox, SIGNAL(scaleChanged(float)), data()->clock(), SLOT(setClockScale(float))); connect(m_TimeStepBox, SIGNAL(scaleChanged(float)), map(), SLOT(setFocus())); //m_equipmentWriter = new EquipmentWriter(); //m_observerAdd = new ObserverAdd; //Do not start the clock if "--paused" specified on the cmd line if (StartClockRunning) { // The initial time is set when KStars is first executed // but until all data is loaded, some time elapsed already so we need to synchronize if no Start Date string // was supplied to KStars if (StartDateString.isEmpty()) data()->changeDateTime(KStarsDateTime::currentDateTimeUtc()); data()->clock()->start(); } // Connect cache function for Find dialog connect(data(), SIGNAL(clearCache()), this, SLOT(clearCachedFindDialog())); //Propagate config settings applyConfig(false); //show the window. must be before kswizard and messageboxes show(); //Initialize focus initFocus(); data()->setFullTimeUpdate(); updateTime(); //If this is the first startup, show the wizard if (Options::runStartupWizard()) { slotWizard(); } //Show TotD KTipDialog::showTip(this, "kstars/tips"); - //DEBUG - qDebug() << "The current Date/Time is: " << KStarsDateTime::currentDateTime().toString(); + // Initial State + qCDebug(KSTARS) << "Date/Time is:" << data()->clock()->utc().toString(); + qCDebug(KSTARS) << "Location:" << data()->geo()->fullName(); + qCDebug(KSTARS) << "TZ0:" << data()->geo()->TZ0() << "TZ:" << data()->geo()->TZ(); KSTheme::Manager::instance()->setCurrentTheme(Options::currentTheme()); } void KStars::initFocus() { //Case 1: tracking on an object if (Options::isTracking() && Options::focusObject() != i18n("nothing")) { SkyObject *oFocus; if (Options::focusObject() == i18n("star")) { SkyPoint p(Options::focusRA(), Options::focusDec()); double maxrad = 1.0; oFocus = data()->skyComposite()->starNearest(&p, maxrad); } else { oFocus = data()->objectNamed(Options::focusObject()); } if (oFocus) { map()->setFocusObject(oFocus); map()->setClickedObject(oFocus); map()->setFocusPoint(oFocus); } else { qWarning() << "Cannot center on " << Options::focusObject() << ": no object found." << endl; } //Case 2: not tracking, and using Alt/Az coords. Set focus point using //FocusRA as the Azimuth, and FocusDec as the Altitude } else if (!Options::isTracking() && Options::useAltAz()) { SkyPoint pFocus; pFocus.setAz(Options::focusRA()); pFocus.setAlt(Options::focusDec()); pFocus.HorizontalToEquatorial(data()->lst(), data()->geo()->lat()); map()->setFocusPoint(&pFocus); //Default: set focus point using FocusRA as the RA and //FocusDec as the Dec } else { SkyPoint pFocus(Options::focusRA(), Options::focusDec()); pFocus.EquatorialToHorizontal(data()->lst(), data()->geo()->lat()); map()->setFocusPoint(&pFocus); } data()->setSnapNextFocus(); map()->setDestination(*map()->focusPoint()); map()->setFocus(map()->destination()); map()->showFocusCoords(); //Check whether initial position is below the horizon. if (Options::useAltAz() && Options::showGround() && map()->focus()->alt().Degrees() < -1.0) { QString caption = i18n("Initial Position is Below Horizon"); QString message = i18n("The initial position is below the horizon.\nWould you like to reset to the default position?"); if (KMessageBox::warningYesNo(this, message, caption, KGuiItem(i18n("Reset Position")), KGuiItem(i18n("Do Not Reset")), "dag_start_below_horiz") == KMessageBox::Yes) { map()->setClickedObject(nullptr); map()->setFocusObject(nullptr); Options::setIsTracking(false); data()->setSnapNextFocus(true); SkyPoint DefaultFocus; DefaultFocus.setAz(180.0); DefaultFocus.setAlt(45.0); DefaultFocus.HorizontalToEquatorial(data()->lst(), data()->geo()->lat()); map()->setDestination(DefaultFocus); } } //If there is a focusObject() and it is a SS body, add a temporary Trail if (map()->focusObject() && map()->focusObject()->isSolarSystem() && Options::useAutoTrail()) { ((KSPlanetBase *)map()->focusObject())->addToTrail(); data()->temporaryTrail = true; } } void KStars::buildGUI() { //create the texture manager TextureManager::Create(); //create the skymap m_SkyMap = SkyMap::Create(); connect(m_SkyMap, SIGNAL(mousePointChanged(SkyPoint*)), SLOT(slotShowPositionBar(SkyPoint*))); connect(m_SkyMap, SIGNAL(zoomChanged()), SLOT(slotZoomChanged())); setCentralWidget(m_SkyMap); //Initialize menus, toolbars, and statusbars initStatusBar(); initActions(); setupGUI(StandardWindowOptions(Default & ~Create)); createGUI("kstarsui.rc"); //get focus of keyboard and mouse actions (for example zoom in with +) map()->QWidget::setFocus(); resize(Options::windowWidth(), Options::windowHeight()); // check zoom in/out buttons if (Options::zoomFactor() >= MAXZOOM) actionCollection()->action("zoom_in")->setEnabled(false); if (Options::zoomFactor() <= MINZOOM) actionCollection()->action("zoom_out")->setEnabled(false); } void KStars::populateThemes() { KSTheme::Manager::instance()->setThemeMenuAction(new QMenu(i18n("&Themes"), this)); KSTheme::Manager::instance()->registerThemeActions(this); connect(KSTheme::Manager::instance(), SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged())); } void KStars::slotThemeChanged() { Options::setCurrentTheme(KSTheme::Manager::instance()->currentThemeName()); } diff --git a/kstars/time/timezonerule.cpp b/kstars/time/timezonerule.cpp index 5377a7a16..c89dd0a1f 100644 --- a/kstars/time/timezonerule.cpp +++ b/kstars/time/timezonerule.cpp @@ -1,576 +1,576 @@ /*************************************************************************** timezonerule.cpp - description ------------------- begin : Tue Apr 2 2002 copyright : (C) 2002 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "timezonerule.h" +#include "kstars_debug.h" #include -#include #include TimeZoneRule::TimeZoneRule() { setEmpty(); } TimeZoneRule::TimeZoneRule(const QString &smonth, const QString &sday, const QTime &stime, const QString &rmonth, const QString &rday, const QTime &rtime, const double &dh) { dTZ = 0.0; if (smonth != "0") { StartMonth = initMonth(smonth); RevertMonth = initMonth(rmonth); if (StartMonth && RevertMonth && initDay(sday, StartDay, StartWeek) && initDay(rday, RevertDay, RevertWeek) && stime.isValid() && rtime.isValid()) { StartTime = stime; RevertTime = rtime; HourOffset = dh; } else { - qWarning() << i18n("Error parsing TimeZoneRule, setting to empty rule."); + qCWarning(KSTARS) << i18n("Error parsing TimeZoneRule, setting to empty rule."); setEmpty(); } } else //Empty rule { setEmpty(); } } void TimeZoneRule::setEmpty() { StartMonth = 0; RevertMonth = 0; StartDay = 0; RevertDay = 0; StartWeek = -1; RevertWeek = -1; StartTime = QTime(); RevertTime = QTime(); HourOffset = 0.0; dTZ = 0.0; } void TimeZoneRule::setDST(bool activate) { if (activate) { - qDebug() << "Daylight Saving Time active"; + qCDebug(KSTARS) << "Daylight Saving Time active"; dTZ = HourOffset; } else { - qDebug() << "Daylight Saving Time inactive"; + qCDebug(KSTARS) << "Daylight Saving Time inactive"; dTZ = 0.0; } } int TimeZoneRule::initMonth(const QString &mn) { //Check whether the argument is a three-letter English month code. QString ml = mn.toLower(); if (ml == "jan") return 1; else if (ml == "feb") return 2; else if (ml == "mar") return 3; else if (ml == "apr") return 4; else if (ml == "may") return 5; else if (ml == "jun") return 6; else if (ml == "jul") return 7; else if (ml == "aug") return 8; else if (ml == "sep") return 9; else if (ml == "oct") return 10; else if (ml == "nov") return 11; else if (ml == "dec") return 12; - qWarning() << i18n("Could not parse %1 as a valid month code.", mn); + qCWarning(KSTARS) << i18n("Could not parse %1 as a valid month code.", mn); return 0; } bool TimeZoneRule::initDay(const QString &dy, int &Day, int &Week) { //Three possible ways to express a day. //1. simple integer; the calendar date...set Week=0 to indicate that Date is not the day of the week bool ok; int day = dy.toInt(&ok); if (ok) { Day = day; Week = 0; return true; } QString dl = dy.toLower(); //2. 3-letter day of week string, indicating the last of that day of the month // ...set Week to 5 to indicate the last weekday of the month if (dl == "mon") { Day = 1; Week = 5; return true; } else if (dl == "tue") { Day = 2; Week = 5; return true; } else if (dl == "wed") { Day = 3; Week = 5; return true; } else if (dl == "thu") { Day = 4; Week = 5; return true; } else if (dl == "fri") { Day = 5; Week = 5; return true; } else if (dl == "sat") { Day = 6; Week = 5; return true; } else if (dl == "sun") { Day = 7; Week = 5; return true; } //3. 1,2 or 3 followed by 3-letter day of week string; this indicates // the (1st/2nd/3rd) weekday of the month. int wn = dl.leftRef(1).toInt(); if (wn > 0 && wn < 4) { QString dm = dl.mid(1, dl.length()).toLower(); if (dm == "mon") { Day = 1; Week = wn; return true; } else if (dm == "tue") { Day = 2; Week = wn; return true; } else if (dm == "wed") { Day = 3; Week = wn; return true; } else if (dm == "thu") { Day = 4; Week = wn; return true; } else if (dm == "fri") { Day = 5; Week = wn; return true; } else if (dm == "sat") { Day = 6; Week = wn; return true; } else if (dm == "sun") { Day = 7; Week = wn; return true; } } - qWarning() << i18n("Could not parse %1 as a valid day code.", dy); + qCWarning(KSTARS) << i18n("Could not parse %1 as a valid day code.", dy); return false; } int TimeZoneRule::findStartDay(const KStarsDateTime &d) { // Determine the calendar date of StartDay for the month and year of the given date. QDate test; // TimeZoneRule is empty, return -1 if (isEmptyRule()) return -1; // If StartWeek=0, just return the integer. if (StartWeek == 0) return StartDay; // Since StartWeek was not zero, StartDay is the day of the week, not the calendar date else if (StartWeek == 5) // count back from end of month until StartDay is found. { for (test = QDate(d.date().year(), d.date().month(), d.date().daysInMonth()); test.day() > 21; test = test.addDays(-1)) if (test.dayOfWeek() == StartDay) break; } else // Count forward from day 1, 8 or 15 (depending on StartWeek) until correct day of week is found { for (test = QDate(d.date().year(), d.date().month(), (StartWeek - 1) * 7 + 1); test.day() < 7 * StartWeek; test = test.addDays(1)) if (test.dayOfWeek() == StartDay) break; } return test.day(); } int TimeZoneRule::findRevertDay(const KStarsDateTime &d) { // Determine the calendar date of RevertDay for the month and year of the given date. QDate test; // TimeZoneRule is empty, return -1 if (isEmptyRule()) return -1; // If RevertWeek=0, just return the integer. if (RevertWeek == 0) return RevertDay; // Since RevertWeek was not zero, RevertDay is the day of the week, not the calendar date else if (RevertWeek == 5) //count back from end of month until RevertDay is found. { for (test = QDate(d.date().year(), d.date().month(), d.date().daysInMonth()); test.day() > 21; test = test.addDays(-1)) if (test.dayOfWeek() == RevertDay) break; } else //Count forward from day 1, 8 or 15 (depending on RevertWeek) until correct day of week is found { for (test = QDate(d.date().year(), d.date().month(), (RevertWeek - 1) * 7 + 1); test.day() < 7 * RevertWeek; test = test.addDays(1)) if (test.dayOfWeek() == StartDay) break; } return test.day(); } bool TimeZoneRule::isDSTActive(const KStarsDateTime &date) { // The empty rule always returns false if (isEmptyRule()) return false; // First, check whether the month is outside the DST interval. Note that // the interval check is different if StartMonth > RevertMonth (indicating that // the DST interval includes the end of the year). int month = date.date().month(); if (StartMonth < RevertMonth) { if (month < StartMonth || month > RevertMonth) return false; } else { if (month < StartMonth && month > RevertMonth) return false; } // OK, if the month is equal to StartMonth or Revert Month, we have more // detailed checking to do... int day = date.date().day(); if (month == StartMonth) { int sday = findStartDay(date); if (day < sday) return false; if (day == sday && date.time() < StartTime) return false; } else if (month == RevertMonth) { int rday = findRevertDay(date); if (day > rday) return false; if (day == rday && date.time() > RevertTime) return false; } // passed all tests, so we must be in DST. return true; } void TimeZoneRule::nextDSTChange_LTime(const KStarsDateTime &date) { KStarsDateTime result; // return an invalid date if the rule is the empty rule. if (isEmptyRule()) result = KStarsDateTime(QDateTime()); else if (deltaTZ()) { // Next change is reverting back to standard time. //y is the year for the next DST Revert date. It's either the current year, or //the next year if the current month is already past RevertMonth int y = date.date().year(); if (RevertMonth < date.date().month()) ++y; result = KStarsDateTime(QDate(y, RevertMonth, 1), RevertTime); result = KStarsDateTime(QDate(y, RevertMonth, findRevertDay(result)), RevertTime); } else { // Next change is starting DST. //y is the year for the next DST Start date. It's either the current year, or //the next year if the current month is already past StartMonth int y = date.date().year(); if (StartMonth < date.date().month()) ++y; result = KStarsDateTime(QDate(y, StartMonth, 1), StartTime); result = KStarsDateTime(QDate(y, StartMonth, findStartDay(result)), StartTime); } - qDebug() << "Next Daylight Savings Time change (Local Time): " << result.toString(); + qCDebug(KSTARS) << "Next Daylight Savings Time change (Local Time): " << result.toString(); next_change_ltime = result; } void TimeZoneRule::previousDSTChange_LTime(const KStarsDateTime &date) { KStarsDateTime result; // return an invalid date if the rule is the empty rule if (isEmptyRule()) next_change_ltime = KStarsDateTime(QDateTime()); if (deltaTZ()) { // Last change was starting DST. //y is the year for the previous DST Start date. It's either the current year, or //the previous year if the current month is earlier than StartMonth int y = date.date().year(); if (StartMonth > date.date().month()) --y; result = KStarsDateTime(QDate(y, StartMonth, 1), StartTime); result = KStarsDateTime(QDate(y, StartMonth, findStartDay(result)), StartTime); } else if (StartMonth) { //Last change was reverting to standard time. //y is the year for the previous DST Start date. It's either the current year, or //the previous year if the current month is earlier than StartMonth int y = date.date().year(); if (RevertMonth > date.date().month()) --y; result = KStarsDateTime(QDate(y, RevertMonth, 1), RevertTime); result = KStarsDateTime(QDate(y, RevertMonth, findRevertDay(result)), RevertTime); } - qDebug() << "Previous Daylight Savings Time change (Local Time): " << result.toString(); + qCDebug(KSTARS) << "Previous Daylight Savings Time change (Local Time): " << result.toString(); next_change_ltime = result; } /**Convert current local DST change time in universal time */ void TimeZoneRule::nextDSTChange(const KStarsDateTime &local_date, const double TZoffset) { // just decrement timezone offset and hour offset KStarsDateTime result = local_date.addSecs(int((TZoffset + deltaTZ()) * -3600)); - qDebug() << "Next Daylight Savings Time change (UTC): " << result.toString(); + qCDebug(KSTARS) << "Next Daylight Savings Time change (UTC): " << result.toString(); next_change_utc = result; } /**Convert current local DST change time in universal time */ void TimeZoneRule::previousDSTChange(const KStarsDateTime &local_date, const double TZoffset) { // just decrement timezone offset KStarsDateTime result = local_date.addSecs(int(TZoffset * -3600)); // if prev DST change is a revert change, so the revert time is in daylight saving time if (result.date().month() == RevertMonth) result = result.addSecs(int(HourOffset * -3600)); - qDebug() << "Previous Daylight Savings Time change (UTC): " << result.toString(); + qCDebug(KSTARS) << "Previous Daylight Savings Time change (UTC): " << result.toString(); next_change_utc = result; } void TimeZoneRule::reset_with_ltime(KStarsDateTime <ime, const double TZoffset, const bool time_runs_forward, const bool automaticDSTchange) { /**There are some problems using local time for getting next daylight saving change time. 1. The local time is the start time of DST change. So the local time doesn't exists and must corrected. 2. The local time is the revert time. So the local time exists twice. 3. Neither start time nor revert time. There is no problem. Problem #1 is more complicated and we have to change the local time by reference. Problem #2 we just have to reset status of DST. automaticDSTchange should only set to true if DST status changed due to running automatically over a DST change time. If local time will changed manually the automaticDSTchange should always set to false, to hold current DST status if possible (just on start and revert time possible). */ //don't need to do anything for empty rule if (isEmptyRule()) return; // check if DST is active before resetting with new time bool wasDSTactive(false); if (deltaTZ() != 0.0) { wasDSTactive = true; } // check if current time is start time, this means if a DST change happend in last hour(s) bool active_with_houroffset = isDSTActive(ltime.addSecs(int(HourOffset * -3600))); bool active_normal = isDSTActive(ltime); // store a valid local time KStarsDateTime ValidLTime = ltime; if (active_with_houroffset != active_normal && ValidLTime.date().month() == StartMonth) { // current time is the start time - qDebug() << "Current time = Starttime: invalid local time due to daylight saving time"; + qCDebug(KSTARS) << "Current time = Starttime: invalid local time due to daylight saving time"; // set a correct local time because the current time doesn't exists // if automatic DST change happend, new DST setting is the opposite of current setting if (automaticDSTchange) { // revert DST status setDST(!wasDSTactive); // new setting DST is inactive, so subtract hour offset to new time if (wasDSTactive) { // DST inactive ValidLTime = ltime.addSecs(int(HourOffset * -3600)); } else { // DST active // add hour offset to new time ValidLTime = ltime.addSecs(int(HourOffset * 3600)); } } else // if ( automaticDSTchange ) { // no automatic DST change happend, so stay in current DST mode setDST(wasDSTactive); if (wasDSTactive) { // DST active // add hour offset to current time, because time doesn't exists ValidLTime = ltime.addSecs(int(HourOffset * 3600)); } else { // DST inactive // subtrace hour offset to current time, because time doesn't exists ValidLTime = ltime.addSecs(int(HourOffset * -3600)); } } // else { // if ( automaticDSTchange ) } else // if ( active_with_houroffset != active_normal && ValidLTime.date().month() == StartMonth ) { // If current time was not start time, so check if current time is revert time // this means if a DST change happend in next hour(s) active_with_houroffset = isDSTActive(ltime.addSecs(int(HourOffset * 3600))); if (active_with_houroffset != active_normal && RevertMonth == ValidLTime.date().month()) { // current time is the revert time - qDebug() << "Current time = Reverttime"; + qCDebug(KSTARS) << "Current time = Reverttime"; // we don't kneed to change the local time, because local time always exists, but // some times exists twice, so we have to reset DST status if (automaticDSTchange) { // revert DST status setDST(!wasDSTactive); } else { // no automatic DST change so stay in current DST mode setDST(wasDSTactive); } } else { //Current time was neither starttime nor reverttime, so use normal calculated DST status setDST(active_normal); } } // if ( active_with_houroffset != active_normal && ValidLTime.date().month() == StartMonth ) // qDebug() << "Using Valid Local Time = " << ValidLTime.toString(); if (time_runs_forward) { // get next DST change time in local time nextDSTChange_LTime(ValidLTime); nextDSTChange(next_change_ltime, TZoffset); } else { // get previous DST change time in local time previousDSTChange_LTime(ValidLTime); previousDSTChange(next_change_ltime, TZoffset); } ltime = ValidLTime; } bool TimeZoneRule::equals(TimeZoneRule *r) { if (StartDay == r->StartDay && RevertDay == r->RevertDay && StartWeek == r->StartWeek && RevertWeek == r->RevertWeek && StartMonth == r->StartMonth && RevertMonth == r->RevertMonth && StartTime == r->StartTime && RevertTime == r->RevertTime && isEmptyRule() == r->isEmptyRule()) return true; else return false; }