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