1 /*
2 SPDX-FileCopyrightText: 2002 Jason Harris <jharris@30doradus.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 // This file contains function definitions for Actions declared in kstars.h
8
9 #include "kstars.h"
10
11 #include "imageexporter.h"
12 #include "kstarsdata.h"
13 #include "kstars_debug.h"
14 #include "ksnotification.h"
15 #include "kswizard.h"
16 #include "Options.h"
17 #include "skymap.h"
18 #include "texturemanager.h"
19 #include "dialogs/exportimagedialog.h"
20 #include "dialogs/finddialog.h"
21 #include "dialogs/focusdialog.h"
22 #include "dialogs/fovdialog.h"
23 #include "dialogs/locationdialog.h"
24 #include "dialogs/timedialog.h"
25 #include "dialogs/catalogsdbui.h"
26 #include "oal/execute.h"
27 #include "oal/equipmentwriter.h"
28 #include "oal/observeradd.h"
29 #include "options/opsadvanced.h"
30 #include "options/opscatalog.h"
31 #include "options/opscolors.h"
32 #include "options/opsguides.h"
33 #include "options/opsterrain.h"
34 #include "options/opssatellites.h"
35 #include "options/opssolarsystem.h"
36 #include "options/opssupernovae.h"
37 #include "printing/printingwizard.h"
38 #include "projections/projector.h"
39 #include "skycomponents/asteroidscomponent.h"
40 #include "skycomponents/cometscomponent.h"
41 #include "skycomponents/satellitescomponent.h"
42 #include "skycomponents/skymapcomposite.h"
43 #include "skycomponents/solarsystemcomposite.h"
44 #include "skycomponents/supernovaecomponent.h"
45 #include "skycomponents/catalogscomponent.h"
46 #include "tools/altvstime.h"
47 #include "tools/astrocalc.h"
48 #include "tools/eyepiecefield.h"
49 #include "tools/flagmanager.h"
50 #include "tools/horizonmanager.h"
51 #include "tools/observinglist.h"
52 #include "tools/planetviewer.h"
53 #include "tools/jmoontool.h"
54 #include "tools/scriptbuilder.h"
55 #include "tools/skycalendar.h"
56 #include "tools/wutdialog.h"
57 #include "tools/polarishourangle.h"
58 #include "tools/whatsinteresting/wiequipsettings.h"
59 #include "tools/whatsinteresting/wilpsettings.h"
60 #include "tools/whatsinteresting/wiview.h"
61 #include "hips/hipsmanager.h"
62 #include "catalogsdb.h"
63 #ifdef HAVE_INDI
64 #include <basedevice.h>
65 //#include "indi/telescopewizardprocess.h"
66 #include "indi/opsindi.h"
67 #include "indi/drivermanager.h"
68 #include "indi/guimanager.h"
69 #include "indi/indilistener.h"
70 #endif
71
72 #ifdef HAVE_CFITSIO
73 #include "fitsviewer/fitsviewer.h"
74 #include "fitsviewer/opsfits.h"
75 #ifdef HAVE_INDI
76 #include "ekos/manager.h"
77 #include "ekos/opsekos.h"
78 #endif
79 #endif
80
81 #include "xplanet/opsxplanet.h"
82
83 #ifdef HAVE_NOTIFYCONFIG
84 #include <KNotifyConfigWidget>
85 #endif
86 #include <KActionCollection>
87 #include <KActionMenu>
88 #include <KTipDialog>
89 #include <KToggleAction>
90 #include <kns3/downloaddialog.h>
91
92 #include <QQuickWindow>
93 #include <QQuickView>
94
95 #ifdef _WIN32
96 #include <windows.h>
97 #undef interface
98 #endif
99 #include <sys/stat.h>
100
101 /** ViewToolBar Action. All of the viewToolBar buttons are connected to this slot. **/
102
slotViewToolBar()103 void KStars::slotViewToolBar()
104 {
105 KToggleAction *a = (KToggleAction *)sender();
106 KConfigDialog *kcd = KConfigDialog::exists("settings");
107
108 if (a == actionCollection()->action("show_stars"))
109 {
110 Options::setShowStars(a->isChecked());
111 if (kcd)
112 {
113 opcatalog->kcfg_ShowStars->setChecked(a->isChecked());
114 }
115 }
116 else if (a == actionCollection()->action("show_deepsky"))
117 {
118 Options::setShowDeepSky(a->isChecked());
119 if (kcd)
120 {
121 opcatalog->kcfg_ShowDeepSky->setChecked(a->isChecked());
122 }
123 }
124 else if (a == actionCollection()->action("show_planets"))
125 {
126 Options::setShowSolarSystem(a->isChecked());
127 if (kcd)
128 {
129 opsolsys->kcfg_ShowSolarSystem->setChecked(a->isChecked());
130 }
131 }
132 else if (a == actionCollection()->action("show_clines"))
133 {
134 Options::setShowCLines(a->isChecked());
135 if (kcd)
136 {
137 opguides->kcfg_ShowCLines->setChecked(a->isChecked());
138 }
139 }
140 else if (a == actionCollection()->action("show_cnames"))
141 {
142 Options::setShowCNames(a->isChecked());
143 if (kcd)
144 {
145 opguides->kcfg_ShowCNames->setChecked(a->isChecked());
146 }
147 }
148 else if (a == actionCollection()->action("show_cbounds"))
149 {
150 Options::setShowCBounds(a->isChecked());
151 if (kcd)
152 {
153 opguides->kcfg_ShowCBounds->setChecked(a->isChecked());
154 }
155 }
156 else if (a == actionCollection()->action("show_constellationart"))
157 {
158 Options::setShowConstellationArt(a->isChecked());
159 if (kcd)
160 {
161 opguides->kcfg_ShowConstellationArt->setChecked(a->isChecked());
162 }
163 }
164 else if (a == actionCollection()->action("show_mw"))
165 {
166 Options::setShowMilkyWay(a->isChecked());
167 if (kcd)
168 {
169 opguides->kcfg_ShowMilkyWay->setChecked(a->isChecked());
170 }
171 }
172 else if (a == actionCollection()->action("show_equatorial_grid"))
173 {
174 // if autoSelectGrid is selected and the user clicked the
175 // show_equatorial_grid button, he probably wants us to disable
176 // the autoSelectGrid and display the equatorial grid.
177 Options::setAutoSelectGrid(false);
178 Options::setShowEquatorialGrid(a->isChecked());
179 if (kcd)
180 {
181 opguides->kcfg_ShowEquatorialGrid->setChecked(a->isChecked());
182 opguides->kcfg_AutoSelectGrid->setChecked(false);
183 }
184 }
185 else if (a == actionCollection()->action("show_horizontal_grid"))
186 {
187 Options::setAutoSelectGrid(false);
188 Options::setShowHorizontalGrid(a->isChecked());
189 if (kcd)
190 {
191 opguides->kcfg_ShowHorizontalGrid->setChecked(a->isChecked());
192 opguides->kcfg_AutoSelectGrid->setChecked(false);
193 }
194 }
195 else if (a == actionCollection()->action("show_horizon"))
196 {
197 Options::setShowGround(a->isChecked());
198 if (!a->isChecked() && Options::useRefraction())
199 {
200 QString caption = i18n("Refraction effects disabled");
201 QString message = i18n("When the horizon is switched off, refraction effects "
202 "are temporarily disabled.");
203
204 KMessageBox::information(this, message, caption, "dag_refract_hide_ground");
205 }
206 if (kcd)
207 {
208 opguides->kcfg_ShowGround->setChecked(a->isChecked());
209 }
210 }
211 else if (a == actionCollection()->action("show_flags"))
212 {
213 Options::setShowFlags(a->isChecked());
214 if (kcd)
215 {
216 opguides->kcfg_ShowFlags->setChecked(a->isChecked());
217 }
218 }
219 else if (a == actionCollection()->action("show_satellites"))
220 {
221 Options::setShowSatellites(a->isChecked());
222 if (kcd)
223 {
224 opssatellites->kcfg_ShowSatellites->setChecked(a->isChecked());
225 }
226 }
227 else if (a == actionCollection()->action("show_supernovae"))
228 {
229 Options::setShowSupernovae(a->isChecked());
230 if (kcd)
231 {
232 opssupernovae->kcfg_ShowSupernovae->setChecked(a->isChecked());
233 }
234 }
235
236 // update time for all objects because they might be not initialized
237 // it's needed when using horizontal coordinates
238 data()->setFullTimeUpdate();
239 updateTime();
240
241 map()->forceUpdate();
242 }
243
slotINDIToolBar()244 void KStars::slotINDIToolBar()
245 {
246 #ifdef HAVE_INDI
247 KToggleAction *a = qobject_cast<KToggleAction *>(sender());
248
249 if (a == actionCollection()->action("show_control_panel"))
250 {
251 if (a->isChecked())
252 {
253 GUIManager::Instance()->raise();
254 GUIManager::Instance()->activateWindow();
255 GUIManager::Instance()->showNormal();
256 }
257 else
258 GUIManager::Instance()->hide();
259 }
260 else if (a == actionCollection()->action("show_ekos"))
261 {
262 if (a->isChecked())
263 {
264 Ekos::Manager::Instance()->raise();
265 Ekos::Manager::Instance()->activateWindow();
266 Ekos::Manager::Instance()->showNormal();
267 }
268 else
269 Ekos::Manager::Instance()->hide();
270 }
271 else if (a == actionCollection()->action("lock_telescope"))
272 {
273 if (INDIListener::Instance()->size() == 0)
274 {
275 KSNotification::sorry(i18n("KStars did not find any active telescopes."));
276 return;
277 }
278
279 ISD::GDInterface *oneScope = nullptr;
280
281 foreach (ISD::GDInterface *gd, INDIListener::Instance()->getDevices())
282 {
283 INDI::BaseDevice *bd = gd->getBaseDevice();
284
285 if (gd->getType() != KSTARS_TELESCOPE)
286 continue;
287
288 if (bd == nullptr)
289 continue;
290
291 if (bd->isConnected() == false)
292 {
293 KMessageBox::error(
294 nullptr,
295 i18n("Telescope %1 is offline. Please connect and retry again.",
296 gd->getDeviceName()));
297 return;
298 }
299
300 oneScope = gd;
301 break;
302 }
303
304 if (oneScope == nullptr)
305 {
306 KSNotification::sorry(i18n("KStars did not find any active telescopes."));
307 return;
308 }
309
310 if (a->isChecked())
311 oneScope->runCommand(INDI_CENTER_LOCK);
312 else
313 oneScope->runCommand(INDI_CENTER_UNLOCK);
314 }
315 else if (a == actionCollection()->action("show_fits_viewer"))
316 {
317 if (m_FITSViewers.isEmpty())
318 {
319 a->setEnabled(false);
320 return;
321 }
322
323 if (a->isChecked())
324 {
325 for (auto &view : m_FITSViewers)
326 {
327 if (view->getTabs().empty() == false)
328 {
329 view->raise();
330 view->activateWindow();
331 view->showNormal();
332 }
333 }
334 }
335 else
336 {
337 for (auto &view : m_FITSViewers)
338 {
339 view->hide();
340 }
341 }
342 }
343 else if (a == actionCollection()->action("show_mount_box"))
344 {
345 Ekos::Manager::Instance()->mountModule()->toggleMountToolBox();
346 }
347 else if (a == actionCollection()->action("show_sensor_fov"))
348 {
349 Options::setShowSensorFOV(a->isChecked());
350 for (auto oneFOV : data()->getTransientFOVs())
351 {
352 if (oneFOV->objectName() == "sensor_fov")
353 oneFOV->setProperty("visible", a->isChecked());
354 }
355 }
356
357 #endif
358 }
359
slotSetTelescopeEnabled(bool enable)360 void KStars::slotSetTelescopeEnabled(bool enable)
361 {
362 telescopeGroup->setEnabled(enable);
363 if (enable == false)
364 {
365 for (QAction *a : telescopeGroup->actions())
366 {
367 a->setChecked(false);
368 }
369 }
370 }
371
slotSetDomeEnabled(bool enable)372 void KStars::slotSetDomeEnabled(bool enable)
373 {
374 domeGroup->setEnabled(enable);
375 if (enable == false)
376 {
377 for (QAction *a : domeGroup->actions())
378 {
379 a->setChecked(false);
380 }
381 }
382 }
383
384 /** Major Dialog Window Actions **/
385
slotCalculator()386 void KStars::slotCalculator()
387 {
388 if (!m_AstroCalc)
389 m_AstroCalc = new AstroCalc(this);
390 m_AstroCalc->show();
391 }
392
slotWizard()393 void KStars::slotWizard()
394 {
395 QPointer<KSWizard> wizard = new KSWizard(this);
396 if (wizard->exec() == QDialog::Accepted)
397 {
398 Options::setRunStartupWizard(false); //don't run on startup next time
399 if (wizard->geo())
400 updateLocationFromWizard(*(wizard->geo()));
401 }
402 }
403
updateLocationFromWizard(const GeoLocation & geo)404 void KStars::updateLocationFromWizard(const GeoLocation &geo)
405 {
406 data()->setLocation(geo);
407 // adjust local time to keep UT the same.
408 // create new LT without DST offset
409 KStarsDateTime ltime = data()->geo()->UTtoLT(data()->ut());
410
411 // reset timezonerule to compute next dst change
412 data()->geo()->tzrule()->reset_with_ltime(ltime, data()->geo()->TZ0(),
413 data()->isTimeRunningForward());
414
415 // reset next dst change time
416 data()->setNextDSTChange(data()->geo()->tzrule()->nextDSTChange());
417
418 // reset local sideral time
419 data()->syncLST();
420
421 // Make sure Numbers, Moon, planets, and sky objects are updated immediately
422 data()->setFullTimeUpdate();
423
424 // If the sky is in Horizontal mode and not tracking, reset focus such that
425 // Alt/Az remain constant.
426 if (!Options::isTracking() && Options::useAltAz())
427 {
428 map()->focus()->HorizontalToEquatorial(data()->lst(), data()->geo()->lat());
429 }
430
431 // recalculate new times and objects
432 data()->setSnapNextFocus();
433 updateTime();
434 }
435
slotDownload()436 void KStars::slotDownload()
437 {
438 KSNotification::event(
439 QLatin1String("KnownIssue"),
440 i18n("Due to a known issue in the kde frameworks, "
441 "updating already downloaded items is currently not possible. <br> "
442 "Please uninstall and reinstall them to update."));
443
444 // 2017-07-04: Explicitly load kstars.knsrc from resources file
445 auto dlg = std::make_unique<KNS3::DownloadDialog>(":/kconfig/kstars.knsrc", this);
446
447 if (!dlg)
448 return;
449
450 dlg->exec();
451
452 // Get the list of all the installed entries.
453 const auto changed_entries = dlg->changedEntries();
454
455 CatalogsDB::DBManager manager{ CatalogsDB::dso_db_path() };
456 for (const KNS3::Entry &entry : changed_entries)
457 {
458 if (entry.category() != "dso")
459 continue;
460 const auto id = entry.id().toInt();
461
462 if (entry.status() == KNS3::Entry::Installed)
463 for (const QString &name : entry.installedFiles())
464 {
465 if (name.endsWith(CatalogsDB::db_file_extension))
466 {
467 const auto meta{ CatalogsDB::read_catalog_meta_from_file(name) };
468
469 if (!meta.first)
470 {
471 QMessageBox::critical(
472 this, i18n("Error"),
473 i18n("The catalog \"%1\" is corrupt.", entry.name()));
474 continue;
475 }
476
477 if (meta.second.id != id)
478 {
479 QMessageBox::critical(
480 this, i18n("Error"),
481 i18n("The catalog \"%1\" is corrupt.<br>Expected id=%2 but "
482 "got id=%3",
483 entry.name(), id, meta.second.id));
484 continue;
485 }
486
487 const auto success{ manager.import_catalog(name, true) };
488 if (!success.first)
489 QMessageBox::critical(
490 this, i18n("Error"),
491 i18n("Could not import the catalog \"%1\"<br>%2",
492 entry.name(), success.second));
493 }
494 }
495
496 if (entry.status() == KNS3::Entry::Deleted)
497 {
498 manager.remove_catalog(id);
499 }
500 }
501
502 TextureManager::discoverTextureDirs();
503 KStars::Instance()->data()->skyComposite()->reloadDeepSky();
504 KStars::Instance()->data()->setFullTimeUpdate();
505 KStars::Instance()->updateTime();
506 KStars::Instance()->map()->forceUpdate();
507 }
508
slotAVT()509 void KStars::slotAVT()
510 {
511 if (!m_AltVsTime)
512 m_AltVsTime = new AltVsTime(this);
513 m_AltVsTime->show();
514 }
515
slotWUT()516 void KStars::slotWUT()
517 {
518 if (!m_WUTDialog)
519 m_WUTDialog = new WUTDialog(this);
520 m_WUTDialog->show();
521 }
522
523 //FIXME Port to QML2
524 //#if 0
slotWISettings()525 void KStars::slotWISettings()
526 {
527 if (!m_WIView)
528 slotToggleWIView();
529 if (m_WIView && !m_wiDock->isVisible())
530 slotToggleWIView();
531
532 if (KConfigDialog::showDialog("wisettings"))
533 {
534 m_WIEquipmentSettings->populateScopeListWidget();
535 return;
536 }
537
538 KConfigDialog *dialog = new KConfigDialog(this, "wisettings", Options::self());
539
540 connect(dialog, SIGNAL(settingsChanged(QString)), this,
541 SLOT(slotApplyWIConfigChanges()));
542
543 m_WISettings = new WILPSettings(this);
544 m_WIEquipmentSettings = new WIEquipSettings();
545 dialog->addPage(m_WISettings, i18n("Light Pollution Settings"));
546 dialog->addPage(m_WIEquipmentSettings,
547 i18n("Equipment Settings - Equipment Type and Parameters"));
548 dialog->exec();
549 if (m_WIEquipmentSettings)
550 m_WIEquipmentSettings->setAperture(); //Something isn't working with this!
551 }
552
slotToggleWIView()553 void KStars::slotToggleWIView()
554 {
555 if (KStars::Closing)
556 return;
557
558 if (!m_WIView)
559 {
560 m_WIView = new WIView(nullptr);
561 m_wiDock = new QDockWidget(this);
562 m_wiDock->setStyleSheet("QDockWidget::title{background-color:black;}");
563 m_wiDock->setObjectName("What's Interesting");
564 m_wiDock->setAllowedAreas(Qt::RightDockWidgetArea);
565 QWidget *container = QWidget::createWindowContainer(m_WIView->getWIBaseView());
566 m_wiDock->setWidget(container);
567 m_wiDock->setMinimumWidth(400);
568 addDockWidget(Qt::RightDockWidgetArea, m_wiDock);
569 connect(m_wiDock, SIGNAL(visibilityChanged(bool)),
570 actionCollection()->action("show_whatsinteresting"),
571 SLOT(setChecked(bool)));
572 m_wiDock->setVisible(true);
573 }
574 else
575 {
576 m_wiDock->setVisible(!m_wiDock->isVisible());
577 }
578 }
579
slotCalendar()580 void KStars::slotCalendar()
581 {
582 if (!m_SkyCalendar)
583 m_SkyCalendar = new SkyCalendar(this);
584 m_SkyCalendar->show();
585 }
586
slotGlossary()587 void KStars::slotGlossary()
588 {
589 // GlossaryDialog *dlg = new GlossaryDialog( this, true );
590 // QString glossaryfile =data()->stdDirs->findResource( "data", "kstars/glossary.xml" );
591 // QUrl u = glossaryfile;
592 // Glossary *g = new Glossary( u );
593 // g->setName( i18n( "Knowledge" ) );
594 // dlg->addGlossary( g );
595 // dlg->show();
596 }
597
slotScriptBuilder()598 void KStars::slotScriptBuilder()
599 {
600 if (!m_ScriptBuilder)
601 m_ScriptBuilder = new ScriptBuilder(this);
602 m_ScriptBuilder->show();
603 }
604
slotSolarSystem()605 void KStars::slotSolarSystem()
606 {
607 if (!m_PlanetViewer)
608 m_PlanetViewer = new PlanetViewer(this);
609 m_PlanetViewer->show();
610 }
611
slotJMoonTool()612 void KStars::slotJMoonTool()
613 {
614 if (!m_JMoonTool)
615 m_JMoonTool = new JMoonTool(this);
616 m_JMoonTool->show();
617 }
618
slotMoonPhaseTool()619 void KStars::slotMoonPhaseTool()
620 {
621 //FIXME Port to KF5
622 //if( ! mpt ) mpt = new MoonPhaseTool( this );
623 //mpt->show();
624 }
625
slotFlagManager()626 void KStars::slotFlagManager()
627 {
628 if (!m_FlagManager)
629 m_FlagManager = new FlagManager(this);
630 m_FlagManager->show();
631 }
632
633 #if 0
634 void KStars::slotTelescopeWizard()
635 {
636 #ifdef HAVE_INDI
637 #ifndef Q_OS_WIN
638
639 QString indiServerDir = Options::indiServer();
640
641 #ifdef Q_OS_OSX
642 if (Options::indiServerIsInternal())
643 indiServerDir = QCoreApplication::applicationDirPath() + "/indi";
644 else
645 indiServerDir = QFileInfo(Options::indiServer()).dir().path();
646 #endif
647
648 QStringList paths;
649 paths << "/usr/bin"
650 << "/usr/local/bin" << indiServerDir;
651
652 if (QStandardPaths::findExecutable("indiserver").isEmpty())
653 {
654 if (QStandardPaths::findExecutable("indiserver", paths).isEmpty())
655 {
656 KSNotification::error(i18n("Unable to find INDI server. Please make sure the package that provides "
657 "the 'indiserver' binary is installed."));
658 return;
659 }
660 }
661 #endif
662
663 QPointer<telescopeWizardProcess> twiz = new telescopeWizardProcess(this);
664 twiz->exec();
665 delete twiz;
666 #endif
667 }
668 #endif
669
slotINDIPanel()670 void KStars::slotINDIPanel()
671 {
672 #ifdef HAVE_INDI
673 #ifndef Q_OS_WIN
674
675 QString indiServerDir = Options::indiServer();
676
677 #ifdef Q_OS_OSX
678 if (Options::indiServerIsInternal())
679 indiServerDir = QCoreApplication::applicationDirPath() + "/indi";
680 else
681 indiServerDir = QFileInfo(Options::indiServer()).dir().path();
682 #endif
683
684 QStringList paths;
685 paths << "/usr/bin"
686 << "/usr/local/bin" << indiServerDir;
687
688 if (QStandardPaths::findExecutable("indiserver").isEmpty())
689 {
690 if (QStandardPaths::findExecutable("indiserver", paths).isEmpty())
691 {
692 KSNotification::error(i18n(
693 "Unable to find INDI server. Please make sure the package that provides "
694 "the 'indiserver' binary is installed."));
695 return;
696 }
697 }
698 #endif
699 GUIManager::Instance()->updateStatus(true);
700 #endif
701 }
702
slotINDIDriver()703 void KStars::slotINDIDriver()
704 {
705 #ifdef HAVE_INDI
706 #ifndef Q_OS_WIN
707
708 if (KMessageBox::warningContinueCancel(
709 nullptr,
710 i18n("INDI Device Manager should only be used by advanced technical users. "
711 "It cannot be used with Ekos. Do you still want to open INDI device "
712 "manager?"),
713 i18n("INDI Device Manager"), KStandardGuiItem::cont(),
714 KStandardGuiItem::cancel(),
715 "indi_device_manager_warning") == KMessageBox::Cancel)
716 return;
717
718 QString indiServerDir = Options::indiServer();
719
720 #ifdef Q_OS_OSX
721 if (Options::indiServerIsInternal())
722 indiServerDir = QCoreApplication::applicationDirPath() + "/indi";
723 else
724 indiServerDir = QFileInfo(Options::indiServer()).dir().path();
725 #endif
726
727 QStringList paths;
728 paths << "/usr/bin"
729 << "/usr/local/bin" << indiServerDir;
730
731 if (QStandardPaths::findExecutable("indiserver").isEmpty())
732 {
733 if (QStandardPaths::findExecutable("indiserver", paths).isEmpty())
734 {
735 KSNotification::error(i18n(
736 "Unable to find INDI server. Please make sure the package that provides "
737 "the 'indiserver' binary is installed."));
738 return;
739 }
740 }
741 #endif
742
743 DriverManager::Instance()->raise();
744 DriverManager::Instance()->activateWindow();
745 DriverManager::Instance()->showNormal();
746
747 #endif
748 }
749
slotEkos()750 void KStars::slotEkos()
751 {
752 #ifdef HAVE_CFITSIO
753 #ifdef HAVE_INDI
754
755 #ifndef Q_OS_WIN
756
757 QString indiServerDir = Options::indiServer();
758
759 #ifdef Q_OS_OSX
760 if (Options::indiServerIsInternal())
761 indiServerDir = QCoreApplication::applicationDirPath() + "/indi";
762 else
763 indiServerDir = QFileInfo(Options::indiServer()).dir().path();
764 #endif
765
766 QStringList paths;
767 paths << "/usr/bin"
768 << "/usr/local/bin" << indiServerDir;
769
770 if (QStandardPaths::findExecutable("indiserver").isEmpty())
771 {
772 if (QStandardPaths::findExecutable("indiserver", paths).isEmpty())
773 {
774 KSNotification::error(i18n(
775 "Unable to find INDI server. Please make sure the package that provides "
776 "the 'indiserver' binary is installed."));
777 return;
778 }
779 }
780 #endif
781
782 if (Ekos::Manager::Instance()->isVisible() &&
783 Ekos::Manager::Instance()->isActiveWindow())
784 {
785 Ekos::Manager::Instance()->hide();
786 }
787 else
788 {
789 Ekos::Manager::Instance()->raise();
790 Ekos::Manager::Instance()->activateWindow();
791 Ekos::Manager::Instance()->showNormal();
792 }
793
794 #endif
795 #endif
796 }
797
slotINDITelescopeTrack()798 void KStars::slotINDITelescopeTrack()
799 {
800 #ifdef HAVE_INDI
801 if (m_KStarsData == nullptr || INDIListener::Instance() == nullptr)
802 return;
803
804 for (auto *gd : INDIListener::Instance()->getDevices())
805 {
806 ISD::Telescope *telescope = dynamic_cast<ISD::Telescope *>(gd);
807
808 if (telescope != nullptr && telescope->isConnected())
809 {
810 KToggleAction *a = qobject_cast<KToggleAction *>(sender());
811
812 if (a != nullptr)
813 {
814 telescope->setTrackEnabled(a->isChecked());
815 return;
816 }
817 }
818 }
819 #endif
820 }
821
slotINDITelescopeSlew(bool focused_object)822 void KStars::slotINDITelescopeSlew(bool focused_object)
823 {
824 #ifdef HAVE_INDI
825 if (m_KStarsData == nullptr || INDIListener::Instance() == nullptr)
826 return;
827
828 for (auto *gd : INDIListener::Instance()->getDevices())
829 {
830 ISD::Telescope *telescope = dynamic_cast<ISD::Telescope *>(gd);
831
832 if (telescope != nullptr && telescope->isConnected())
833 {
834 if (focused_object)
835 {
836 if (m_SkyMap->focusObject() != nullptr)
837 telescope->Slew(m_SkyMap->focusObject());
838 }
839 else
840 telescope->Slew(m_SkyMap->mousePoint());
841
842 return;
843 }
844 }
845 #else
846 Q_UNUSED(focused_object)
847 #endif
848 }
849
slotINDITelescopeSlewMousePointer()850 void KStars::slotINDITelescopeSlewMousePointer()
851 {
852 #ifdef HAVE_INDI
853 slotINDITelescopeSlew(false);
854 #endif
855 }
856
slotINDITelescopeSync(bool focused_object)857 void KStars::slotINDITelescopeSync(bool focused_object)
858 {
859 #ifdef HAVE_INDI
860 if (m_KStarsData == nullptr || INDIListener::Instance() == nullptr)
861 return;
862
863 for (auto *gd : INDIListener::Instance()->getDevices())
864 {
865 ISD::Telescope *telescope = dynamic_cast<ISD::Telescope *>(gd);
866
867 if (telescope != nullptr && telescope->isConnected() && telescope->canSync())
868 {
869 if (focused_object)
870 {
871 if (m_SkyMap->focusObject() != nullptr)
872 telescope->Sync(m_SkyMap->focusObject());
873 }
874 else
875 telescope->Sync(m_SkyMap->mousePoint());
876
877 return;
878 }
879 }
880 #else
881 Q_UNUSED(focused_object)
882 #endif
883 }
884
slotINDITelescopeSyncMousePointer()885 void KStars::slotINDITelescopeSyncMousePointer()
886 {
887 #ifdef HAVE_INDI
888 slotINDITelescopeSync(false);
889 #endif
890 }
891
slotINDITelescopeAbort()892 void KStars::slotINDITelescopeAbort()
893 {
894 #ifdef HAVE_INDI
895 if (m_KStarsData == nullptr || INDIListener::Instance() == nullptr)
896 return;
897
898 for (auto *gd : INDIListener::Instance()->getDevices())
899 {
900 ISD::Telescope *telescope = dynamic_cast<ISD::Telescope *>(gd);
901
902 if (telescope != nullptr && telescope->isConnected())
903 {
904 telescope->Abort();
905 return;
906 }
907 }
908 #endif
909 }
910
slotINDITelescopePark()911 void KStars::slotINDITelescopePark()
912 {
913 #ifdef HAVE_INDI
914 if (m_KStarsData == nullptr || INDIListener::Instance() == nullptr)
915 return;
916
917 for (auto *gd : INDIListener::Instance()->getDevices())
918 {
919 ISD::Telescope *telescope = dynamic_cast<ISD::Telescope *>(gd);
920
921 if (telescope != nullptr && telescope->isConnected() && telescope->canPark())
922 {
923 telescope->Park();
924 return;
925 }
926 }
927 #endif
928 }
929
slotINDITelescopeUnpark()930 void KStars::slotINDITelescopeUnpark()
931 {
932 #ifdef HAVE_INDI
933 if (m_KStarsData == nullptr || INDIListener::Instance() == nullptr)
934 return;
935
936 for (auto *gd : INDIListener::Instance()->getDevices())
937 {
938 ISD::Telescope *telescope = dynamic_cast<ISD::Telescope *>(gd);
939
940 if (telescope != nullptr && telescope->isConnected() && telescope->canPark())
941 {
942 telescope->UnPark();
943 return;
944 }
945 }
946 #endif
947 }
948
slotINDIDomePark()949 void KStars::slotINDIDomePark()
950 {
951 #ifdef HAVE_INDI
952 if (m_KStarsData == nullptr || INDIListener::Instance() == nullptr)
953 return;
954
955 for (auto *gd : INDIListener::Instance()->getDevices())
956 {
957 ISD::Dome *dome = dynamic_cast<ISD::Dome *>(gd);
958
959 if (dome != nullptr && dome->isConnected() && dome->canPark())
960 {
961 dome->Park();
962 return;
963 }
964 }
965 #endif
966 }
967
slotINDIDomeUnpark()968 void KStars::slotINDIDomeUnpark()
969 {
970 #ifdef HAVE_INDI
971 if (m_KStarsData == nullptr || INDIListener::Instance() == nullptr)
972 return;
973
974 for (auto *gd : INDIListener::Instance()->getDevices())
975 {
976 ISD::Dome *dome = dynamic_cast<ISD::Dome *>(gd);
977
978 if (dome != nullptr && dome->isConnected() && dome->canPark())
979 {
980 dome->UnPark();
981 return;
982 }
983 }
984 #endif
985 }
986
slotGeoLocator()987 void KStars::slotGeoLocator()
988 {
989 QPointer<LocationDialog> locationdialog = new LocationDialog(this);
990 if (locationdialog->exec() == QDialog::Accepted)
991 {
992 GeoLocation *newLocation = locationdialog->selectedCity();
993 if (newLocation)
994 {
995 // set new location in options
996 data()->setLocation(*newLocation);
997
998 // adjust local time to keep UT the same.
999 // create new LT without DST offset
1000 KStarsDateTime ltime = newLocation->UTtoLT(data()->ut());
1001
1002 // reset timezonerule to compute next dst change
1003 newLocation->tzrule()->reset_with_ltime(ltime, newLocation->TZ0(),
1004 data()->isTimeRunningForward());
1005
1006 // reset next dst change time
1007 data()->setNextDSTChange(newLocation->tzrule()->nextDSTChange());
1008
1009 // reset local sideral time
1010 data()->syncLST();
1011
1012 // Make sure Numbers, Moon, planets, and sky objects are updated immediately
1013 data()->setFullTimeUpdate();
1014
1015 // If the sky is in Horizontal mode and not tracking, reset focus such that
1016 // Alt/Az remain constant.
1017 if (!Options::isTracking() && Options::useAltAz())
1018 {
1019 map()->focus()->HorizontalToEquatorial(data()->lst(),
1020 data()->geo()->lat());
1021 }
1022
1023 // recalculate new times and objects
1024 data()->setSnapNextFocus();
1025 updateTime();
1026 }
1027 }
1028 delete locationdialog;
1029 }
1030
slotViewOps()1031 void KStars::slotViewOps()
1032 {
1033 // An instance of your dialog could be already created and could be cached,
1034 // in which case you want to display the cached dialog instead of creating
1035 // another one
1036 prepareOps()->show();
1037 }
1038
prepareOps()1039 KConfigDialog *KStars::prepareOps()
1040 {
1041 KConfigDialog *dialog = KConfigDialog::exists("settings");
1042 if (nullptr != dialog)
1043 return dialog;
1044
1045 // KConfigDialog didn't find an instance of this dialog, so lets create it :
1046 dialog = new KConfigDialog(this, "settings", Options::self());
1047
1048 // For some reason the dialog does not resize to contents
1049 // so we set initial 'resonable' size here. Any better way to do this?
1050 dialog->resize(800, 600);
1051 #ifdef Q_OS_OSX
1052 dialog->setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
1053 #endif
1054
1055 connect(dialog, SIGNAL(settingsChanged(QString)), this,
1056 SLOT(slotApplyConfigChanges()));
1057
1058 opcatalog = new OpsCatalog();
1059 opguides = new OpsGuides();
1060 opterrain = new OpsTerrain();
1061 opsolsys = new OpsSolarSystem();
1062 opssatellites = new OpsSatellites();
1063 opssupernovae = new OpsSupernovae();
1064 opcolors = new OpsColors();
1065 opadvanced = new OpsAdvanced();
1066
1067 KPageWidgetItem *page;
1068
1069 page = dialog->addPage(opcatalog, i18n("Catalogs"), "kstars_catalog");
1070 page->setIcon(QIcon::fromTheme("kstars_catalog"));
1071
1072 page = dialog->addPage(opsolsys, i18n("Solar System"), "kstars_solarsystem");
1073 page->setIcon(QIcon::fromTheme("kstars_solarsystem"));
1074
1075 page = dialog->addPage(opssatellites, i18n("Satellites"), "kstars_satellites");
1076 page->setIcon(QIcon::fromTheme("kstars_satellites"));
1077
1078 page = dialog->addPage(opssupernovae, i18n("Supernovae"), "kstars_supernovae");
1079 page->setIcon(QIcon::fromTheme("kstars_supernovae"));
1080
1081 page = dialog->addPage(opguides, i18n("Guides"), "kstars_guides");
1082 page->setIcon(QIcon::fromTheme("kstars_guides"));
1083
1084 page = dialog->addPage(opterrain, i18n("Terrain"), "kstars_terrain");
1085 page->setIcon(QIcon::fromTheme("kstars_terrain", QIcon(":/icons/kstars_terrain.png")));
1086
1087
1088 page = dialog->addPage(opcolors, i18n("Colors"), "kstars_colors");
1089 page->setIcon(QIcon::fromTheme("kstars_colors"));
1090
1091 #ifdef HAVE_CFITSIO
1092 opsfits = new OpsFITS();
1093 page = dialog->addPage(opsfits, i18n("FITS"), "kstars_fitsviewer");
1094 page->setIcon(QIcon::fromTheme("kstars_fitsviewer"));
1095 #endif
1096
1097 #ifdef HAVE_INDI
1098 opsindi = new OpsINDI();
1099 page = dialog->addPage(opsindi, i18n("INDI"), "kstars_indi");
1100 page->setIcon(QIcon::fromTheme("kstars_indi"));
1101 #ifdef HAVE_CFITSIO
1102 opsekos = new OpsEkos();
1103 KPageWidgetItem *ekosOption = dialog->addPage(opsekos, i18n("Ekos"), "kstars_ekos");
1104 ekosOption->setIcon(QIcon::fromTheme("kstars_ekos"));
1105 if (m_EkosManager)
1106 m_EkosManager->setOptionsWidget(ekosOption);
1107 #endif
1108
1109 #endif
1110
1111 opsxplanet = new OpsXplanet(this);
1112 page = dialog->addPage(opsxplanet, i18n("Xplanet"), "kstars_xplanet");
1113 page->setIcon(QIcon::fromTheme("kstars_xplanet"));
1114
1115 page = dialog->addPage(opadvanced, i18n("Advanced"), "kstars_advanced");
1116 page->setIcon(QIcon::fromTheme("kstars_advanced"));
1117
1118 return dialog;
1119 }
1120
syncOps()1121 void KStars::syncOps()
1122 {
1123 opterrain->syncOptions();
1124 actionCollection()->action("toggle_terrain")
1125 ->setText(Options::showTerrain() ? i18n("Hide Terrain") : i18n("Show Terrain"));
1126 }
1127
slotApplyConfigChanges()1128 void KStars::slotApplyConfigChanges()
1129 {
1130 Options::self()->save();
1131
1132 applyConfig();
1133
1134 //data()->setFullTimeUpdate();
1135 //map()->forceUpdate();
1136 }
1137
slotApplyWIConfigChanges()1138 void KStars::slotApplyWIConfigChanges()
1139 {
1140 Options::self()->save();
1141 applyConfig();
1142 m_WIView->updateObservingConditions();
1143 m_WIView->onReloadIconClicked();
1144 }
1145
slotSetTime()1146 void KStars::slotSetTime()
1147 {
1148 QPointer<TimeDialog> timedialog = new TimeDialog(data()->lt(), data()->geo(), this);
1149
1150 if (timedialog->exec() == QDialog::Accepted)
1151 {
1152 data()->changeDateTime(data()->geo()->LTtoUT(timedialog->selectedDateTime()));
1153
1154 if (Options::useAltAz())
1155 {
1156 if (map()->focusObject())
1157 {
1158 map()->focusObject()->EquatorialToHorizontal(data()->lst(),
1159 data()->geo()->lat());
1160 map()->setFocus(map()->focusObject());
1161 }
1162 else
1163 map()->focus()->HorizontalToEquatorial(data()->lst(),
1164 data()->geo()->lat());
1165 }
1166
1167 map()->forceUpdateNow();
1168
1169 //If focusObject has a Planet Trail, clear it and start anew.
1170 KSPlanetBase *planet = dynamic_cast<KSPlanetBase *>(map()->focusObject());
1171 if (planet && planet->hasTrail())
1172 {
1173 planet->clearTrail();
1174 planet->addToTrail();
1175 }
1176 }
1177 delete timedialog;
1178 }
1179
1180 //Set Time to CPU clock
slotSetTimeToNow()1181 void KStars::slotSetTimeToNow()
1182 {
1183 data()->changeDateTime(KStarsDateTime::currentDateTimeUtc());
1184
1185 if (Options::useAltAz())
1186 {
1187 if (map()->focusObject())
1188 {
1189 map()->focusObject()->EquatorialToHorizontal(data()->lst(),
1190 data()->geo()->lat());
1191 map()->setFocus(map()->focusObject());
1192 }
1193 else
1194 map()->focus()->HorizontalToEquatorial(data()->lst(), data()->geo()->lat());
1195 }
1196
1197 map()->forceUpdateNow();
1198
1199 //If focusObject has a Planet Trail, clear it and start anew.
1200 KSPlanetBase *planet = dynamic_cast<KSPlanetBase *>(map()->focusObject());
1201 if (planet && planet->hasTrail())
1202 {
1203 planet->clearTrail();
1204 planet->addToTrail();
1205 }
1206 }
1207
slotFind()1208 void KStars::slotFind()
1209 {
1210 //clearCachedFindDialog();
1211 SkyObject *targetObject = nullptr;
1212 if (FindDialog::Instance()->exec() == QDialog::Accepted &&
1213 (targetObject = FindDialog::Instance()->targetObject()))
1214 {
1215 map()->setClickedObject(targetObject);
1216 map()->setClickedPoint(map()->clickedObject());
1217 map()->slotCenter();
1218 }
1219
1220 // check if data has changed while dialog was open
1221 //if (DialogIsObsolete)
1222 // clearCachedFindDialog();
1223 }
1224
slotOpenFITS()1225 void KStars::slotOpenFITS()
1226 {
1227 #ifdef HAVE_CFITSIO
1228
1229 static QUrl path = QUrl::fromLocalFile(QDir::homePath());
1230 QUrl fileURL =
1231 QFileDialog::getOpenFileUrl(KStars::Instance(), i18nc("@title:window", "Open Image"), path,
1232 "Images (*.fits *.fits.fz *.fit *.fts "
1233 "*.jpg *.jpeg *.png *.gif *.bmp "
1234 "*.cr2 *.cr3 *.crw *.nef *.raf *.dng *.arw *.orf)");
1235 if (fileURL.isEmpty())
1236 return;
1237
1238 // Remember last directory
1239 path.setUrl(fileURL.url(QUrl::RemoveFilename));
1240
1241 QPointer<FITSViewer> fv = createFITSViewer();
1242 //fv.reset(new FITSViewer((Options::independentWindowFITS()) ? nullptr : this));
1243
1244 // connect(fv.get(), &FITSViewer::loaded, [ &, fv]()
1245 // {
1246 // addFITSViewer(fv);
1247 // fv->show();
1248 // });
1249
1250 fv->loadFile(fileURL, FITS_NORMAL, FITS_NONE, QString(), false);
1251 #endif
1252 }
1253
slotExportImage()1254 void KStars::slotExportImage()
1255 {
1256 //TODO Check this
1257 //For remote files, this returns
1258 //QFileInfo::absolutePath: QFileInfo::absolutePath: Constructed with empty filename
1259 //As of 2014-07-19
1260 //QUrl fileURL = KFileDialog::getSaveUrl( QDir::homePath(), "image/png image/jpeg image/gif image/x-portable-pixmap image/bmp image/svg+xml" );
1261 QUrl fileURL =
1262 QFileDialog::getSaveFileUrl(KStars::Instance(), i18nc("@title:window", "Export Image"), QUrl(),
1263 "Images (*.png *.jpeg *.gif *.bmp *.svg)");
1264
1265 //User cancelled file selection dialog - abort image export
1266 if (fileURL.isEmpty())
1267 {
1268 return;
1269 }
1270
1271 //Warn user if file exists!
1272 if (QFile::exists(fileURL.toLocalFile()))
1273 {
1274 int r = KMessageBox::warningContinueCancel(
1275 parentWidget(),
1276 i18n("A file named \"%1\" already exists. Overwrite it?", fileURL.fileName()),
1277 i18n("Overwrite File?"), KStandardGuiItem::overwrite());
1278 if (r == KMessageBox::Cancel)
1279 return;
1280 }
1281
1282 // execute image export dialog
1283
1284 // Note: We don't let ExportImageDialog create its own ImageExporter because we want legend settings etc to be remembered between UI use and DBus scripting interface use.
1285 //if ( !m_ImageExporter )
1286 //m_ImageExporter = new ImageExporter( this );
1287
1288 if (!m_ExportImageDialog)
1289 {
1290 m_ExportImageDialog = new ExportImageDialog(
1291 fileURL.toLocalFile(), QSize(map()->width(), map()->height()),
1292 KStarsData::Instance()->imageExporter());
1293 }
1294 else
1295 {
1296 m_ExportImageDialog->setOutputUrl(fileURL.toLocalFile());
1297 m_ExportImageDialog->setOutputSize(QSize(map()->width(), map()->height()));
1298 }
1299
1300 m_ExportImageDialog->show();
1301 }
1302
slotRunScript()1303 void KStars::slotRunScript()
1304 {
1305 QUrl fileURL = QFileDialog::getOpenFileUrl(
1306 KStars::Instance(), QString(), QUrl(QDir::homePath()),
1307 "*.kstars|" +
1308 i18nc("Filter by file type: KStars Scripts.", "KStars Scripts (*.kstars)"));
1309 QFile f;
1310 //QString fname;
1311
1312 if (fileURL.isValid())
1313 {
1314 if (fileURL.isLocalFile() == false)
1315 {
1316 KSNotification::sorry(i18n("Executing remote scripts is not supported."));
1317 return;
1318 }
1319
1320 f.setFileName(fileURL.toLocalFile());
1321
1322 if (!f.open(QIODevice::ReadOnly))
1323 {
1324 QString message = i18n("Could not open file %1", f.fileName());
1325 KSNotification::sorry(message, i18n("Could Not Open File"));
1326 return;
1327 }
1328
1329 QTextStream istream(&f);
1330 QString line;
1331 bool fileOK(true);
1332
1333 while (!istream.atEnd())
1334 {
1335 line = istream.readLine();
1336 if (line.at(0) != '#' && line.left(9) != "dbus-send")
1337 {
1338 fileOK = false;
1339 break;
1340 }
1341 }
1342
1343 if (!fileOK)
1344 {
1345 int answer;
1346 answer = KMessageBox::warningContinueCancel(
1347 nullptr,
1348 i18n(
1349 "The selected script contains unrecognized elements, "
1350 "indicating that it was not created using the KStars script builder. "
1351 "This script may not function properly, and it may even contain "
1352 "malicious code. "
1353 "Would you like to execute it anyway?"),
1354 i18n("Script Validation Failed"), KGuiItem(i18n("Run Nevertheless")),
1355 KStandardGuiItem::cancel(), "daExecuteScript");
1356 if (answer == KMessageBox::Cancel)
1357 return;
1358 }
1359
1360 //Add statusbar message that script is running
1361 statusBar()->showMessage(i18n("Running script: %1", fileURL.fileName()));
1362
1363 // 2017-09-19: Jasem
1364 // FIXME This is a hack and does not work on non-Linux systems
1365 // The Script Builder should generate files that can run cross-platform
1366 QProcess p;
1367 p.start(f.fileName());
1368 if (!p.waitForStarted())
1369 return;
1370
1371 while (!p.waitForFinished(10))
1372 {
1373 qApp->processEvents(); //otherwise tempfile may get deleted before script completes.
1374 if (p.state() != QProcess::Running)
1375 break;
1376 }
1377
1378 statusBar()->showMessage(i18n("Script finished."), 0);
1379 }
1380 }
1381
slotPrint()1382 void KStars::slotPrint()
1383 {
1384 bool switchColors(false);
1385
1386 //Suggest Chart color scheme
1387 if (data()->colorScheme()->colorNamed("SkyColor") != QColor(255, 255, 255))
1388 {
1389 QString message =
1390 i18n("You can save printer ink by using the \"Star Chart\" "
1391 "color scheme, which uses a white background. Would you like to "
1392 "temporarily switch to the Star Chart color scheme for printing?");
1393
1394 int answer = KMessageBox::questionYesNoCancel(
1395 nullptr, message, i18n("Switch to Star Chart Colors?"),
1396 KGuiItem(i18n("Switch Color Scheme")), KGuiItem(i18n("Do Not Switch")),
1397 KStandardGuiItem::cancel(), "askAgainPrintColors");
1398
1399 if (answer == KMessageBox::Cancel)
1400 return;
1401 if (answer == KMessageBox::Yes)
1402 switchColors = true;
1403 }
1404
1405 printImage(true, switchColors);
1406 }
1407
slotPrintingWizard()1408 void KStars::slotPrintingWizard()
1409 {
1410 if (m_PrintingWizard)
1411 {
1412 delete m_PrintingWizard;
1413 }
1414
1415 m_PrintingWizard = new PrintingWizard(this);
1416 m_PrintingWizard->show();
1417 }
1418
slotToggleTimer()1419 void KStars::slotToggleTimer()
1420 {
1421 if (data()->clock()->isActive())
1422 {
1423 data()->clock()->stop();
1424 updateTime();
1425 }
1426 else
1427 {
1428 if (fabs(data()->clock()->scale()) > Options::slewTimeScale())
1429 data()->clock()->setManualMode(true);
1430 data()->clock()->start();
1431 if (data()->clock()->isManualMode())
1432 map()->forceUpdate();
1433 }
1434
1435 // Update clock state in options
1436 Options::setRunClock(data()->clock()->isActive());
1437 }
1438
slotStepForward()1439 void KStars::slotStepForward()
1440 {
1441 if (data()->clock()->isActive())
1442 data()->clock()->stop();
1443 data()->clock()->manualTick(true);
1444 map()->forceUpdate();
1445 }
1446
slotStepBackward()1447 void KStars::slotStepBackward()
1448 {
1449 if (data()->clock()->isActive())
1450 data()->clock()->stop();
1451 data()->clock()->manualTick(true, true);
1452 map()->forceUpdate();
1453 }
1454
1455 //Pointing
slotPointFocus()1456 void KStars::slotPointFocus()
1457 {
1458 // In the following cases, we set slewing=true in order to disengage tracking
1459 map()->stopTracking();
1460
1461 if (sender() == actionCollection()->action("zenith"))
1462 map()->setDestinationAltAz(dms(90.0), map()->focus()->az(),
1463 Options::useRefraction());
1464 else if (sender() == actionCollection()->action("north"))
1465 map()->setDestinationAltAz(dms(15.0), dms(0.0001), Options::useRefraction());
1466 else if (sender() == actionCollection()->action("east"))
1467 map()->setDestinationAltAz(dms(15.0), dms(90.0), Options::useRefraction());
1468 else if (sender() == actionCollection()->action("south"))
1469 map()->setDestinationAltAz(dms(15.0), dms(180.0), Options::useRefraction());
1470 else if (sender() == actionCollection()->action("west"))
1471 map()->setDestinationAltAz(dms(15.0), dms(270.0), Options::useRefraction());
1472 }
1473
slotTrack()1474 void KStars::slotTrack()
1475 {
1476 if (Options::isTracking())
1477 {
1478 Options::setIsTracking(false);
1479 actionCollection()->action("track_object")->setText(i18n("Engage &Tracking"));
1480 actionCollection()
1481 ->action("track_object")
1482 ->setIcon(QIcon::fromTheme("document-decrypt"));
1483
1484 KSPlanetBase *planet = dynamic_cast<KSPlanetBase *>(map()->focusObject());
1485 if (planet && data()->temporaryTrail)
1486 {
1487 planet->clearTrail();
1488 data()->temporaryTrail = false;
1489 }
1490
1491 map()->setClickedObject(nullptr);
1492 map()->setFocusObject(nullptr); //no longer tracking focusObject
1493 map()->setFocusPoint(nullptr);
1494 }
1495 else
1496 {
1497 map()->setClickedPoint(map()->focus());
1498 map()->setClickedObject(nullptr);
1499 map()->setFocusObject(nullptr); //no longer tracking focusObject
1500 map()->setFocusPoint(map()->clickedPoint());
1501 Options::setIsTracking(true);
1502 actionCollection()->action("track_object")->setText(i18n("Stop &Tracking"));
1503 actionCollection()
1504 ->action("track_object")
1505 ->setIcon(QIcon::fromTheme("document-encrypt"));
1506 }
1507
1508 map()->forceUpdate();
1509 }
1510
slotManualFocus()1511 void KStars::slotManualFocus()
1512 {
1513 QPointer<FocusDialog> focusDialog = new FocusDialog();
1514
1515 // JM 2019-09-04: Should default to RA/DE always
1516 // if (Options::useAltAz())
1517 // focusDialog->activateAzAltPage();
1518
1519 if (focusDialog->exec() == QDialog::Accepted)
1520 {
1521 //If the requested position is very near the pole, we need to point first
1522 //to an intermediate location just below the pole in order to get the longitudinal
1523 //position (RA/Az) right.
1524
1525 // Do not access (RA0, Dec0) of focusDialog->point() as it can be of unknown epoch.
1526 // (RA, Dec) should be synced to JNow
1527 // -- asimha (2020-07-06)
1528 double realAlt(focusDialog->point()->alt().Degrees());
1529 double realDec(focusDialog->point()->dec().Degrees());
1530 if (Options::useAltAz() && realAlt > 89.0)
1531 {
1532 focusDialog->point()->setAlt(89.0);
1533 focusDialog->point()->HorizontalToEquatorial(data()->lst(),
1534 data()->geo()->lat());
1535 }
1536 if (!Options::useAltAz() && realDec > 89.0)
1537 {
1538 focusDialog->point()->setDec(89.0);
1539 focusDialog->point()->EquatorialToHorizontal(data()->lst(),
1540 data()->geo()->lat());
1541 }
1542
1543 map()->setClickedPoint(focusDialog->point());
1544
1545 if (Options::isTracking())
1546 slotTrack();
1547
1548 map()->slotCenter();
1549
1550 //The slew takes some time to complete, and this often causes the final focus point to be slightly
1551 //offset from the user's requested coordinates (because EquatorialToHorizontal() is called
1552 //throughout the process, which depends on the sidereal time). So we now "polish" the final
1553 //position by resetting the final focus to the focusDialog point.
1554 //
1555 //Also, if the requested position was within 1 degree of the coordinate pole, this will
1556 //automatically correct the final pointing from the intermediate offset position to the final position
1557 data()->setSnapNextFocus();
1558 if (Options::useAltAz())
1559 {
1560 // N.B. We have applied unrefract() in focusDialog
1561 map()->setDestinationAltAz(focusDialog->point()->alt(),
1562 focusDialog->point()->az(), false);
1563 }
1564 else
1565 {
1566 map()->setDestination(focusDialog->point()->ra(),
1567 focusDialog->point()->dec());
1568 }
1569
1570 //Now, if the requested point was near a pole, we need to reset the Alt/Dec of the focus.
1571 if (Options::useAltAz() && realAlt > 89.0)
1572 map()->focus()->setAlt(realAlt);
1573 if (!Options::useAltAz() && realDec > 89.0)
1574 map()->focus()->setDec(realAlt);
1575
1576 //Don't track if we set Alt/Az coordinates. This way, Alt/Az remain constant.
1577 if (focusDialog->usedAltAz())
1578 map()->stopTracking();
1579 }
1580 delete focusDialog;
1581 }
1582
slotZoomChanged()1583 void KStars::slotZoomChanged()
1584 {
1585 // Enable/disable actions
1586 actionCollection()->action("zoom_out")->setEnabled(Options::zoomFactor() > MINZOOM);
1587 actionCollection()->action("zoom_in")->setEnabled(Options::zoomFactor() < MAXZOOM);
1588 // Update status bar
1589 map()
1590 ->setupProjector(); // this needs to be run redundantly, so that the FOV returned below is up-to-date.
1591 float fov = map()->projector()->fov();
1592 KLocalizedString fovi18nstring =
1593 ki18nc("approximate field of view", "Approximate FOV: %1 degrees");
1594 if (fov < 1.0)
1595 {
1596 fov = fov * 60.0;
1597 fovi18nstring =
1598 ki18nc("approximate field of view", "Approximate FOV: %1 arcminutes");
1599 }
1600 if (fov < 1.0)
1601 {
1602 fov = fov * 60.0;
1603 fovi18nstring =
1604 ki18nc("approximate field of view", "Approximate FOV: %1 arcseconds");
1605 }
1606 QString fovstring = fovi18nstring.subs(QString::number(fov, 'f', 1)).toString();
1607
1608 statusBar()->showMessage(fovstring, 0);
1609 }
1610
slotSetZoom()1611 void KStars::slotSetZoom()
1612 {
1613 bool ok;
1614 double currentAngle = map()->width() / (Options::zoomFactor() * dms::DegToRad);
1615 double minAngle = map()->width() / (MAXZOOM * dms::DegToRad);
1616 double maxAngle = map()->width() / (MINZOOM * dms::DegToRad);
1617
1618 double angSize = QInputDialog::getDouble(
1619 nullptr,
1620 i18nc("The user should enter an angle for the field-of-view of the display",
1621 "Enter Desired Field-of-View Angle"),
1622 i18n("Enter a field-of-view angle in degrees: "), currentAngle, minAngle,
1623 maxAngle, 1, &ok);
1624
1625 if (ok)
1626 {
1627 map()->setZoomFactor(map()->width() / (angSize * dms::DegToRad));
1628 }
1629 }
1630
slotCoordSys()1631 void KStars::slotCoordSys()
1632 {
1633 if (Options::useAltAz())
1634 {
1635 Options::setUseAltAz(false);
1636 if (Options::useRefraction())
1637 {
1638 if (map()->focusObject()) //simply update focus to focusObject's position
1639 map()->setFocus(map()->focusObject());
1640 else //need to recompute focus for unrefracted position
1641 {
1642 // FIXME: Changed focus()->alt() to be unrefracted by convention; is this still necessary? -- asimha 2020/07/05
1643 map()->setFocusAltAz(map()->focus()->alt(), map()->focus()->az());
1644 map()->focus()->HorizontalToEquatorial(data()->lst(),
1645 data()->geo()->lat());
1646 }
1647 }
1648 actionCollection()
1649 ->action("coordsys")
1650 ->setText(i18n("Switch to Horizonal View (Horizontal &Coordinates)"));
1651 }
1652 else
1653 {
1654 Options::setUseAltAz(true);
1655 if (Options::useRefraction())
1656 {
1657 // FIXME: Changed focus()->alt() to be unrefracted by convention; is this still necessary? -- asimha 2020/07/05
1658 map()->setFocusAltAz(map()->focus()->alt(), map()->focus()->az());
1659 }
1660 actionCollection()
1661 ->action("coordsys")
1662 ->setText(i18n("Switch to Star Globe View (Equatorial &Coordinates)"));
1663 }
1664 map()->forceUpdate();
1665 }
1666
slotMapProjection()1667 void KStars::slotMapProjection()
1668 {
1669 if (sender() == actionCollection()->action("project_lambert"))
1670 Options::setProjection(Projector::Lambert);
1671 if (sender() == actionCollection()->action("project_azequidistant"))
1672 Options::setProjection(Projector::AzimuthalEquidistant);
1673 if (sender() == actionCollection()->action("project_orthographic"))
1674 Options::setProjection(Projector::Orthographic);
1675 if (sender() == actionCollection()->action("project_equirectangular"))
1676 Options::setProjection(Projector::Equirectangular);
1677 if (sender() == actionCollection()->action("project_stereographic"))
1678 Options::setProjection(Projector::Stereographic);
1679 if (sender() == actionCollection()->action("project_gnomonic"))
1680 Options::setProjection(Projector::Gnomonic);
1681
1682 //DEBUG
1683 qCDebug(KSTARS) << "Projection system: " << Options::projection();
1684
1685 m_SkyMap->forceUpdate();
1686 }
1687
1688 //Settings Menu:
slotColorScheme()1689 void KStars::slotColorScheme()
1690 {
1691 loadColorScheme(sender()->objectName());
1692 }
1693
slotTargetSymbol(bool flag)1694 void KStars::slotTargetSymbol(bool flag)
1695 {
1696 qDebug() << QString("slotTargetSymbol: %1 %2").arg(sender()->objectName()).arg(flag);
1697
1698 QStringList names = Options::fOVNames();
1699 if (flag)
1700 {
1701 // Add FOV to list
1702 names.append(sender()->objectName());
1703 }
1704 else
1705 {
1706 // Remove FOV from list
1707 int ix = names.indexOf(sender()->objectName());
1708 if (ix >= 0)
1709 names.removeAt(ix);
1710 }
1711 Options::setFOVNames(names);
1712
1713 // Sync visibleFOVs with fovNames
1714 data()->syncFOV();
1715
1716 map()->forceUpdate();
1717 }
1718
slotHIPSSource()1719 void KStars::slotHIPSSource()
1720 {
1721 QAction *selectedAction = qobject_cast<QAction *>(sender());
1722 Q_ASSERT(selectedAction != nullptr);
1723
1724 QString selectedSource = selectedAction->text().remove('&');
1725
1726 // selectedSource could be translated, while we need to send only Latin "None"
1727 // to Hips manager.
1728 if (selectedSource == i18n("None"))
1729 HIPSManager::Instance()->setCurrentSource("None");
1730 else
1731 HIPSManager::Instance()->setCurrentSource(selectedSource);
1732
1733 map()->forceUpdate();
1734 }
1735
slotFOVEdit()1736 void KStars::slotFOVEdit()
1737 {
1738 QPointer<FOVDialog> fovdlg = new FOVDialog(this);
1739 if (fovdlg->exec() == QDialog::Accepted)
1740 {
1741 FOVManager::save();
1742 repopulateFOV();
1743 }
1744 delete fovdlg;
1745 }
1746
slotObsList()1747 void KStars::slotObsList()
1748 {
1749 m_KStarsData->observingList()->show();
1750 }
1751
slotEquipmentWriter()1752 void KStars::slotEquipmentWriter()
1753 {
1754 QPointer<EquipmentWriter> equipmentdlg = new EquipmentWriter();
1755 equipmentdlg->loadEquipment();
1756 equipmentdlg->exec();
1757 delete equipmentdlg;
1758 }
1759
slotObserverManager()1760 void KStars::slotObserverManager()
1761 {
1762 QPointer<ObserverAdd> m_observerAdd = new ObserverAdd();
1763 m_observerAdd->exec();
1764 delete m_observerAdd;
1765 }
1766
slotHorizonManager()1767 void KStars::slotHorizonManager()
1768 {
1769 if (!m_HorizonManager)
1770 {
1771 m_HorizonManager = new HorizonManager(this);
1772 connect(m_SkyMap, SIGNAL(positionClicked(SkyPoint *)), m_HorizonManager,
1773 SLOT(addSkyPoint(SkyPoint *)));
1774 }
1775
1776 m_HorizonManager->show();
1777 }
1778
slotEyepieceView(SkyPoint * sp,const QString & imagePath)1779 void KStars::slotEyepieceView(SkyPoint *sp, const QString &imagePath)
1780 {
1781 if (!m_EyepieceView)
1782 m_EyepieceView = new EyepieceField(this);
1783
1784 // FIXME: Move FOV choice into the Eyepiece View tool itself.
1785 bool ok = true;
1786 const FOV *fov = nullptr;
1787 if (!data()->getAvailableFOVs().isEmpty())
1788 {
1789 // Ask the user to choose from a list of available FOVs.
1790 //int index;
1791 const FOV *f;
1792 QMap<QString, const FOV *> nameToFovMap;
1793 foreach (f, data()->getAvailableFOVs())
1794 {
1795 nameToFovMap.insert(f->name(), f);
1796 }
1797 nameToFovMap.insert(i18n("Attempt to determine from image"), nullptr);
1798 fov = nameToFovMap[QInputDialog::getItem(
1799 this, i18n("Eyepiece View: Choose a field-of-view"),
1800 i18n("FOV to render eyepiece view for:"), nameToFovMap.uniqueKeys(), 0, false,
1801 &ok)];
1802 }
1803 if (ok)
1804 m_EyepieceView->showEyepieceField(sp, fov, imagePath);
1805 }
1806
slotExecute()1807 void KStars::slotExecute()
1808 {
1809 KStarsData::Instance()->executeSession()->init();
1810 KStarsData::Instance()->executeSession()->show();
1811 }
1812
slotPolarisHourAngle()1813 void KStars::slotPolarisHourAngle()
1814 {
1815 QPointer<PolarisHourAngle> pHourAngle = new PolarisHourAngle(this);
1816 pHourAngle->exec();
1817 }
1818
1819 //Help Menu
slotTipOfDay()1820 void KStars::slotTipOfDay()
1821 {
1822 KTipDialog::showTip(this, "kstars/tips", true);
1823 }
1824
1825 // Toggle to and from full screen mode
slotFullScreen()1826 void KStars::slotFullScreen()
1827 {
1828 if (topLevelWidget()->isFullScreen())
1829 {
1830 topLevelWidget()->setWindowState(topLevelWidget()->windowState() &
1831 ~Qt::WindowFullScreen); // reset
1832 }
1833 else
1834 {
1835 topLevelWidget()->setWindowState(topLevelWidget()->windowState() |
1836 Qt::WindowFullScreen); // set
1837 }
1838 }
1839
1840 // Toggle to and from full screen mode
slotTerrain()1841 void KStars::slotTerrain()
1842 {
1843 Options::setShowTerrain(!Options::showTerrain());
1844 actionCollection()->action("toggle_terrain")
1845 ->setText(Options::showTerrain() ? i18n("Hide Terrain") : i18n("Show Terrain"));
1846 KStars::Instance()->map()->forceUpdate();
1847 }
1848
slotClearAllTrails()1849 void KStars::slotClearAllTrails()
1850 {
1851 //Exclude object with temporary trail
1852 SkyObject *exOb(nullptr);
1853 if (map()->focusObject() && map()->focusObject()->isSolarSystem() &&
1854 data()->temporaryTrail)
1855 {
1856 exOb = map()->focusObject();
1857 }
1858
1859 TrailObject::clearTrailsExcept(exOb);
1860
1861 map()->forceUpdate();
1862 }
1863
1864 //toggle display of GUI Items on/off
slotShowGUIItem(bool show)1865 void KStars::slotShowGUIItem(bool show)
1866 {
1867 //Toolbars
1868 if (sender() == actionCollection()->action("show_statusBar"))
1869 {
1870 Options::setShowStatusBar(show);
1871 statusBar()->setVisible(show);
1872 }
1873
1874 if (sender() == actionCollection()->action("show_sbAzAlt"))
1875 {
1876 Options::setShowAltAzField(show);
1877 if (!show)
1878 AltAzField.hide();
1879 else
1880 AltAzField.show();
1881 }
1882
1883 if (sender() == actionCollection()->action("show_sbRADec"))
1884 {
1885 Options::setShowRADecField(show);
1886 if (!show)
1887 RADecField.hide();
1888 else
1889 RADecField.show();
1890 }
1891
1892 if (sender() == actionCollection()->action("show_sbJ2000RADec"))
1893 {
1894 Options::setShowJ2000RADecField(show);
1895 if (!show)
1896 J2000RADecField.hide();
1897 else
1898 J2000RADecField.show();
1899 }
1900 }
addColorMenuItem(QString name,const QString & actionName)1901 void KStars::addColorMenuItem(QString name, const QString &actionName)
1902 {
1903 KToggleAction *kta = actionCollection()->add<KToggleAction>(actionName);
1904 const QString filename = QString(actionName).mid(3) + ".colors";
1905 kta->setText(name);
1906 kta->setObjectName(filename);
1907 kta->setActionGroup(cschemeGroup);
1908
1909 colorActionMenu->addAction(kta);
1910
1911 KConfigGroup cg = KSharedConfig::openConfig()->group("Colors");
1912 if (actionName.mid(3) ==
1913 cg.readEntry("ColorSchemeFile", "moonless-night.colors").remove(".colors"))
1914 {
1915 kta->setChecked(true);
1916 }
1917
1918 //use mid(3) to exclude the leading "cs_" prefix from the action name
1919 data()->add_color_scheme(filename, name.replace("&", ""));
1920 connect(kta, SIGNAL(toggled(bool)), this, SLOT(slotColorScheme()));
1921 }
1922
removeColorMenuItem(const QString & actionName)1923 void KStars::removeColorMenuItem(const QString &actionName)
1924 {
1925 qCDebug(KSTARS) << "removing " << actionName;
1926 colorActionMenu->removeAction(actionCollection()->action(actionName));
1927 }
1928
slotAboutToQuit()1929 void KStars::slotAboutToQuit()
1930 {
1931 // Delete skymap. This required to run destructors and save
1932 // current state in the option.
1933 delete m_SkyMap;
1934
1935 //Store Window geometry in Options object
1936 Options::setWindowWidth(width());
1937 Options::setWindowHeight(height());
1938
1939 //explicitly save the colorscheme data to the config file
1940 data()->colorScheme()->saveToConfig();
1941
1942 //synch the config file with the Config object
1943 writeConfig();
1944
1945 //Terminate Child Processes if on OS X
1946 #ifdef Q_OS_OSX
1947 QProcess *quit = new QProcess(this);
1948 quit->start("killall kdeinit5");
1949 quit->waitForFinished(1000);
1950 quit->start("killall klauncher");
1951 quit->waitForFinished(1000);
1952 quit->start("killall kioslave");
1953 quit->waitForFinished(1000);
1954 quit->start("killall kio_http_cache_cleaner");
1955 quit->waitForFinished(1000);
1956 delete quit;
1957 #endif
1958 }
1959
slotShowPositionBar(SkyPoint * p)1960 void KStars::slotShowPositionBar(SkyPoint *p)
1961 {
1962 if (Options::showAltAzField())
1963 {
1964 dms a = p->alt();
1965 if (Options::useAltAz())
1966 a = p->altRefracted();
1967 QString s =
1968 QString("%1, %2").arg(p->az().toDMSString(true), //true: force +/- symbol
1969 a.toDMSString(true)); //true: force +/- symbol
1970 //statusBar()->changeItem( s, 1 );
1971 AltAzField.setText(s);
1972 }
1973 if (Options::showRADecField())
1974 {
1975 KStarsDateTime lastUpdate;
1976 lastUpdate.setDJD(KStarsData::Instance()->updateNum()->getJD());
1977 QString sEpoch = QString::number(lastUpdate.epoch(), 'f', 1);
1978 QString s = QString("%1, %2 (J%3)")
1979 .arg(p->ra().toHMSString(), p->dec().toDMSString(true),
1980 sEpoch); //true: force +/- symbol
1981 //statusBar()->changeItem( s, 2 );
1982 RADecField.setText(s);
1983 }
1984
1985 if (Options::showJ2000RADecField())
1986 {
1987 SkyPoint p0;
1988 //p0 = p->deprecess(KStarsData::Instance()->updateNum()); // deprecess to update RA0/Dec0 from RA/Dec
1989 p0 = p->catalogueCoord(KStarsData::Instance()->updateNum()->julianDay());
1990 QString s = QString("%1, %2 (J2000)")
1991 .arg(p0.ra().toHMSString(),
1992 p0.dec().toDMSString(true)); //true: force +/- symbol
1993 //statusBar()->changeItem( s, 2 );
1994 J2000RADecField.setText(s);
1995 }
1996 }
1997
slotUpdateComets(bool isAutoUpdate)1998 void KStars::slotUpdateComets(bool isAutoUpdate)
1999 {
2000 data()->skyComposite()->solarSystemComposite()->cometsComponent()->updateDataFile(
2001 isAutoUpdate);
2002 }
2003
slotUpdateAsteroids(bool isAutoUpdate)2004 void KStars::slotUpdateAsteroids(bool isAutoUpdate)
2005 {
2006 data()->skyComposite()->solarSystemComposite()->asteroidsComponent()->updateDataFile(
2007 isAutoUpdate);
2008 }
2009
slotUpdateSupernovae()2010 void KStars::slotUpdateSupernovae()
2011 {
2012 data()->skyComposite()->supernovaeComponent()->slotTriggerDataFileUpdate();
2013 }
2014
slotUpdateSatellites()2015 void KStars::slotUpdateSatellites()
2016 {
2017 data()->skyComposite()->satellites()->updateTLEs();
2018 }
2019
slotConfigureNotifications()2020 void KStars::slotConfigureNotifications()
2021 {
2022 #ifdef HAVE_NOTIFYCONFIG
2023 KNotifyConfigWidget::configure(this);
2024 #endif
2025 }
slotDSOCatalogGUI()2026 void KStars::slotDSOCatalogGUI()
2027 {
2028 auto *ui = new CatalogsDBUI{ this, CatalogsDB::dso_db_path() };
2029 ui->show();
2030 connect(ui, &QDialog::finished, this, [&](const auto) {
2031 KStars::Instance()->data()->skyComposite()->catalogsComponent()->dropCache();
2032 });
2033 }
2034