1 /*
2 * Stellarium
3 * Copyright (C) 2008 Guillaume Chereau
4 * Copyright (C) 2011 Alexander Wolf
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
19 */
20
21 #include "Dialog.hpp"
22 #include "LocationDialog.hpp"
23 #include "StelLocationMgr.hpp"
24 #include "ui_locationDialogGui.h"
25 #include "StelApp.hpp"
26 #include "StelCore.hpp"
27
28 #include "StelModuleMgr.hpp"
29 #include "SolarSystem.hpp"
30 #include "StelFileMgr.hpp"
31 #include "StelLocaleMgr.hpp"
32 #include "StelGui.hpp"
33 #include "StelGuiItems.hpp"
34 #include "StelSkyCultureMgr.hpp"
35
36 #include <QSettings>
37 #include <QDebug>
38 #include <QFrame>
39 #include <QSortFilterProxyModel>
40 #include <QTimer>
41 #include <QStringListModel>
42 #include <QTimeZone>
43
LocationDialog(QObject * parent)44 LocationDialog::LocationDialog(QObject* parent)
45 : StelDialog("Location", parent)
46 , isEditingNew(false)
47 , allModel(Q_NULLPTR)
48 , pickedModel(Q_NULLPTR)
49 , proxyModel(Q_NULLPTR)
50 #ifdef ENABLE_GPS
51 , gpsCount(0)
52 #endif
53 {
54 ui = new Ui_locationDialogForm;
55 }
56
~LocationDialog()57 LocationDialog::~LocationDialog()
58 {
59 delete ui;
60 }
61
retranslate()62 void LocationDialog::retranslate()
63 {
64 if (dialog)
65 {
66 ui->retranslateUi(dialog);
67 populatePlanetList();
68 populateRegionList(StelApp::getInstance().getCore()->getCurrentLocation().planetName);
69 populateTimeZonesList();
70 populateTooltips();
71 }
72 }
73
styleChanged()74 void LocationDialog::styleChanged()
75 {
76 // Make the map red if needed
77 if (dialog)
78 setMapForLocation(StelApp::getInstance().getCore()->getCurrentLocation());
79 }
80
81 // Initialize the dialog widgets and connect the signals/slots
createDialogContent()82 void LocationDialog::createDialogContent()
83 {
84 // We try to directly connect to the observer slots as much as we can
85 ui->setupUi(dialog);
86
87 //enable resizability
88 ui->mapLabel->setMinimumSize(0,0);
89 ui->mapLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
90 ui->mapLabel->setScaledContents(false);
91
92 StelApp *app = &StelApp::getInstance();
93 connect(app, SIGNAL(languageChanged()), this, SLOT(retranslate()));
94 connect(app, SIGNAL(flagShowDecimalDegreesChanged(bool)), this, SLOT(setDisplayFormatForSpins(bool)));
95 connect(&app->getSkyCultureMgr(), SIGNAL(currentSkyCultureChanged(QString)), this, SLOT(populatePlanetList(QString)));
96 // Init the SpinBox entries
97 ui->longitudeSpinBox->setPrefixType(AngleSpinBox::Longitude);
98 ui->longitudeSpinBox->setMinimum(-180.0, true);
99 ui->longitudeSpinBox->setMaximum( 180.0, true);
100 ui->longitudeSpinBox->setWrapping(true);
101 ui->latitudeSpinBox->setPrefixType(AngleSpinBox::Latitude);
102 ui->latitudeSpinBox->setMinimum(-90.0, true);
103 ui->latitudeSpinBox->setMaximum( 90.0, true);
104 ui->latitudeSpinBox->setWrapping(false);
105 setDisplayFormatForSpins(app->getFlagShowDecimalDegrees());
106
107 //initialize list model
108 allModel = new QStringListModel(this);
109 pickedModel = new QStringListModel(this);
110 StelLocationMgr *locMgr=&(StelApp::getInstance().getLocationMgr());
111 connect(locMgr, SIGNAL(locationListChanged()), this, SLOT(reloadLocations()));
112 reloadLocations();
113 proxyModel = new QSortFilterProxyModel(ui->citiesListView);
114 proxyModel->setSourceModel(allModel);
115 proxyModel->sort(0, Qt::AscendingOrder);
116 proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
117 ui->citiesListView->setModel(proxyModel);
118
119 // Kinetic scrolling
120 kineticScrollingList << ui->citiesListView;
121 StelGui* gui= dynamic_cast<StelGui*>(StelApp::getInstance().getGui());
122 if (gui)
123 {
124 enableKineticScrolling(gui->getFlagUseKineticScrolling());
125 connect(gui, SIGNAL(flagUseKineticScrollingChanged(bool)), this, SLOT(enableKineticScrolling(bool)));
126 }
127
128 populatePlanetList();
129 populateTimeZonesList();
130
131 connect(ui->citySearchLineEdit, SIGNAL(textChanged(const QString&)), proxyModel, SLOT(setFilterWildcard(const QString&)));
132 connect(ui->citiesListView, SIGNAL(clicked(const QModelIndex&)),
133 this, SLOT(setLocationFromList(const QModelIndex&)));
134
135 // Connect all the QT signals
136 connect(ui->closeStelWindow, SIGNAL(clicked()), this, SLOT(close()));
137 connect(ui->TitleBar, SIGNAL(movedTo(QPoint)), this, SLOT(handleMovedTo(QPoint)));
138 connect(ui->mapLabel, SIGNAL(positionChanged(double, double)), this, SLOT(setLocationFromMap(double, double)));
139
140 connect(ui->addLocationToListPushButton, SIGNAL(clicked()), this, SLOT(addCurrentLocationToList()));
141 connect(ui->deleteLocationFromListPushButton, SIGNAL(clicked()), this, SLOT(deleteCurrentLocationFromList()));
142 connect(ui->resetListPushButton, SIGNAL(clicked()), this, SLOT(resetLocationList()));
143 connect(ui->regionNameComboBox, SIGNAL(activated(const QString &)), this, SLOT(filterSitesByRegion()));
144
145 StelCore* core = StelApp::getInstance().getCore();
146 const StelLocation& currentLocation = core->getCurrentLocation();
147 populateRegionList(currentLocation.planetName);
148 bool b = (currentLocation.getID() == core->getDefaultLocationID());
149 QSettings* conf = StelApp::getInstance().getSettings();
150 if (conf->value("init_location/location", "auto").toString() == "auto")
151 {
152 ui->useIpQueryCheckBox->setChecked(true);
153 b = false;
154 }
155 updateDefaultLocationControls(b);
156
157 setFieldsFromLocation(currentLocation);
158 if (currentLocation.ianaTimeZone != core->getCurrentTimeZone())
159 {
160 setTimezone(core->getCurrentTimeZone()); // also sets string customTimeZone and GUI checkbox
161 }
162 else
163 {
164 //ui->timeZoneNameComboBox->setEnabled(false);
165 // TODO Maybe also:
166 // StelApp::getInstance().getSettings()->remove("localization/time_zone");
167 }
168
169 #ifdef ENABLE_GPS
170 connect(ui->gpsToolButton, SIGNAL(toggled(bool)), this, SLOT(gpsEnableQueryLocation(bool)));
171 ui->gpsToolButton->setStyleSheet(QString("QToolButton{ background: gray; }")); // ? Missing default style?
172 connect(locMgr, SIGNAL(gpsQueryFinished(bool)), this, SLOT(gpsReturn(bool)));
173 #else
174 ui->gpsToolButton->setEnabled(false);
175 ui->gpsToolButton->hide();
176 #endif
177 connect(ui->useIpQueryCheckBox, SIGNAL(clicked(bool)), this, SLOT(ipQueryLocation(bool)));
178 connect(ui->useAsDefaultLocationCheckBox, SIGNAL(clicked(bool)), this, SLOT(setDefaultLocation(bool)));
179 connect(ui->pushButtonReturnToDefault, SIGNAL(clicked()), core, SLOT(returnToDefaultLocation()));
180 connect(ui->pushButtonReturnToDefault, SIGNAL(clicked()), this, SLOT(resetLocationList()));
181 connectBoolProperty(ui->dstCheckBox, "StelCore.flagUseDST");
182 connectBoolProperty(ui->useCustomTimeZoneCheckBox, "StelCore.flagUseCTZ");
183 connect(ui->useCustomTimeZoneCheckBox, SIGNAL(toggled(bool)), ui->timeZoneNameComboBox, SLOT(setEnabled(bool)));
184 ui->timeZoneNameComboBox->setEnabled(core->getUseCustomTimeZone());
185 connect(ui->useCustomTimeZoneCheckBox, SIGNAL(clicked(bool)), this, SLOT(updateTimeZoneControls(bool)));
186 connect(core, SIGNAL(currentTimeZoneChanged(QString)), this, SLOT(setTimezone(QString)));
187
188 connectEditSignals();
189
190 populateTooltips();
191
192 connect(core, SIGNAL(locationChanged(StelLocation)), this, SLOT(updateFromProgram(StelLocation)));
193
194 ui->citySearchLineEdit->setFocus();
195 }
196
setDisplayFormatForSpins(bool flagDecimalDegrees)197 void LocationDialog::setDisplayFormatForSpins(bool flagDecimalDegrees)
198 {
199 int places = 2;
200 AngleSpinBox::DisplayFormat format = AngleSpinBox::DMSSymbols;
201 if (flagDecimalDegrees)
202 {
203 places = 6;
204 format = AngleSpinBox::DecimalDeg;
205 }
206 ui->longitudeSpinBox->setDecimals(places);
207 ui->longitudeSpinBox->setDisplayFormat(format);
208 ui->latitudeSpinBox->setDecimals(places);
209 ui->latitudeSpinBox->setDisplayFormat(format);
210 }
211
handleDialogSizeChanged(QSizeF size)212 void LocationDialog::handleDialogSizeChanged(QSizeF size)
213 {
214 StelDialog::handleDialogSizeChanged(size);
215 StelLocation loc = locationFromFields();
216 //resizePixmap();
217 //ui->mapLabel->setCursorPos(loc.longitude, loc.latitude);
218 }
219
reloadLocations()220 void LocationDialog::reloadLocations()
221 {
222 allModel->setStringList(StelApp::getInstance().getLocationMgr().getAllMap().keys());
223 }
224
populateTooltips()225 void LocationDialog::populateTooltips()
226 {
227 ui->resetListPushButton->setToolTip(q_("Reset location list to show all known locations"));
228 ui->gpsToolButton->setToolTip(QString("<p>%1</p>").arg(q_("Toggle fetching GPS location. (Does not change time zone!) When satisfied, toggle off to let other programs access the GPS device.")));
229 }
230
231 // Update the widget to make sure it is synchrone if the location is changed programmatically
updateFromProgram(const StelLocation & currentLocation)232 void LocationDialog::updateFromProgram(const StelLocation& currentLocation)
233 {
234 if (!dialog)
235 return;
236
237 StelCore* stelCore = StelApp::getInstance().getCore();
238
239 isEditingNew = false;
240
241 // Check that the use as default check box needs to be updated
242 // Move to setFieldsFromLocation()? --BM?
243 const bool b = currentLocation.getID() == stelCore->getDefaultLocationID();
244 QSettings* conf = StelApp::getInstance().getSettings();
245 if (conf->value("init_location/location", "auto").toString() != ("auto"))
246 {
247 updateDefaultLocationControls(b);
248 ui->pushButtonReturnToDefault->setEnabled(!b);
249 }
250
251 const QString& key1 = currentLocation.getID();
252 const QString& key2 = locationFromFields().getID();
253 if (key1!=key2)
254 {
255 setFieldsFromLocation(currentLocation);
256 }
257 }
258
disconnectEditSignals()259 void LocationDialog::disconnectEditSignals()
260 {
261 disconnect(ui->longitudeSpinBox, SIGNAL(valueChanged()), this, SLOT(setLocationFromCoords()));
262 disconnect(ui->latitudeSpinBox, SIGNAL(valueChanged()), this, SLOT(setLocationFromCoords()));
263 disconnect(ui->altitudeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setLocationFromCoords(int)));
264 disconnect(ui->planetNameComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(moveToAnotherPlanet(const QString&)));
265 disconnect(ui->regionNameComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(reportEdit()));
266 disconnect(ui->timeZoneNameComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(saveTimeZone()));
267 disconnect(ui->cityNameLineEdit, SIGNAL(textEdited(const QString&)), this, SLOT(reportEdit()));
268 }
269
connectEditSignals()270 void LocationDialog::connectEditSignals()
271 {
272 connect(ui->longitudeSpinBox, SIGNAL(valueChanged()), this, SLOT(setLocationFromCoords()));
273 connect(ui->latitudeSpinBox, SIGNAL(valueChanged()), this, SLOT(setLocationFromCoords()));
274 connect(ui->altitudeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setLocationFromCoords(int)));
275 connect(ui->planetNameComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(moveToAnotherPlanet(const QString&)));
276 connect(ui->regionNameComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(reportEdit()));
277 connect(ui->timeZoneNameComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(saveTimeZone()));
278 connect(ui->cityNameLineEdit, SIGNAL(textEdited(const QString&)), this, SLOT(reportEdit()));
279 }
280
setFieldsFromLocation(const StelLocation & loc)281 void LocationDialog::setFieldsFromLocation(const StelLocation& loc)
282 {
283 StelCore *core = StelApp::getInstance().getCore();
284
285 // Deactivate edit signals
286 disconnectEditSignals();
287
288 // When user has activated a custom timezone, we should not change this!
289 if (core->getUseCustomTimeZone())
290 customTimeZone=core->getCurrentTimeZone();
291
292 ui->cityNameLineEdit->setText(loc.name);
293 ui->longitudeSpinBox->setDegrees(loc.longitude);
294 ui->latitudeSpinBox->setDegrees(loc.latitude);
295 ui->altitudeSpinBox->setValue(loc.altitude);
296
297 int idx = ui->planetNameComboBox->findData(loc.planetName, Qt::UserRole, Qt::MatchCaseSensitive);
298 if (idx==-1)
299 {
300 // Use Earth as default
301 idx = ui->planetNameComboBox->findData(QVariant("Earth"), Qt::UserRole, Qt::MatchCaseSensitive);
302 }
303 ui->planetNameComboBox->setCurrentIndex(idx);
304
305 idx = ui->regionNameComboBox->findData(loc.region, Qt::UserRole, Qt::MatchCaseSensitive);
306 if (idx==-1)
307 {
308 if (ui->planetNameComboBox->currentData(Qt::UserRole).toString()=="Earth")
309 {
310 // Use Western Europe as default on Earth
311 idx = ui->regionNameComboBox->findData(QVariant("Western Europe"), Qt::UserRole, Qt::MatchCaseSensitive);
312 }
313 else
314 idx = ui->regionNameComboBox->findData(QVariant(""), Qt::UserRole, Qt::MatchCaseSensitive);
315 }
316 ui->regionNameComboBox->setCurrentIndex(idx);
317
318 QString tz = loc.ianaTimeZone;
319 if (loc.planetName=="Earth" && tz.isEmpty())
320 {
321 qDebug() << "setFieldsFromLocation(): Empty loc.ianaTimeZone!";
322 tz = "system_default";
323 }
324 if (loc.planetName!="Earth") // Check for non-terrestial location...
325 tz = "LMST";
326
327 if (core->getUseCustomTimeZone())
328 tz=customTimeZone;
329
330 idx = ui->timeZoneNameComboBox->findData(tz, Qt::UserRole, Qt::MatchCaseSensitive);
331 if (idx==-1)
332 {
333 if (loc.planetName=="Earth")
334 tz = "system_default";
335 else
336 tz = "LMST";
337 // Use LMST/system_default as default
338 idx = ui->timeZoneNameComboBox->findData(tz, Qt::UserRole, Qt::MatchCaseSensitive);
339 }
340 ui->timeZoneNameComboBox->setCurrentIndex(idx);
341 core->setCurrentTimeZone(tz);
342
343 setMapForLocation(loc);
344 ui->mapLabel->setCursorPos(loc.longitude, loc.latitude);
345
346 ui->deleteLocationFromListPushButton->setEnabled(StelApp::getInstance().getLocationMgr().canDeleteUserLocation(loc.getID()));
347
348 SolarSystem* ssm = GETSTELMODULE(SolarSystem);
349 PlanetP p = ssm->searchByEnglishName(loc.planetName);
350 StelModule* ls = StelApp::getInstance().getModule("LandscapeMgr");
351 if (ls->property("flagEnvironmentAutoEnable").toBool())
352 {
353 if (loc.planetName != StelApp::getInstance().getCore()->getCurrentLocation().planetName)
354 {
355 QSettings* conf = StelApp::getInstance().getSettings();
356 ls->setProperty("atmosphereDisplayed", p->hasAtmosphere() && conf->value("landscape/flag_atmosphere", true).toBool());
357 ls->setProperty("fogDisplayed", p->hasAtmosphere() && conf->value("landscape/flag_fog", true).toBool());
358 }
359 }
360
361 // Reactivate edit signals
362 connectEditSignals();
363 }
364
365 // Update the map for the given location.
setMapForLocation(const StelLocation & loc)366 void LocationDialog::setMapForLocation(const StelLocation& loc)
367 {
368 // Avoids useless processing
369 if (lastPlanet==loc.planetName)
370 return;
371
372 QPixmap pixmap;
373 // Try to set the proper planet map image
374 if (loc.planetName=="Earth")
375 {
376 // Special case for earth, we don't want to see the clouds
377 pixmap = QPixmap(":/graphicGui/miscWorldMap.png");
378 }
379 else
380 {
381 SolarSystem* ssm = GETSTELMODULE(SolarSystem);
382 PlanetP p = ssm->searchByEnglishName(loc.planetName);
383 if (p)
384 {
385 QString path = StelFileMgr::findFile("textures/"+p->getTextMapName());
386 if (path.isEmpty())
387 {
388 qWarning() << "ERROR - could not find planet map for " << loc.planetName;
389 return;
390 }
391 pixmap = QPixmap(path);
392 }
393 }
394 StelCore * core = StelApp::getInstance().getCore();
395 pixmap.setDevicePixelRatio(core->getCurrentStelProjectorParams().devicePixelsPerPixel);
396 ui->mapLabel->setPixmap(pixmap);
397 ui->mapLabel->resizePixmap();
398 ui->mapLabel->setCursorPos(loc.longitude, loc.latitude);
399 // For caching
400 lastPlanet = loc.planetName;
401 }
402
populatePlanetList()403 void LocationDialog::populatePlanetList()
404 {
405 Q_ASSERT(ui->planetNameComboBox);
406 QComboBox* planetsCombo = ui->planetNameComboBox;
407 SolarSystem* ssystem = GETSTELMODULE(SolarSystem);
408 QList<PlanetP> ssList = ssystem->getAllPlanets();
409
410 //Save the current selection to be restored later
411 planetsCombo->blockSignals(true);
412 int index = planetsCombo->currentIndex();
413 QVariant selectedPlanetId = planetsCombo->itemData(index);
414 planetsCombo->clear();
415 //For each planet, display the localized name and store the original as user
416 //data. Unfortunately, there's no other way to do this than with a loop.
417 for (const auto& p : ssList)
418 {
419 planetsCombo->addItem(p->getNameI18n(), p->getEnglishName());
420 }
421 //Restore the selection
422 index = planetsCombo->findData(selectedPlanetId, Qt::UserRole, Qt::MatchCaseSensitive);
423 planetsCombo->setCurrentIndex(index);
424 planetsCombo->model()->sort(0);
425 planetsCombo->blockSignals(false);
426 }
427
populateRegionList(const QString & planet)428 void LocationDialog::populateRegionList(const QString& planet)
429 {
430 Q_ASSERT(ui->regionNameComboBox);
431
432 QComboBox* regionCombo = ui->regionNameComboBox;
433 QStringList regionNames(StelApp::getInstance().getLocationMgr().getRegionNames(planet));
434
435 //Save the current selection to be restored later
436 regionCombo->blockSignals(true);
437 int index = regionCombo->currentIndex();
438 QVariant selectedRegionId = regionCombo->itemData(index);
439 regionCombo->clear();
440 //For each region, display the localized name and store the original as user
441 //data. Unfortunately, there's no other way to do this than with a loop.
442 for (const auto& name : qAsConst(regionNames))
443 regionCombo->addItem(q_(name), name);
444
445 regionCombo->addItem(QChar(0x2014), "");
446 //Restore the selection
447 index = regionCombo->findData(selectedRegionId, Qt::UserRole, Qt::MatchCaseSensitive);
448 regionCombo->setCurrentIndex(index);
449 regionCombo->model()->sort(0);
450 regionCombo->blockSignals(false);
451 }
452
populateTimeZonesList()453 void LocationDialog::populateTimeZonesList()
454 {
455 Q_ASSERT(ui->timeZoneNameComboBox);
456
457 QComboBox* tzCombo = ui->timeZoneNameComboBox;
458 // Return a list of all the known time zone names (from Qt)
459 QStringList tzNames;
460 auto tzList = QTimeZone::availableTimeZoneIds(); // System dependent set of IANA timezone names.
461 for (const auto& tz : qAsConst(tzList))
462 {
463 tzNames.append(tz);
464 // Activate this to get a list of known TZ names...
465 //qDebug() << "Qt/IANA TZ entry from QTimeZone::available: " << tz;
466 }
467 tzNames.sort();
468
469 //Save the current selection to be restored later
470 tzCombo->blockSignals(true);
471 int index = tzCombo->currentIndex();
472 QVariant selectedTzId = tzCombo->itemData(index);
473 tzCombo->clear();
474 //For each time zone, display the localized name and store the original as user
475 //data. Unfortunately, there's no other way to do this than with a loop.
476 for (const auto& name : tzNames)
477 {
478 tzCombo->addItem(name, name);
479 }
480 tzCombo->addItem(q_("Local Mean Solar Time"), "LMST");
481 tzCombo->addItem(q_("Local True Solar Time"), "LTST");
482 tzCombo->addItem(q_("System default"), "system_default");
483 //Restore the selection
484 index = tzCombo->findData(selectedTzId, Qt::UserRole, Qt::MatchCaseSensitive);
485 // TODO: Handle notfound!?
486 if (index==-1)
487 {
488 index=tzCombo->count()-1; // should point to system_default.
489 }
490 Q_ASSERT(index!=-1);
491 tzCombo->setCurrentIndex(index);
492 tzCombo->blockSignals(false);
493 }
494
495 // Create a StelLocation instance from the fields
locationFromFields() const496 StelLocation LocationDialog::locationFromFields() const
497 {
498 StelLocation loc;
499 int index = ui->planetNameComboBox->currentIndex();
500 if (index < 0)
501 {
502 qWarning() << "LocationDialog::locationFromFields(): no valid planet name from combo?";
503 loc.planetName = QString("Earth"); //As returned by QComboBox::currentText()
504 }
505 else
506 loc.planetName = ui->planetNameComboBox->itemData(index).toString();
507 loc.name = ui->cityNameLineEdit->text().trimmed(); // avoid locations with leading whitespace
508 loc.latitude = qBound(-90.0, ui->latitudeSpinBox->valueDegrees(), 90.0);
509 loc.longitude = ui->longitudeSpinBox->valueDegrees();
510 loc.altitude = ui->altitudeSpinBox->value();
511 index = ui->regionNameComboBox->currentIndex();
512 if (index < 0)
513 {
514 qWarning() << "LocationDialog::locationFromFields(): no valid region name from combo?";
515 loc.region = QString();//As returned by QComboBox::currentText()
516 }
517 else
518 loc.region = ui->regionNameComboBox->itemData(index).toString();
519
520 index = ui->timeZoneNameComboBox->currentIndex();
521 if (index < 0)
522 {
523 qWarning() << "LocationDialog::locationFromFields(): no valid timezone name from combo?";
524 loc.ianaTimeZone = QString("UTC"); //Give at least some useful default
525 }
526 else
527 {
528 QString tz=ui->timeZoneNameComboBox->itemData(index).toString();
529 loc.ianaTimeZone = tz;
530 }
531 return loc;
532 }
533
setLocationFromList(const QModelIndex & index)534 void LocationDialog::setLocationFromList(const QModelIndex& index)
535 {
536 isEditingNew=false;
537 ui->addLocationToListPushButton->setEnabled(false);
538 StelLocation loc = StelApp::getInstance().getLocationMgr().locationForString(index.data().toString());
539 setFieldsFromLocation(loc);
540 StelApp::getInstance().getCore()->moveObserverTo(loc, 0.);
541 // This calls indirectly updateFromProgram()
542 }
543
setLocationFromMap(double longitude,double latitude)544 void LocationDialog::setLocationFromMap(double longitude, double latitude)
545 {
546 StelCore *core = StelApp::getInstance().getCore();
547 if (core->getUseCustomTimeZone())
548 customTimeZone=core->getCurrentTimeZone();
549 reportEdit();
550 StelLocation loc = locationFromFields();
551 loc.latitude = latitude;
552 loc.longitude = longitude;
553 setFieldsFromLocation(loc);
554 core->moveObserverTo(loc, 0.);
555 // Only for locations on Earth: set zone to LMST.
556 // TODO: Find a way to lookup (lon,lat)->country->timezone.
557
558 if (core->getUseCustomTimeZone())
559 ui->timeZoneNameComboBox->setCurrentIndex(ui->timeZoneNameComboBox->findData(customTimeZone, Qt::UserRole, Qt::MatchCaseSensitive));
560 else
561 ui->timeZoneNameComboBox->setCurrentIndex(ui->timeZoneNameComboBox->findData("LMST", Qt::UserRole, Qt::MatchCaseSensitive));
562
563 // Filter location list for nearby sites. I assume Earth locations are better known. With only few locations on other planets in the list, 30 degrees seem OK.
564 LocationMap results = StelApp::getInstance().getLocationMgr().pickLocationsNearby(loc.planetName, longitude, latitude, loc.planetName=="Earth" ? 5.0f: 30.0f);
565 pickedModel->setStringList(results.keys());
566 proxyModel->setSourceModel(pickedModel);
567 proxyModel->sort(0, Qt::AscendingOrder);
568 ui->citySearchLineEdit->setText(""); // https://wiki.qt.io/Technical_FAQ#Why_does_the_memory_keep_increasing_when_repeatedly_pasting_text_and_calling_clear.28.29_in_a_QLineEdit.3F
569 }
570
571 // Called when the planet name is changed by hand
moveToAnotherPlanet(const QString &)572 void LocationDialog::moveToAnotherPlanet(const QString&)
573 {
574 reportEdit();
575 StelLocation loc = locationFromFields();
576 StelCore* stelCore = StelApp::getInstance().getCore();
577 StelModule* lMgr = StelApp::getInstance().getModule("LandscapeMgr");
578 populateRegionList(loc.planetName);
579 if (loc.planetName != stelCore->getCurrentLocation().planetName)
580 {
581 setFieldsFromLocation(loc);
582 if (lMgr->property("flagLandscapeAutoSelection").toBool())
583 {
584 // If we have a landscape for selected planet then set it, otherwise use default landscape
585 // Details: https://bugs.launchpad.net/stellarium/+bug/1173254
586 if (lMgr->property("allLandscapeNames").toStringList().indexOf(loc.planetName)>0)
587 lMgr->setProperty("currentLandscapeName", loc.planetName);
588 else
589 lMgr->setProperty("currentLandscapeID", lMgr->property("defaultLandscapeID"));
590 }
591
592 // GZ populate site list with sites only from that planet, or full list for Earth (faster than removing the ~50 non-Earth positions...).
593 StelLocationMgr &locMgr=StelApp::getInstance().getLocationMgr();
594 if (loc.planetName == "Earth")
595 {
596 proxyModel->setSourceModel(allModel);
597 }
598 else
599 {
600 LocationMap results = locMgr.pickLocationsNearby(loc.planetName, 0.0f, 0.0f, 180.0f);
601 pickedModel->setStringList(results.keys());
602 proxyModel->setSourceModel(pickedModel);
603 ui->regionNameComboBox->setCurrentIndex(ui->regionNameComboBox->findData("", Qt::UserRole, Qt::MatchCaseSensitive));
604 // For 0.19, time zone should not change. When we can work out LMST for other planets, we can accept LMST.
605 //if (customTimeZone.isEmpty()) // This is always true!
606 // ui->timeZoneNameComboBox->setCurrentIndex(ui->timeZoneNameComboBox->findData("LMST", Qt::UserRole, Qt::MatchCaseSensitive));
607 }
608 proxyModel->sort(0, Qt::AscendingOrder);
609 ui->citySearchLineEdit->setText(""); // https://wiki.qt.io/Technical_FAQ#Why_does_the_memory_keep_increasing_when_repeatedly_pasting_text_and_calling_clear.28.29_in_a_QLineEdit.3F
610 ui->citySearchLineEdit->setFocus();
611 stelCore->moveObserverTo(loc, 0., 0.);
612 }
613 // Planet transition time also set to null to prevent uglyness when
614 // "use landscape location" is enabled for that planet's landscape. --BM
615 // NOTE: I think it also makes sense in the other cases. --BM
616 }
617
setLocationFromCoords(int i)618 void LocationDialog::setLocationFromCoords(int i)
619 {
620 Q_UNUSED(i)
621 reportEdit();
622 StelLocation loc = locationFromFields();
623 StelApp::getInstance().getCore()->moveObserverTo(loc, 0.);
624 //Update the position of the map pointer
625 ui->mapLabel->setCursorPos(loc.longitude, loc.latitude);
626 }
627
saveTimeZone()628 void LocationDialog::saveTimeZone()
629 {
630 QString tz = ui->timeZoneNameComboBox->itemData(ui->timeZoneNameComboBox->currentIndex()).toString();
631 StelCore* core = StelApp::getInstance().getCore();
632 core->setCurrentTimeZone(tz);
633 if (core->getUseCustomTimeZone())
634 {
635 StelApp::getInstance().getSettings()->setValue("localization/time_zone", tz);
636 }
637 }
638
setTimezone(QString tz)639 void LocationDialog::setTimezone(QString tz)
640 {
641 disconnectEditSignals();
642
643 int idx=ui->timeZoneNameComboBox->findData(tz, Qt::UserRole, Qt::MatchCaseSensitive);
644 if (idx>=0)
645 {
646 ui->timeZoneNameComboBox->setCurrentIndex(idx);
647 }
648 else
649 {
650 qWarning() << "LocationDialog::setTimezone(): invalid name:" << tz;
651 }
652
653 connectEditSignals();
654 }
655
reportEdit()656 void LocationDialog::reportEdit()
657 {
658 if (isEditingNew==false)
659 {
660 // The user starts editing manually a field, this creates automatically a new location
661 // and allows to save it to the user locations list
662 isEditingNew=true;
663 }
664
665 StelLocation loc = locationFromFields();
666 StelLocationMgr& locationMgr = StelApp::getInstance().getLocationMgr();
667 bool canSave = locationMgr.canSaveUserLocation(loc);
668 if (!canSave)
669 {
670 if (ui->cityNameLineEdit->hasFocus())
671 {
672 // The user is editing the location name: don't change it!
673 ui->addLocationToListPushButton->setEnabled(false);
674 ui->deleteLocationFromListPushButton->setEnabled(false);
675 return;
676 }
677 else
678 {
679 ui->cityNameLineEdit->setText("");
680 ui->cityNameLineEdit->selectAll();
681 loc = locationFromFields();
682 }
683 }
684 ui->addLocationToListPushButton->setEnabled(isEditingNew && canSave);
685 ui->deleteLocationFromListPushButton->setEnabled(locationMgr.canDeleteUserLocation(loc.getID()));
686 }
687
688 // Called when the user clicks on the save button
addCurrentLocationToList()689 void LocationDialog::addCurrentLocationToList()
690 {
691 const StelLocation& loc = locationFromFields();
692 ui->citySearchLineEdit->setText(""); // https://wiki.qt.io/Technical_FAQ#Why_does_the_memory_keep_increasing_when_repeatedly_pasting_text_and_calling_clear.28.29_in_a_QLineEdit.3F
693 StelApp::getInstance().getLocationMgr().saveUserLocation(loc);
694 isEditingNew=false;
695 ui->addLocationToListPushButton->setEnabled(false);
696
697 const QAbstractItemModel* model = ui->citiesListView->model();
698 const QString id = loc.getID();
699 for (int i=0;i<model->rowCount();++i)
700 {
701 if (model->index(i,0).data()==id)
702 {
703 ui->citiesListView->selectionModel()->select(model->index(i,0), QItemSelectionModel::ClearAndSelect|QItemSelectionModel::Rows);
704 ui->citiesListView->scrollTo(model->index(i,0));
705 setLocationFromList(model->index(i,0));
706 disconnectEditSignals();
707 ui->citySearchLineEdit->setFocus();
708 connectEditSignals();
709 break;
710 }
711 }
712 }
713
714 // Called when the user wants to use the current location as default
setDefaultLocation(bool state)715 void LocationDialog::setDefaultLocation(bool state)
716 {
717 if (state)
718 {
719 StelCore* core = StelApp::getInstance().getCore();
720 QString currentLocationId = core->getCurrentLocation().getID();
721 core->setDefaultLocationID(currentLocationId);
722
723 // Why this code even exists? After the previous code, this should always
724 // be true, except if setting the default location somehow fails. --BM
725 bool isDefault = (currentLocationId == core->getDefaultLocationID());
726 disconnectEditSignals();
727 updateDefaultLocationControls(isDefault);
728 ui->pushButtonReturnToDefault->setEnabled(!isDefault);
729 ui->useIpQueryCheckBox->setChecked(!state);
730 ui->citySearchLineEdit->setFocus();
731 connectEditSignals();
732 }
733 }
734
735 // Called when the user clicks on the delete button
deleteCurrentLocationFromList()736 void LocationDialog::deleteCurrentLocationFromList()
737 {
738 const StelLocation& loc = locationFromFields();
739 StelApp::getInstance().getLocationMgr().deleteUserLocation(loc.getID());
740 }
741
updateDefaultLocationControls(bool currentIsDefault)742 void LocationDialog::updateDefaultLocationControls(bool currentIsDefault)
743 {
744 ui->useAsDefaultLocationCheckBox->setChecked(currentIsDefault);
745 ui->useAsDefaultLocationCheckBox->setEnabled(!currentIsDefault);
746 }
747
updateTimeZoneControls(bool useCustomTimeZone)748 void LocationDialog::updateTimeZoneControls(bool useCustomTimeZone)
749 {
750 StelCore* core = StelApp::getInstance().getCore();
751 core->setUseCustomTimeZone(useCustomTimeZone);
752
753 if (useCustomTimeZone)
754 {
755 saveTimeZone();
756 }
757 else
758 {
759 StelLocation loc = core->getCurrentLocation();
760 QString tz = loc.ianaTimeZone;
761 if (loc.planetName=="Earth" && tz.isEmpty())
762 tz = "system_default";
763 int idx = ui->timeZoneNameComboBox->findData(tz, Qt::UserRole, Qt::MatchCaseSensitive);
764 if (idx==-1)
765 {
766 QString defTZ = "LMST";
767 if (loc.planetName=="Earth")
768 defTZ = "system_default";
769 // Use LMST/system_default as default
770 idx = ui->timeZoneNameComboBox->findData(defTZ, Qt::UserRole, Qt::MatchCaseSensitive);
771 }
772 ui->timeZoneNameComboBox->setCurrentIndex(idx);
773 StelApp::getInstance().getSettings()->remove("localization/time_zone");
774 core->setUseCustomTimeZone(false);
775 }
776 }
777
778 // called when the user clicks on the IP Query button
ipQueryLocation(bool state)779 void LocationDialog::ipQueryLocation(bool state)
780 {
781 disconnectEditSignals();
782 resetLocationList(); // in case we are on Moon/Mars, we must get list back to show all (earth) locations...
783 StelLocationMgr &locMgr=StelApp::getInstance().getLocationMgr();
784 locMgr.locationFromIP(); // This just triggers asynchronous lookup.
785 // NOTE: These steps seem to assume IP lookup is successful!
786 ui->useAsDefaultLocationCheckBox->setChecked(!state);
787 ui->pushButtonReturnToDefault->setEnabled(!state);
788 updateTimeZoneControls(!state);
789 connectEditSignals();
790 ui->citySearchLineEdit->setFocus();
791 QSettings* conf = StelApp::getInstance().getSettings();
792 if (state)
793 conf->setValue("init_location/location", "auto");
794 else
795 conf->setValue("init_location/location", StelApp::getInstance().getCore()->getCurrentLocation().getID());
796 }
797
798 #ifdef ENABLE_GPS
799 // called when the user toggles the GPS Query button. Use gpsd or Qt's NMEA reader.
gpsEnableQueryLocation(bool running)800 void LocationDialog::gpsEnableQueryLocation(bool running)
801 {
802 if (running)
803 {
804 disconnectEditSignals();
805 gpsCount=0;
806 ui->gpsToolButton->setText(q_("GPS listening..."));
807 StelApp::getInstance().getLocationMgr().locationFromGPS(3000);
808 }
809 else
810 {
811 // (edit signals restored by gpsReturn())
812 StelApp::getInstance().getLocationMgr().locationFromGPS(0);
813 ui->gpsToolButton->setText(q_("GPS disconnecting..."));
814 QTimer::singleShot(1500, this, SLOT(resetGPSbuttonLabel()));
815 }
816 }
817
gpsReturn(bool success)818 void LocationDialog::gpsReturn(bool success)
819 {
820 if (success)
821 {
822 StelCore* core = StelApp::getInstance().getCore();
823
824 gpsCount++;
825 ui->gpsToolButton->setText(QString("%1 %2").arg(q_("GPS location fix")).arg(gpsCount));
826 ui->useAsDefaultLocationCheckBox->setChecked(false);
827 ui->pushButtonReturnToDefault->setEnabled(true);
828 //ui->useCustomTimeZoneCheckBox->setChecked(true); // done by updateTimeZoneControls(true) below.
829 ui->useIpQueryCheckBox->setChecked(false); // Disable IP query option when GPS is used!
830 resetLocationList(); // in case we come back from Moon/Mars, we must get list back to show all (earth) locations...
831 updateTimeZoneControls(true);
832 StelLocation loc=core->getCurrentLocation();
833 setFieldsFromLocation(loc);
834 if (loc.altitude==0) // give feedback of fix quality.
835 {
836 ui->gpsToolButton->setStyleSheet(QString("QToolButton{ background: yellow; }"));
837 }
838 else
839 {
840 ui->gpsToolButton->setStyleSheet(QString("QToolButton{ background: green; }"));
841 }
842 QSettings* conf = StelApp::getInstance().getSettings();
843 conf->setValue("init_location/location", loc.getID());
844 conf->setValue("init_location/last_location", QString("%1, %2, %3").arg(loc.latitude).arg(loc.longitude).arg(loc.altitude));
845 }
846 else
847 {
848 ui->gpsToolButton->setText(q_("GPS:FAILED"));
849 ui->gpsToolButton->setStyleSheet(QString("QToolButton{ background: red; }"));
850 // Use QTimer to reset the labels after 2 seconds.
851 QTimer::singleShot(2000, this, SLOT(resetGPSbuttonLabel()));
852 }
853 connectEditSignals();
854 ui->citySearchLineEdit->setFocus();
855 }
856
resetGPSbuttonLabel()857 void LocationDialog::resetGPSbuttonLabel()
858 {
859 ui->gpsToolButton->setChecked(false);
860 ui->gpsToolButton->setText(q_("Get location from GPS"));
861 ui->gpsToolButton->setStyleSheet(QString("QToolButton{ background: gray; }"));
862 }
863 #endif
864
865 // called when user clicks "reset list"
resetLocationList()866 void LocationDialog::resetLocationList()
867 {
868 //reset search before setting model, prevents unnecessary search in full list
869 ui->citySearchLineEdit->setText(""); // https://wiki.qt.io/Technical_FAQ#Why_does_the_memory_keep_increasing_when_repeatedly_pasting_text_and_calling_clear.28.29_in_a_QLineEdit.3F
870 ui->citySearchLineEdit->setFocus();
871 proxyModel->setSourceModel(allModel);
872 proxyModel->sort(0, Qt::AscendingOrder);
873 }
874
875 // called when user clicks in the country combobox and selects a country. The locations in the list are updated to select only sites in that country.
filterSitesByRegion()876 void LocationDialog::filterSitesByRegion()
877 {
878 QString region=ui->regionNameComboBox->currentData(Qt::UserRole).toString();
879 StelLocationMgr &locMgr=StelApp::getInstance().getLocationMgr();
880
881 LocationMap results = locMgr.pickLocationsInRegion(region);
882 pickedModel->setStringList(results.keys());
883 proxyModel->setSourceModel(pickedModel);
884 proxyModel->sort(0, Qt::AscendingOrder);
885 ui->citySearchLineEdit->setText(""); // https://wiki.qt.io/Technical_FAQ#Why_does_the_memory_keep_increasing_when_repeatedly_pasting_text_and_calling_clear.28.29_in_a_QLineEdit.3F
886 ui->citySearchLineEdit->setFocus();
887 }
888