File indexing completed on 2024-04-21 14:47:07

0001 /*
0002     SPDX-FileCopyrightText: 2002 Jason Harris <jharris@30doradus.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 // This file contains function definitions for Actions declared in kstars.h
0008 
0009 #include "kstars.h"
0010 
0011 #include "imageexporter.h"
0012 #include "kstarsdata.h"
0013 #include "kstars_debug.h"
0014 #include "ksnotification.h"
0015 #include "kswizard.h"
0016 #include "Options.h"
0017 #include "skymap.h"
0018 #include "texturemanager.h"
0019 #include "dialogs/exportimagedialog.h"
0020 #include "dialogs/finddialog.h"
0021 #include "dialogs/focusdialog.h"
0022 #include "dialogs/fovdialog.h"
0023 #include "dialogs/locationdialog.h"
0024 #include "dialogs/timedialog.h"
0025 #include "dialogs/catalogsdbui.h"
0026 #include "oal/execute.h"
0027 #include "oal/equipmentwriter.h"
0028 #include "oal/observeradd.h"
0029 #include "options/opsadvanced.h"
0030 #include "options/opscatalog.h"
0031 #include "options/opscolors.h"
0032 #include "options/opsguides.h"
0033 #include "options/opsterrain.h"
0034 #include "options/opsimageoverlay.h"
0035 #include "options/opsdeveloper.h"
0036 #include "options/opssatellites.h"
0037 #include "options/opssolarsystem.h"
0038 #include "options/opssupernovae.h"
0039 #include "printing/printingwizard.h"
0040 #include "projections/projector.h"
0041 #include "skycomponents/asteroidscomponent.h"
0042 #include "skycomponents/cometscomponent.h"
0043 #include "skycomponents/satellitescomponent.h"
0044 #include "skycomponents/skymapcomposite.h"
0045 #include "skycomponents/solarsystemcomposite.h"
0046 #include "skycomponents/supernovaecomponent.h"
0047 #include "skycomponents/catalogscomponent.h"
0048 #include "skycomponents/mosaiccomponent.h"
0049 #include "skycomponents/imageoverlaycomponent.h"
0050 #ifdef HAVE_INDI
0051 #include "skyobjects/mosaictiles.h"
0052 #include "indi/indidome.h"
0053 #endif
0054 #include "tools/altvstime.h"
0055 #include "tools/astrocalc.h"
0056 #include "tools/eyepiecefield.h"
0057 #include "tools/flagmanager.h"
0058 #include "tools/horizonmanager.h"
0059 #include "tools/observinglist.h"
0060 #include "tools/planetviewer.h"
0061 #include "tools/jmoontool.h"
0062 #include "tools/scriptbuilder.h"
0063 #include "tools/skycalendar.h"
0064 #include "tools/wutdialog.h"
0065 #include "tools/polarishourangle.h"
0066 #include "tools/whatsinteresting/wiequipsettings.h"
0067 #include "tools/whatsinteresting/wilpsettings.h"
0068 #include "tools/whatsinteresting/wiview.h"
0069 #include "hips/hipsmanager.h"
0070 #include "catalogsdb.h"
0071 #ifdef HAVE_INDI
0072 #include <basedevice.h>
0073 //#include "indi/telescopewizardprocess.h"
0074 #include "indi/opsindi.h"
0075 #include "indi/drivermanager.h"
0076 #include "indi/guimanager.h"
0077 #include "indi/indilistener.h"
0078 #endif
0079 
0080 #ifdef HAVE_CFITSIO
0081 #include "fitsviewer/fitsviewer.h"
0082 #include "fitsviewer/opsfits.h"
0083 #ifdef HAVE_INDI
0084 #include "ekos/manager.h"
0085 #include "ekos/scheduler/framingassistantui.h"
0086 #include "ekos/scheduler/scheduler.h"
0087 #include "ekos/opsekos.h"
0088 #include "ekos/mount/mount.h"
0089 #endif
0090 #endif
0091 
0092 #include "xplanet/opsxplanet.h"
0093 
0094 #ifdef HAVE_NOTIFYCONFIG
0095 #include <KNotifyConfigWidget>
0096 #endif
0097 #include <KActionCollection>
0098 #include <KActionMenu>
0099 #include <KTipDialog>
0100 #include <KToggleAction>
0101 #include <kns3/downloaddialog.h>
0102 
0103 #include <QQuickWindow>
0104 #include <QQuickView>
0105 
0106 #ifdef _WIN32
0107 #include <windows.h>
0108 #undef interface
0109 #endif
0110 #include <sys/stat.h>
0111 
0112 /** ViewToolBar Action.  All of the viewToolBar buttons are connected to this slot. **/
0113 
0114 void KStars::slotViewToolBar()
0115 {
0116     KToggleAction *a   = (KToggleAction *)sender();
0117     KConfigDialog *kcd = KConfigDialog::exists("settings");
0118 
0119     if (a == actionCollection()->action("show_stars"))
0120     {
0121         Options::setShowStars(a->isChecked());
0122         if (kcd)
0123         {
0124             opcatalog->kcfg_ShowStars->setChecked(a->isChecked());
0125         }
0126     }
0127     else if (a == actionCollection()->action("show_deepsky"))
0128     {
0129         Options::setShowDeepSky(a->isChecked());
0130         if (kcd)
0131         {
0132             opcatalog->kcfg_ShowDeepSky->setChecked(a->isChecked());
0133         }
0134     }
0135     else if (a == actionCollection()->action("show_planets"))
0136     {
0137         Options::setShowSolarSystem(a->isChecked());
0138         if (kcd)
0139         {
0140             opsolsys->kcfg_ShowSolarSystem->setChecked(a->isChecked());
0141         }
0142     }
0143     else if (a == actionCollection()->action("show_clines"))
0144     {
0145         Options::setShowCLines(a->isChecked());
0146         if (kcd)
0147         {
0148             opguides->kcfg_ShowCLines->setChecked(a->isChecked());
0149         }
0150     }
0151     else if (a == actionCollection()->action("show_cnames"))
0152     {
0153         Options::setShowCNames(a->isChecked());
0154         if (kcd)
0155         {
0156             opguides->kcfg_ShowCNames->setChecked(a->isChecked());
0157         }
0158     }
0159     else if (a == actionCollection()->action("show_cbounds"))
0160     {
0161         Options::setShowCBounds(a->isChecked());
0162         if (kcd)
0163         {
0164             opguides->kcfg_ShowCBounds->setChecked(a->isChecked());
0165         }
0166     }
0167     else if (a == actionCollection()->action("show_constellationart"))
0168     {
0169         Options::setShowConstellationArt(a->isChecked());
0170         if (kcd)
0171         {
0172             opguides->kcfg_ShowConstellationArt->setChecked(a->isChecked());
0173         }
0174     }
0175     else if (a == actionCollection()->action("show_mw"))
0176     {
0177         Options::setShowMilkyWay(a->isChecked());
0178         if (kcd)
0179         {
0180             opguides->kcfg_ShowMilkyWay->setChecked(a->isChecked());
0181         }
0182     }
0183     else if (a == actionCollection()->action("show_equatorial_grid"))
0184     {
0185         // if autoSelectGrid is selected and the user clicked the
0186         // show_equatorial_grid button, he probably wants us to disable
0187         // the autoSelectGrid and display the equatorial grid.
0188         Options::setAutoSelectGrid(false);
0189         Options::setShowEquatorialGrid(a->isChecked());
0190         if (kcd)
0191         {
0192             opguides->kcfg_ShowEquatorialGrid->setChecked(a->isChecked());
0193             opguides->kcfg_AutoSelectGrid->setChecked(false);
0194         }
0195     }
0196     else if (a == actionCollection()->action("show_horizontal_grid"))
0197     {
0198         Options::setAutoSelectGrid(false);
0199         Options::setShowHorizontalGrid(a->isChecked());
0200         if (kcd)
0201         {
0202             opguides->kcfg_ShowHorizontalGrid->setChecked(a->isChecked());
0203             opguides->kcfg_AutoSelectGrid->setChecked(false);
0204         }
0205     }
0206     else if (a == actionCollection()->action("show_horizon"))
0207     {
0208         Options::setShowGround(a->isChecked());
0209         if (!a->isChecked() && Options::useRefraction())
0210         {
0211             QString caption = i18n("Refraction effects disabled");
0212             QString message = i18n("When the horizon is switched off, refraction effects "
0213                                    "are temporarily disabled.");
0214 
0215             KMessageBox::information(this, message, caption, "dag_refract_hide_ground");
0216         }
0217         if (kcd)
0218         {
0219             opguides->kcfg_ShowGround->setChecked(a->isChecked());
0220         }
0221     }
0222     else if (a == actionCollection()->action("show_flags"))
0223     {
0224         Options::setShowFlags(a->isChecked());
0225         if (kcd)
0226         {
0227             opguides->kcfg_ShowFlags->setChecked(a->isChecked());
0228         }
0229     }
0230     else if (a == actionCollection()->action("show_satellites"))
0231     {
0232         Options::setShowSatellites(a->isChecked());
0233         if (kcd)
0234         {
0235             opssatellites->kcfg_ShowSatellites->setChecked(a->isChecked());
0236         }
0237     }
0238     else if (a == actionCollection()->action("show_supernovae"))
0239     {
0240         Options::setShowSupernovae(a->isChecked());
0241         if (kcd)
0242         {
0243             opssupernovae->kcfg_ShowSupernovae->setChecked(a->isChecked());
0244         }
0245     }
0246 
0247     // update time for all objects because they might be not initialized
0248     // it's needed when using horizontal coordinates
0249     data()->setFullTimeUpdate();
0250     updateTime();
0251 
0252     map()->forceUpdate();
0253 }
0254 
0255 void KStars::slotINDIToolBar()
0256 {
0257 #ifdef HAVE_INDI
0258     KToggleAction *a = qobject_cast<KToggleAction *>(sender());
0259 
0260     if (a == actionCollection()->action("show_control_panel"))
0261     {
0262         if (a->isChecked())
0263         {
0264             GUIManager::Instance()->raise();
0265             GUIManager::Instance()->activateWindow();
0266             GUIManager::Instance()->showNormal();
0267         }
0268         else
0269             GUIManager::Instance()->hide();
0270     }
0271     else if (a == actionCollection()->action("show_ekos"))
0272     {
0273         if (a->isChecked())
0274         {
0275             Ekos::Manager::Instance()->raise();
0276             Ekos::Manager::Instance()->activateWindow();
0277             Ekos::Manager::Instance()->showNormal();
0278         }
0279         else
0280             Ekos::Manager::Instance()->hide();
0281     }
0282     else if (a == actionCollection()->action("lock_telescope"))
0283     {
0284         for (auto &oneDevice : INDIListener::devices())
0285         {
0286             if (!(oneDevice->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE))
0287                 continue;
0288 
0289             if (oneDevice->isConnected() == false)
0290             {
0291                 KSNotification::error(i18n("Mount %1 is offline. Please connect and retry again.", oneDevice->getDeviceName()));
0292                 return;
0293             }
0294 
0295             auto mount = oneDevice->getMount();
0296             if (!mount)
0297                 continue;
0298 
0299             if (a->isChecked())
0300                 mount->centerLock();
0301             else
0302                 mount->centerUnlock();
0303             return;
0304         }
0305 
0306         KSNotification::sorry(i18n("No connected mounts found."));
0307         return;
0308     }
0309     else if (a == actionCollection()->action("show_fits_viewer"))
0310     {
0311         if (m_FITSViewers.isEmpty())
0312         {
0313             a->setEnabled(false);
0314             return;
0315         }
0316 
0317         if (a->isChecked())
0318         {
0319             for (auto &view : m_FITSViewers)
0320             {
0321                 if (view->tabs().empty() == false)
0322                 {
0323                     view->raise();
0324                     view->activateWindow();
0325                     view->showNormal();
0326                 }
0327             }
0328         }
0329         else
0330         {
0331             for (auto &view : m_FITSViewers)
0332             {
0333                 view->hide();
0334             }
0335         }
0336     }
0337     else if (a == actionCollection()->action("show_mount_box"))
0338     {
0339 #ifdef HAVE_CFITSIO
0340 #ifdef HAVE_INDI
0341         Ekos::Manager::Instance()->mountModule()->toggleMountToolBox();
0342 #endif
0343 #endif
0344     }
0345     else if (a == actionCollection()->action("show_sensor_fov"))
0346     {
0347         Options::setShowSensorFOV(a->isChecked());
0348         for (auto &oneFOV : data()->getTransientFOVs())
0349         {
0350             if (oneFOV->objectName() == "sensor_fov")
0351                 oneFOV->setProperty("visible", a->isChecked());
0352         }
0353     }
0354     else if (a == actionCollection()->action("show_mosaic_panel"))
0355     {
0356 #ifdef HAVE_INDI
0357         Options::setShowMosaicPanel(a->isChecked());
0358         // TODO
0359         // If scheduler is not running, then we should also show the Mosaic Planner dialog.
0360         auto scheduler = Ekos::Manager::Instance()->schedulerModule();
0361         if (a->isChecked() && scheduler && scheduler->status() != Ekos::SCHEDULER_RUNNING)
0362         {
0363             // Only create if we don't have an instance already
0364             if (findChild<Ekos::FramingAssistantUI *>("FramingAssistant") == nullptr)
0365             {
0366                 Ekos::FramingAssistantUI *assistant = new Ekos::FramingAssistantUI();
0367                 assistant->setAttribute(Qt::WA_DeleteOnClose, true);
0368                 assistant->show();
0369             }
0370         }
0371 #endif
0372     }
0373 
0374 #endif
0375 }
0376 
0377 void KStars::slotSetTelescopeEnabled(bool enable)
0378 {
0379     telescopeGroup->setEnabled(enable);
0380     if (enable == false)
0381     {
0382         for (auto &a : telescopeGroup->actions())
0383         {
0384             a->setChecked(false);
0385         }
0386     }
0387 }
0388 
0389 void KStars::slotSetDomeEnabled(bool enable)
0390 {
0391     domeGroup->setEnabled(enable);
0392     if (enable == false)
0393     {
0394         for (auto &a : domeGroup->actions())
0395         {
0396             a->setChecked(false);
0397         }
0398     }
0399 }
0400 
0401 /** Major Dialog Window Actions **/
0402 
0403 void KStars::slotCalculator()
0404 {
0405     if (!m_AstroCalc)
0406         m_AstroCalc = new AstroCalc(this);
0407     m_AstroCalc->show();
0408 }
0409 
0410 void KStars::slotWizard()
0411 {
0412     QPointer<KSWizard> wizard = new KSWizard(this);
0413     if (wizard->exec() == QDialog::Accepted)
0414     {
0415         Options::setRunStartupWizard(false); //don't run on startup next time
0416         if (wizard->geo())
0417             updateLocationFromWizard(*(wizard->geo()));
0418     }
0419 }
0420 
0421 void KStars::updateLocationFromWizard(const GeoLocation &geo)
0422 {
0423     data()->setLocation(geo);
0424     // adjust local time to keep UT the same.
0425     // create new LT without DST offset
0426     KStarsDateTime ltime = data()->geo()->UTtoLT(data()->ut());
0427 
0428     // reset timezonerule to compute next dst change
0429     data()->geo()->tzrule()->reset_with_ltime(ltime, data()->geo()->TZ0(),
0430             data()->isTimeRunningForward());
0431 
0432     // reset next dst change time
0433     data()->setNextDSTChange(data()->geo()->tzrule()->nextDSTChange());
0434 
0435     // reset local sideral time
0436     data()->syncLST();
0437 
0438     // Make sure Numbers, Moon, planets, and sky objects are updated immediately
0439     data()->setFullTimeUpdate();
0440 
0441     // If the sky is in Horizontal mode and not tracking, reset focus such that
0442     // Alt/Az remain constant.
0443     if (!Options::isTracking() && Options::useAltAz())
0444     {
0445         map()->focus()->HorizontalToEquatorial(data()->lst(), data()->geo()->lat());
0446     }
0447 
0448     // recalculate new times and objects
0449     data()->setSnapNextFocus();
0450     updateTime();
0451 }
0452 
0453 void KStars::slotDownload()
0454 {
0455     KSNotification::event(
0456         QLatin1String("KnownIssue"),
0457         i18n("Due to a known issue in the kde frameworks, "
0458              "updating already downloaded items is currently not possible. <br> "
0459              "Please uninstall and reinstall them to update."));
0460 
0461     // 2017-07-04: Explicitly load kstars.knsrc from resources file
0462     auto dlg = std::make_unique<KNS3::DownloadDialog>(":/kconfig/kstars.knsrc", this);
0463 
0464     if (!dlg)
0465         return;
0466 
0467     dlg->exec();
0468 
0469     // Get the list of all the installed entries.
0470     const auto changed_entries = dlg->changedEntries();
0471 
0472     CatalogsDB::DBManager manager{ CatalogsDB::dso_db_path() };
0473     for (const KNS3::Entry &entry : changed_entries)
0474     {
0475         if (entry.category() != "dso")
0476             continue;
0477         const auto id = entry.id().toInt();
0478 
0479         if (entry.status() == KNS3::Entry::Installed)
0480             for (const QString &name : entry.installedFiles())
0481             {
0482                 if (name.endsWith(CatalogsDB::db_file_extension))
0483                 {
0484                     const auto meta{ CatalogsDB::read_catalog_meta_from_file(name) };
0485 
0486                     if (!meta.first)
0487                     {
0488                         QMessageBox::critical(
0489                             this, i18n("Error"),
0490                             i18n("The catalog \"%1\" is corrupt.", entry.name()));
0491                         continue;
0492                     }
0493 
0494                     if (meta.second.id != id)
0495                     {
0496                         QMessageBox::critical(
0497                             this, i18n("Error"),
0498                             i18n("The catalog \"%1\" is corrupt.<br>Expected id=%2 but "
0499                                  "got id=%3",
0500                                  entry.name(), id, meta.second.id));
0501                         continue;
0502                     }
0503 
0504                     const auto success{ manager.import_catalog(name, true) };
0505                     if (!success.first)
0506                         QMessageBox::critical(
0507                             this, i18n("Error"),
0508                             i18n("Could not import the catalog \"%1\"<br>%2",
0509                                  entry.name(), success.second));
0510                 }
0511             }
0512 
0513         if (entry.status() == KNS3::Entry::Deleted)
0514         {
0515             manager.remove_catalog(id);
0516         }
0517     }
0518 
0519     TextureManager::discoverTextureDirs();
0520     KStars::Instance()->data()->skyComposite()->reloadDeepSky();
0521     KStars::Instance()->data()->setFullTimeUpdate();
0522     KStars::Instance()->updateTime();
0523     KStars::Instance()->map()->forceUpdate();
0524 }
0525 
0526 void KStars::slotAVT()
0527 {
0528     if (!m_AltVsTime)
0529         m_AltVsTime = new AltVsTime(this);
0530     m_AltVsTime->show();
0531 }
0532 
0533 void KStars::slotWUT()
0534 {
0535     if (!m_WUTDialog)
0536         m_WUTDialog = new WUTDialog(this);
0537     m_WUTDialog->show();
0538 }
0539 
0540 //FIXME Port to QML2
0541 //#if 0
0542 void KStars::slotWISettings()
0543 {
0544     if (!m_WIView)
0545         slotToggleWIView();
0546     if (m_WIView && !m_wiDock->isVisible())
0547         slotToggleWIView();
0548 
0549     if (KConfigDialog::showDialog("wisettings"))
0550     {
0551         m_WIEquipmentSettings->populateScopeListWidget();
0552         return;
0553     }
0554 
0555     KConfigDialog *dialog = new KConfigDialog(this, "wisettings", Options::self());
0556 
0557     connect(dialog, SIGNAL(settingsChanged(QString)), this,
0558             SLOT(slotApplyWIConfigChanges()));
0559 
0560     m_WISettings          = new WILPSettings(this);
0561     m_WIEquipmentSettings = new WIEquipSettings();
0562     dialog->addPage(m_WISettings, i18n("Light Pollution Settings"));
0563     dialog->addPage(m_WIEquipmentSettings,
0564                     i18n("Equipment Settings - Equipment Type and Parameters"));
0565     dialog->exec();
0566     if (m_WIEquipmentSettings)
0567         m_WIEquipmentSettings->setAperture(); //Something isn't working with this!
0568 }
0569 
0570 void KStars::slotToggleWIView()
0571 {
0572     if (KStars::Closing)
0573         return;
0574 
0575     if (!m_WIView)
0576     {
0577         m_WIView = new WIView(nullptr);
0578         m_wiDock = new QDockWidget(this);
0579         m_wiDock->setStyleSheet("QDockWidget::title{background-color:black;}");
0580         m_wiDock->setObjectName("What's Interesting");
0581         m_wiDock->setAllowedAreas(Qt::RightDockWidgetArea);
0582         QWidget *container = QWidget::createWindowContainer(m_WIView->getWIBaseView());
0583         m_wiDock->setWidget(container);
0584         m_wiDock->setMinimumWidth(400);
0585         addDockWidget(Qt::RightDockWidgetArea, m_wiDock);
0586         connect(m_wiDock, SIGNAL(visibilityChanged(bool)),
0587                 actionCollection()->action("show_whatsinteresting"),
0588                 SLOT(setChecked(bool)));
0589         m_wiDock->setVisible(true);
0590     }
0591     else
0592     {
0593         m_wiDock->setVisible(!m_wiDock->isVisible());
0594     }
0595 }
0596 
0597 void KStars::slotCalendar()
0598 {
0599     if (!m_SkyCalendar)
0600         m_SkyCalendar = new SkyCalendar(this);
0601     m_SkyCalendar->show();
0602 }
0603 
0604 void KStars::slotGlossary()
0605 {
0606     //  GlossaryDialog *dlg = new GlossaryDialog( this, true );
0607     //  QString glossaryfile =data()->stdDirs->findResource( "data", "kstars/glossary.xml" );
0608     //  QUrl u = glossaryfile;
0609     //  Glossary *g = new Glossary( u );
0610     //  g->setName( i18n( "Knowledge" ) );
0611     //  dlg->addGlossary( g );
0612     //  dlg->show();
0613 }
0614 
0615 void KStars::slotScriptBuilder()
0616 {
0617     if (!m_ScriptBuilder)
0618         m_ScriptBuilder = new ScriptBuilder(this);
0619     m_ScriptBuilder->show();
0620 }
0621 
0622 void KStars::slotSolarSystem()
0623 {
0624     if (!m_PlanetViewer)
0625         m_PlanetViewer = new PlanetViewer(this);
0626     m_PlanetViewer->show();
0627 }
0628 
0629 void KStars::slotJMoonTool()
0630 {
0631     if (!m_JMoonTool)
0632         m_JMoonTool = new JMoonTool(this);
0633     m_JMoonTool->show();
0634 }
0635 
0636 void KStars::slotMoonPhaseTool()
0637 {
0638     //FIXME Port to KF5
0639     //if( ! mpt ) mpt = new MoonPhaseTool( this );
0640     //mpt->show();
0641 }
0642 
0643 void KStars::slotFlagManager()
0644 {
0645     if (!m_FlagManager)
0646         m_FlagManager = new FlagManager(this);
0647     m_FlagManager->show();
0648 }
0649 
0650 #if 0
0651 void KStars::slotTelescopeWizard()
0652 {
0653 #ifdef HAVE_INDI
0654 #ifndef Q_OS_WIN
0655 
0656     QString indiServerDir = Options::indiServer();
0657 
0658 #ifdef Q_OS_OSX
0659     if (Options::indiServerIsInternal())
0660         indiServerDir = QCoreApplication::applicationDirPath();
0661     else
0662         indiServerDir = QFileInfo(Options::indiServer()).dir().path();
0663 #endif
0664 
0665     QStringList paths;
0666     paths << "/usr/bin"
0667           << "/usr/local/bin" << indiServerDir;
0668 
0669     if (QStandardPaths::findExecutable("indiserver").isEmpty())
0670     {
0671         if (QStandardPaths::findExecutable("indiserver", paths).isEmpty())
0672         {
0673             KSNotification::error(i18n("Unable to find INDI server. Please make sure the package that provides "
0674                                        "the 'indiserver' binary is installed."));
0675             return;
0676         }
0677     }
0678 #endif
0679 
0680     QPointer<telescopeWizardProcess> twiz = new telescopeWizardProcess(this);
0681     twiz->exec();
0682     delete twiz;
0683 #endif
0684 }
0685 #endif
0686 
0687 void KStars::slotINDIPanel()
0688 {
0689 #ifdef HAVE_INDI
0690 #ifndef Q_OS_WIN
0691 
0692     QString indiServerDir = Options::indiServer();
0693 
0694 #ifdef Q_OS_OSX
0695     if (Options::indiServerIsInternal())
0696         indiServerDir = QCoreApplication::applicationDirPath();
0697     else
0698         indiServerDir = QFileInfo(Options::indiServer()).dir().path();
0699 #endif
0700 
0701     QStringList paths;
0702     paths << "/usr/bin"
0703           << "/usr/local/bin" << indiServerDir;
0704 
0705     if (QStandardPaths::findExecutable("indiserver").isEmpty())
0706     {
0707         if (QStandardPaths::findExecutable("indiserver", paths).isEmpty())
0708         {
0709             KSNotification::error(i18n(
0710                                       "Unable to find INDI server. Please make sure the package that provides "
0711                                       "the 'indiserver' binary is installed."));
0712             return;
0713         }
0714     }
0715 #endif
0716     GUIManager::Instance()->updateStatus(true);
0717 #endif
0718 }
0719 
0720 void KStars::slotINDIDriver()
0721 {
0722 #ifdef HAVE_INDI
0723 #ifndef Q_OS_WIN
0724 
0725     if (KMessageBox::warningContinueCancel(
0726                 nullptr,
0727                 i18n("INDI Device Manager should only be used by advanced technical users. "
0728                      "It cannot be used with Ekos. Do you still want to open INDI device "
0729                      "manager?"),
0730                 i18n("INDI Device Manager"), KStandardGuiItem::cont(),
0731                 KStandardGuiItem::cancel(),
0732                 "indi_device_manager_warning") == KMessageBox::Cancel)
0733         return;
0734 
0735     QString indiServerDir = Options::indiServer();
0736 
0737 #ifdef Q_OS_OSX
0738     if (Options::indiServerIsInternal())
0739         indiServerDir = QCoreApplication::applicationDirPath();
0740     else
0741         indiServerDir = QFileInfo(Options::indiServer()).dir().path();
0742 #endif
0743 
0744     QStringList paths;
0745     paths << "/usr/bin"
0746           << "/usr/local/bin" << indiServerDir;
0747 
0748     if (QStandardPaths::findExecutable("indiserver").isEmpty())
0749     {
0750         if (QStandardPaths::findExecutable("indiserver", paths).isEmpty())
0751         {
0752             KSNotification::error(i18n(
0753                                       "Unable to find INDI server. Please make sure the package that provides "
0754                                       "the 'indiserver' binary is installed."));
0755             return;
0756         }
0757     }
0758 #endif
0759 
0760     DriverManager::Instance()->raise();
0761     DriverManager::Instance()->activateWindow();
0762     DriverManager::Instance()->showNormal();
0763 
0764 #endif
0765 }
0766 
0767 void KStars::slotEkos()
0768 {
0769 #ifdef HAVE_CFITSIO
0770 #ifdef HAVE_INDI
0771 
0772 #ifndef Q_OS_WIN
0773 
0774     QString indiServerDir = Options::indiServer();
0775 
0776 #ifdef Q_OS_OSX
0777     if (Options::indiServerIsInternal())
0778         indiServerDir = QCoreApplication::applicationDirPath();
0779     else
0780         indiServerDir = QFileInfo(Options::indiServer()).dir().path();
0781 #endif
0782 
0783     QStringList paths;
0784     paths << "/usr/bin"
0785           << "/usr/local/bin" << indiServerDir;
0786 
0787     if (QStandardPaths::findExecutable("indiserver").isEmpty())
0788     {
0789         if (QStandardPaths::findExecutable("indiserver", paths).isEmpty())
0790         {
0791             KSNotification::error(i18n(
0792                                       "Unable to find INDI server. Please make sure the package that provides "
0793                                       "the 'indiserver' binary is installed."));
0794             return;
0795         }
0796     }
0797 #endif
0798 
0799     if (Ekos::Manager::Instance()->isVisible() &&
0800             Ekos::Manager::Instance()->isActiveWindow())
0801     {
0802         Ekos::Manager::Instance()->hide();
0803     }
0804     else
0805     {
0806         Ekos::Manager::Instance()->raise();
0807         Ekos::Manager::Instance()->activateWindow();
0808         Ekos::Manager::Instance()->showNormal();
0809     }
0810 
0811 #endif
0812 #endif
0813 }
0814 
0815 void KStars::slotINDITelescopeTrack()
0816 {
0817 #ifdef HAVE_INDI
0818     if (m_KStarsData == nullptr || INDIListener::Instance() == nullptr)
0819         return;
0820 
0821     for (auto &oneDevice : INDIListener::devices())
0822     {
0823         if (!(oneDevice->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE))
0824             continue;
0825 
0826         auto mount = oneDevice->getMount();
0827         if (!mount || mount->isConnected() == false)
0828             continue;
0829 
0830         KToggleAction *a = qobject_cast<KToggleAction *>(sender());
0831 
0832         if (a != nullptr)
0833         {
0834             mount->setTrackEnabled(a->isChecked());
0835             return;
0836         }
0837     }
0838 #endif
0839 }
0840 
0841 void KStars::slotINDITelescopeSlew(bool focused_object)
0842 {
0843 #ifdef HAVE_INDI
0844     if (m_KStarsData == nullptr || INDIListener::Instance() == nullptr)
0845         return;
0846 
0847     for (auto &oneDevice : INDIListener::devices())
0848     {
0849         if (!(oneDevice->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE))
0850             continue;
0851 
0852         auto mount = oneDevice->getMount();
0853         if (!mount || mount->isConnected() == false)
0854             continue;
0855         if (focused_object)
0856         {
0857             if (m_SkyMap->focusObject() != nullptr)
0858                 mount->Slew(m_SkyMap->focusObject());
0859         }
0860         else
0861             mount->Slew(m_SkyMap->mousePoint());
0862 
0863         return;
0864     }
0865 #else
0866     Q_UNUSED(focused_object)
0867 #endif
0868 }
0869 
0870 void KStars::slotINDITelescopeSlewMousePointer()
0871 {
0872 #ifdef HAVE_INDI
0873     slotINDITelescopeSlew(false);
0874 #endif
0875 }
0876 
0877 void KStars::slotINDITelescopeSync(bool focused_object)
0878 {
0879 #ifdef HAVE_INDI
0880     if (m_KStarsData == nullptr || INDIListener::Instance() == nullptr)
0881         return;
0882 
0883     for (auto &oneDevice : INDIListener::devices())
0884     {
0885         if (!(oneDevice->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE))
0886             continue;
0887 
0888         auto mount = oneDevice->getMount();
0889         if (!mount || mount->isConnected() == false)
0890             continue;
0891 
0892         if (focused_object)
0893         {
0894             if (m_SkyMap->focusObject() != nullptr)
0895                 mount->Sync(m_SkyMap->focusObject());
0896         }
0897         else
0898             mount->Sync(m_SkyMap->mousePoint());
0899 
0900         return;
0901     }
0902 #else
0903     Q_UNUSED(focused_object)
0904 #endif
0905 }
0906 
0907 void KStars::slotINDITelescopeSyncMousePointer()
0908 {
0909 #ifdef HAVE_INDI
0910     slotINDITelescopeSync(false);
0911 #endif
0912 }
0913 
0914 void KStars::slotINDITelescopeAbort()
0915 {
0916 #ifdef HAVE_INDI
0917     if (m_KStarsData == nullptr || INDIListener::Instance() == nullptr)
0918         return;
0919 
0920     for (auto &oneDevice : INDIListener::devices())
0921     {
0922         if (!(oneDevice->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE))
0923             continue;
0924 
0925         auto mount = oneDevice->getMount();
0926         if (!mount || mount->isConnected() == false)
0927             continue;
0928 
0929         mount->abort();
0930         return;
0931     }
0932 #endif
0933 }
0934 
0935 void KStars::slotINDITelescopePark()
0936 {
0937 #ifdef HAVE_INDI
0938     if (m_KStarsData == nullptr || INDIListener::Instance() == nullptr)
0939         return;
0940 
0941     for (auto &oneDevice : INDIListener::devices())
0942     {
0943         if (!(oneDevice->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE))
0944             continue;
0945 
0946         auto mount = oneDevice->getMount();
0947         if (!mount || mount->isConnected() == false || mount->canPark() == false)
0948             continue;
0949 
0950         mount->park();
0951         return;
0952     }
0953 #endif
0954 }
0955 
0956 void KStars::slotINDITelescopeUnpark()
0957 {
0958 #ifdef HAVE_INDI
0959     if (m_KStarsData == nullptr || INDIListener::Instance() == nullptr)
0960         return;
0961 
0962     for (auto &oneDevice : INDIListener::devices())
0963     {
0964         if (!(oneDevice->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE))
0965             continue;
0966 
0967         auto mount = oneDevice->getMount();
0968         if (!mount || mount->isConnected() == false || mount->canPark() == false)
0969             continue;
0970 
0971         mount->unpark();
0972         return;
0973     }
0974 #endif
0975 }
0976 
0977 void KStars::slotINDIDomePark()
0978 {
0979 #ifdef HAVE_INDI
0980     if (m_KStarsData == nullptr || INDIListener::Instance() == nullptr)
0981         return;
0982 
0983     for (auto &oneDevice : INDIListener::devices())
0984     {
0985         if (!(oneDevice->getDriverInterface() & INDI::BaseDevice::DOME_INTERFACE))
0986             continue;
0987 
0988         auto dome = oneDevice->getDome();
0989         if (!dome || dome->isConnected() == false)
0990             continue;
0991         if (dome->canPark())
0992         {
0993             dome->park();
0994             return;
0995         }
0996     }
0997 #endif
0998 }
0999 
1000 void KStars::slotINDIDomeUnpark()
1001 {
1002 #ifdef HAVE_INDI
1003     if (m_KStarsData == nullptr || INDIListener::Instance() == nullptr)
1004         return;
1005 
1006     for (auto &oneDevice : INDIListener::devices())
1007     {
1008         if (!(oneDevice->getDriverInterface() & INDI::BaseDevice::DOME_INTERFACE))
1009             continue;
1010 
1011         auto dome = oneDevice->getDome();
1012         if (!dome || dome->isConnected() == false)
1013             continue;
1014         if (dome->canPark())
1015         {
1016             dome->unpark();
1017             return;
1018         }
1019     }
1020 #endif
1021 }
1022 
1023 void KStars::slotGeoLocator()
1024 {
1025     QPointer<LocationDialog> locationdialog = new LocationDialog(this);
1026     if (locationdialog->exec() == QDialog::Accepted)
1027     {
1028         GeoLocation *newLocation = locationdialog->selectedCity();
1029         if (newLocation)
1030         {
1031             // set new location in options
1032             data()->setLocation(*newLocation);
1033 
1034             // adjust local time to keep UT the same.
1035             // create new LT without DST offset
1036             KStarsDateTime ltime = newLocation->UTtoLT(data()->ut());
1037 
1038             // reset timezonerule to compute next dst change
1039             newLocation->tzrule()->reset_with_ltime(ltime, newLocation->TZ0(),
1040                                                     data()->isTimeRunningForward());
1041 
1042             // reset next dst change time
1043             data()->setNextDSTChange(newLocation->tzrule()->nextDSTChange());
1044 
1045             // reset local sideral time
1046             data()->syncLST();
1047 
1048             // Make sure Numbers, Moon, planets, and sky objects are updated immediately
1049             data()->setFullTimeUpdate();
1050 
1051             // If the sky is in Horizontal mode and not tracking, reset focus such that
1052             // Alt/Az remain constant.
1053             if (!Options::isTracking() && Options::useAltAz())
1054             {
1055                 map()->focus()->HorizontalToEquatorial(data()->lst(),
1056                                                        data()->geo()->lat());
1057             }
1058 
1059             // recalculate new times and objects
1060             data()->setSnapNextFocus();
1061             updateTime();
1062         }
1063     }
1064     delete locationdialog;
1065 }
1066 
1067 void KStars::slotViewOps()
1068 {
1069     // An instance of your dialog could be already created and could be cached,
1070     // in which case you want to display the cached dialog instead of creating
1071     // another one
1072     auto ops = prepareOps();
1073     ops->show();
1074     // Bring to the front.
1075     ops->raise();  // for MacOS
1076     ops->activateWindow(); // for Windows
1077 }
1078 
1079 KConfigDialog *KStars::prepareOps()
1080 {
1081     KConfigDialog *dialog = KConfigDialog::exists("settings");
1082     if (nullptr != dialog)
1083         return dialog;
1084 
1085     // KConfigDialog didn't find an instance of this dialog, so lets create it :
1086     dialog = new KConfigDialog(this, "settings", Options::self());
1087 
1088     // For some reason the dialog does not resize to contents
1089     // so we set initial 'resonable' size here. Any better way to do this?
1090     dialog->resize(800, 600);
1091 #ifdef Q_OS_OSX
1092     dialog->setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
1093 #endif
1094 
1095     connect(dialog, SIGNAL(settingsChanged(QString)), this,
1096             SLOT(slotApplyConfigChanges()));
1097 
1098     opcatalog     = new OpsCatalog();
1099     opguides      = new OpsGuides();
1100     opterrain     = new OpsTerrain();
1101     opsImageOverlay = new OpsImageOverlay();
1102     opsdeveloper  = new OpsDeveloper();
1103     opsolsys      = new OpsSolarSystem();
1104     opssatellites = new OpsSatellites();
1105     opssupernovae = new OpsSupernovae();
1106     opcolors      = new OpsColors();
1107     opadvanced    = new OpsAdvanced();
1108 
1109     KPageWidgetItem *page;
1110 
1111     page = dialog->addPage(opcatalog, i18n("Catalogs"), "kstars_catalog");
1112     page->setIcon(QIcon::fromTheme("kstars_catalog"));
1113 
1114     page = dialog->addPage(opsolsys, i18n("Solar System"), "kstars_solarsystem");
1115     page->setIcon(QIcon::fromTheme("kstars_solarsystem"));
1116 
1117     page = dialog->addPage(opssatellites, i18n("Satellites"), "kstars_satellites");
1118     page->setIcon(QIcon::fromTheme("kstars_satellites"));
1119 
1120     page = dialog->addPage(opssupernovae, i18n("Supernovae"), "kstars_supernovae");
1121     page->setIcon(QIcon::fromTheme("kstars_supernovae"));
1122 
1123     page = dialog->addPage(opguides, i18n("Guides"), "kstars_guides");
1124     page->setIcon(QIcon::fromTheme("kstars_guides"));
1125 
1126     page = dialog->addPage(opterrain, i18n("Terrain"), "kstars_terrain");
1127     page->setIcon(QIcon::fromTheme("kstars_terrain", QIcon(":/icons/kstars_terrain.png")));
1128 
1129     page = dialog->addPage(opsImageOverlay, i18n("Image Overlays"), "kstars_imageoverlay");
1130     page->setIcon(QIcon::fromTheme("kstars_imageoverlay", QIcon(":/icons/kstars_imageoverlay.png")));
1131     KStarsData::Instance()->skyComposite()->imageOverlay()->setWidgets(
1132         opsImageOverlay->table(), opsImageOverlay->statusDisplay(), opsImageOverlay->solvePushButton(),
1133         opsImageOverlay->tableTitleBox(), opsImageOverlay->solverProfile());
1134 
1135     page = dialog->addPage(opcolors, i18n("Colors"), "kstars_colors");
1136     page->setIcon(QIcon::fromTheme("kstars_colors"));
1137 
1138 #ifdef HAVE_CFITSIO
1139     opsfits = new OpsFITS();
1140     page    = dialog->addPage(opsfits, i18n("FITS"), "kstars_fitsviewer");
1141     page->setIcon(QIcon::fromTheme("kstars_fitsviewer"));
1142 #endif
1143 
1144 #ifdef HAVE_INDI
1145     opsindi = new OpsINDI();
1146     page    = dialog->addPage(opsindi, i18n("INDI"), "kstars_indi");
1147     page->setIcon(QIcon::fromTheme("kstars_indi"));
1148 #ifdef HAVE_CFITSIO
1149     opsekos                     = new OpsEkos();
1150     KPageWidgetItem *ekosOption = dialog->addPage(opsekos, i18n("Ekos"), "kstars_ekos");
1151     ekosOption->setIcon(QIcon::fromTheme("kstars_ekos"));
1152     if (Ekos::Manager::Instance())
1153         Ekos::Manager::Instance()->setOptionsWidget(ekosOption, opsekos);
1154 #endif
1155 
1156 #endif
1157 
1158     opsxplanet = new OpsXplanet(this);
1159     page       = dialog->addPage(opsxplanet, i18n("Xplanet"), "kstars_xplanet");
1160     page->setIcon(QIcon::fromTheme("kstars_xplanet"));
1161 
1162     page = dialog->addPage(opadvanced, i18n("Advanced"), "kstars_advanced");
1163     page->setIcon(QIcon::fromTheme("kstars_advanced"));
1164 
1165     page = dialog->addPage(opsdeveloper, i18n("Developer"), "kstars_developer");
1166     page->setIcon(QIcon::fromTheme("kstars_developer", QIcon(":/icons/kstars_developer.png")));
1167 
1168 #ifdef Q_OS_OSX // This is because KHelpClient doesn't seem to be working right on MacOS
1169     dialog->button(QDialogButtonBox::Help)->disconnect();
1170     connect(dialog->button(QDialogButtonBox::Help), &QPushButton::clicked, this, []()
1171     {
1172         KStars::Instance()->appHelpActivated();
1173     });
1174 #endif
1175 
1176     return dialog;
1177 }
1178 
1179 void KStars::syncOps()
1180 {
1181     opterrain->syncOptions();
1182     actionCollection()->action("toggle_terrain")
1183     ->setText(Options::showTerrain() ? i18n("Hide Terrain") : i18n("Show Terrain"));
1184 
1185     opsImageOverlay->syncOptions();
1186     actionCollection()->action("toggle_image_overlays")
1187     ->setText(Options::showImageOverlays() ? i18n("Hide Image Overlays") : i18n("Show Image Overlays"));
1188 }
1189 
1190 void KStars::slotApplyConfigChanges()
1191 {
1192     Options::self()->save();
1193 
1194     applyConfig();
1195 
1196     //data()->setFullTimeUpdate();
1197     //map()->forceUpdate();
1198 }
1199 
1200 void KStars::slotApplyWIConfigChanges()
1201 {
1202     Options::self()->save();
1203     applyConfig();
1204     m_WIView->updateObservingConditions();
1205     m_WIView->onReloadIconClicked();
1206 }
1207 
1208 void KStars::slotSetTime()
1209 {
1210     QPointer<TimeDialog> timedialog = new TimeDialog(data()->lt(), data()->geo(), this);
1211 
1212     if (timedialog->exec() == QDialog::Accepted)
1213     {
1214         data()->changeDateTime(data()->geo()->LTtoUT(timedialog->selectedDateTime()));
1215 
1216         if (Options::useAltAz())
1217         {
1218             if (map()->focusObject())
1219             {
1220                 map()->focusObject()->EquatorialToHorizontal(data()->lst(),
1221                         data()->geo()->lat());
1222                 map()->setFocus(map()->focusObject());
1223             }
1224             else
1225                 map()->focus()->HorizontalToEquatorial(data()->lst(),
1226                                                        data()->geo()->lat());
1227         }
1228 
1229         map()->forceUpdateNow();
1230 
1231         //If focusObject has a Planet Trail, clear it and start anew.
1232         KSPlanetBase *planet = dynamic_cast<KSPlanetBase *>(map()->focusObject());
1233         if (planet && planet->hasTrail())
1234         {
1235             planet->clearTrail();
1236             planet->addToTrail();
1237         }
1238     }
1239     delete timedialog;
1240 }
1241 
1242 //Set Time to CPU clock
1243 void KStars::slotSetTimeToNow()
1244 {
1245     data()->changeDateTime(KStarsDateTime::currentDateTimeUtc());
1246 
1247     if (Options::useAltAz())
1248     {
1249         if (map()->focusObject())
1250         {
1251             map()->focusObject()->EquatorialToHorizontal(data()->lst(),
1252                     data()->geo()->lat());
1253             map()->setFocus(map()->focusObject());
1254         }
1255         else
1256             map()->focus()->HorizontalToEquatorial(data()->lst(), data()->geo()->lat());
1257     }
1258 
1259     map()->forceUpdateNow();
1260 
1261     //If focusObject has a Planet Trail, clear it and start anew.
1262     KSPlanetBase *planet = dynamic_cast<KSPlanetBase *>(map()->focusObject());
1263     if (planet && planet->hasTrail())
1264     {
1265         planet->clearTrail();
1266         planet->addToTrail();
1267     }
1268 }
1269 
1270 void KStars::slotFind()
1271 {
1272     //clearCachedFindDialog();
1273     SkyObject *targetObject = nullptr;
1274     if (FindDialog::Instance()->exec() == QDialog::Accepted &&
1275             (targetObject = FindDialog::Instance()->targetObject()))
1276     {
1277         map()->setClickedObject(targetObject);
1278         map()->setClickedPoint(map()->clickedObject());
1279         map()->slotCenter();
1280     }
1281 
1282     // check if data has changed while dialog was open
1283     //if (DialogIsObsolete)
1284     //    clearCachedFindDialog();
1285 }
1286 
1287 void KStars::slotOpenFITS()
1288 {
1289 #ifdef HAVE_CFITSIO
1290     auto fv = createFITSViewer();
1291     fv->openFile();
1292 #endif
1293 }
1294 
1295 void KStars::slotBlink()
1296 {
1297 #ifdef HAVE_CFITSIO
1298     auto fv = createFITSViewer();
1299     fv->blink();
1300 #endif
1301 }
1302 
1303 void KStars::slotExportImage()
1304 {
1305     //TODO Check this
1306     //For remote files, this returns
1307     //QFileInfo::absolutePath: QFileInfo::absolutePath: Constructed with empty filename
1308     //As of 2014-07-19
1309     //QUrl fileURL = KFileDialog::getSaveUrl( QDir::homePath(), "image/png image/jpeg image/gif image/x-portable-pixmap image/bmp image/svg+xml" );
1310     QUrl fileURL =
1311         QFileDialog::getSaveFileUrl(KStars::Instance(), i18nc("@title:window", "Export Image"), QUrl(),
1312                                     "Images (*.png *.jpeg *.gif *.bmp *.svg)");
1313 
1314     //User cancelled file selection dialog - abort image export
1315     if (fileURL.isEmpty())
1316     {
1317         return;
1318     }
1319 
1320     //Warn user if file exists!
1321     if (QFile::exists(fileURL.toLocalFile()))
1322     {
1323         int r = KMessageBox::warningContinueCancel(
1324                     parentWidget(),
1325                     i18n("A file named \"%1\" already exists. Overwrite it?", fileURL.fileName()),
1326                     i18n("Overwrite File?"), KStandardGuiItem::overwrite());
1327         if (r == KMessageBox::Cancel)
1328             return;
1329     }
1330 
1331     // execute image export dialog
1332 
1333     // 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.
1334     //if ( !m_ImageExporter )
1335     //m_ImageExporter = new ImageExporter( this );
1336 
1337     if (!m_ExportImageDialog)
1338     {
1339         m_ExportImageDialog = new ExportImageDialog(
1340             fileURL.toLocalFile(), QSize(map()->width(), map()->height()),
1341             KStarsData::Instance()->imageExporter());
1342     }
1343     else
1344     {
1345         m_ExportImageDialog->setOutputUrl(fileURL.toLocalFile());
1346         m_ExportImageDialog->setOutputSize(QSize(map()->width(), map()->height()));
1347     }
1348 
1349     m_ExportImageDialog->show();
1350 }
1351 
1352 void KStars::slotRunScript()
1353 {
1354     QUrl fileURL = QFileDialog::getOpenFileUrl(
1355                        KStars::Instance(), QString(), QUrl(QDir::homePath()),
1356                        "*.kstars|" +
1357                        i18nc("Filter by file type: KStars Scripts.", "KStars Scripts (*.kstars)"));
1358     QFile f;
1359     //QString fname;
1360 
1361     if (fileURL.isValid())
1362     {
1363         if (fileURL.isLocalFile() == false)
1364         {
1365             KSNotification::sorry(i18n("Executing remote scripts is not supported."));
1366             return;
1367         }
1368 
1369         f.setFileName(fileURL.toLocalFile());
1370 
1371         if (!f.open(QIODevice::ReadOnly))
1372         {
1373             QString message = i18n("Could not open file %1", f.fileName());
1374             KSNotification::sorry(message, i18n("Could Not Open File"));
1375             return;
1376         }
1377 
1378         QTextStream istream(&f);
1379         QString line;
1380         bool fileOK(true);
1381 
1382         while (!istream.atEnd())
1383         {
1384             line = istream.readLine();
1385             if (line.at(0) != '#' && line.left(9) != "dbus-send")
1386             {
1387                 fileOK = false;
1388                 break;
1389             }
1390         }
1391 
1392         if (!fileOK)
1393         {
1394             int answer;
1395             answer = KMessageBox::warningContinueCancel(
1396                          nullptr,
1397                          i18n(
1398                              "The selected script contains unrecognized elements, "
1399                              "indicating that it was not created using the KStars script builder. "
1400                              "This script may not function properly, and it may even contain "
1401                              "malicious code. "
1402                              "Would you like to execute it anyway?"),
1403                          i18n("Script Validation Failed"), KGuiItem(i18n("Run Nevertheless")),
1404                          KStandardGuiItem::cancel(), "daExecuteScript");
1405             if (answer == KMessageBox::Cancel)
1406                 return;
1407         }
1408 
1409         //Add statusbar message that script is running
1410         statusBar()->showMessage(i18n("Running script: %1", fileURL.fileName()));
1411 
1412         // 2017-09-19: Jasem
1413         // FIXME This is a hack and does not work on non-Linux systems
1414         // The Script Builder should generate files that can run cross-platform
1415         QProcess p;
1416         QStringList arguments;
1417         p.start(f.fileName(), arguments);
1418         if (!p.waitForStarted())
1419             return;
1420 
1421         while (!p.waitForFinished(10))
1422         {
1423             qApp->processEvents(); //otherwise tempfile may get deleted before script completes.
1424             if (p.state() != QProcess::Running)
1425                 break;
1426         }
1427 
1428         statusBar()->showMessage(i18n("Script finished."), 0);
1429     }
1430 }
1431 
1432 void KStars::slotPrint()
1433 {
1434     bool switchColors(false);
1435 
1436     //Suggest Chart color scheme
1437     if (data()->colorScheme()->colorNamed("SkyColor") != QColor(255, 255, 255))
1438     {
1439         QString message =
1440             i18n("You can save printer ink by using the \"Star Chart\" "
1441                  "color scheme, which uses a white background. Would you like to "
1442                  "temporarily switch to the Star Chart color scheme for printing?");
1443 
1444         int answer = KMessageBox::questionYesNoCancel(
1445                          nullptr, message, i18n("Switch to Star Chart Colors?"),
1446                          KGuiItem(i18n("Switch Color Scheme")), KGuiItem(i18n("Do Not Switch")),
1447                          KStandardGuiItem::cancel(), "askAgainPrintColors");
1448 
1449         if (answer == KMessageBox::Cancel)
1450             return;
1451         if (answer == KMessageBox::Yes)
1452             switchColors = true;
1453     }
1454 
1455     printImage(true, switchColors);
1456 }
1457 
1458 void KStars::slotPrintingWizard()
1459 {
1460     if (m_PrintingWizard)
1461     {
1462         delete m_PrintingWizard;
1463     }
1464 
1465     m_PrintingWizard = new PrintingWizard(this);
1466     m_PrintingWizard->show();
1467 }
1468 
1469 void KStars::slotToggleTimer()
1470 {
1471     if (data()->clock()->isActive())
1472     {
1473         data()->clock()->stop();
1474         updateTime();
1475     }
1476     else
1477     {
1478         if (fabs(data()->clock()->scale()) > Options::slewTimeScale())
1479             data()->clock()->setManualMode(true);
1480         data()->clock()->start();
1481         if (data()->clock()->isManualMode())
1482             map()->forceUpdate();
1483     }
1484 
1485     // Update clock state in options
1486     Options::setRunClock(data()->clock()->isActive());
1487 }
1488 
1489 void KStars::slotStepForward()
1490 {
1491     if (data()->clock()->isActive())
1492         data()->clock()->stop();
1493     data()->clock()->manualTick(true);
1494     map()->forceUpdate();
1495 }
1496 
1497 void KStars::slotStepBackward()
1498 {
1499     if (data()->clock()->isActive())
1500         data()->clock()->stop();
1501     data()->clock()->manualTick(true, true);
1502     map()->forceUpdate();
1503 }
1504 
1505 //Pointing
1506 void KStars::slotPointFocus()
1507 {
1508     // In the following cases, we set slewing=true in order to disengage tracking
1509     map()->stopTracking();
1510 
1511     if (sender() == actionCollection()->action("zenith"))
1512         map()->setDestinationAltAz(dms(90.0), map()->focus()->az(),
1513                                    Options::useRefraction());
1514     else if (sender() == actionCollection()->action("north"))
1515         map()->setDestinationAltAz(dms(15.0), dms(0.0001), Options::useRefraction());
1516     else if (sender() == actionCollection()->action("east"))
1517         map()->setDestinationAltAz(dms(15.0), dms(90.0), Options::useRefraction());
1518     else if (sender() == actionCollection()->action("south"))
1519         map()->setDestinationAltAz(dms(15.0), dms(180.0), Options::useRefraction());
1520     else if (sender() == actionCollection()->action("west"))
1521         map()->setDestinationAltAz(dms(15.0), dms(270.0), Options::useRefraction());
1522 }
1523 
1524 void KStars::slotTrack()
1525 {
1526     if (Options::isTracking())
1527     {
1528         Options::setIsTracking(false);
1529         actionCollection()->action("track_object")->setText(i18n("Engage &Tracking"));
1530         actionCollection()
1531         ->action("track_object")
1532         ->setIcon(QIcon::fromTheme("document-decrypt"));
1533 
1534         KSPlanetBase *planet = dynamic_cast<KSPlanetBase *>(map()->focusObject());
1535         if (planet && data()->temporaryTrail)
1536         {
1537             planet->clearTrail();
1538             data()->temporaryTrail = false;
1539         }
1540 
1541         map()->setClickedObject(nullptr);
1542         map()->setFocusObject(nullptr); //no longer tracking focusObject
1543         map()->setFocusPoint(nullptr);
1544     }
1545     else
1546     {
1547         map()->setClickedPoint(map()->focus());
1548         map()->setClickedObject(nullptr);
1549         map()->setFocusObject(nullptr); //no longer tracking focusObject
1550         map()->setFocusPoint(map()->clickedPoint());
1551         Options::setIsTracking(true);
1552         actionCollection()->action("track_object")->setText(i18n("Stop &Tracking"));
1553         actionCollection()
1554         ->action("track_object")
1555         ->setIcon(QIcon::fromTheme("document-encrypt"));
1556     }
1557 
1558     map()->forceUpdate();
1559 }
1560 
1561 void KStars::slotManualFocus()
1562 {
1563     QPointer<FocusDialog> focusDialog = new FocusDialog();
1564 
1565     // JM 2019-09-04: Should default to RA/DE always
1566     //    if (Options::useAltAz())
1567     //        focusDialog->activateAzAltPage();
1568 
1569     if (focusDialog->exec() == QDialog::Accepted)
1570     {
1571         //If the requested position is very near the pole, we need to point first
1572         //to an intermediate location just below the pole in order to get the longitudinal
1573         //position (RA/Az) right.
1574 
1575         // Do not access (RA0, Dec0) of focusDialog->point() as it can be of unknown epoch.
1576         // (RA, Dec) should be synced to JNow
1577         // -- asimha (2020-07-06)
1578         double realAlt(focusDialog->point()->alt().Degrees());
1579         double realDec(focusDialog->point()->dec().Degrees());
1580         if (Options::useAltAz() && realAlt > 89.0)
1581         {
1582             focusDialog->point()->setAlt(89.0);
1583             focusDialog->point()->HorizontalToEquatorial(data()->lst(),
1584                     data()->geo()->lat());
1585         }
1586         if (!Options::useAltAz() && realDec > 89.0)
1587         {
1588             focusDialog->point()->setDec(89.0);
1589             focusDialog->point()->EquatorialToHorizontal(data()->lst(),
1590                     data()->geo()->lat());
1591         }
1592 
1593         map()->setClickedPoint(focusDialog->point());
1594 
1595         if (Options::isTracking())
1596             slotTrack();
1597 
1598         map()->slotCenter();
1599 
1600         //The slew takes some time to complete, and this often causes the final focus point to be slightly
1601         //offset from the user's requested coordinates (because EquatorialToHorizontal() is called
1602         //throughout the process, which depends on the sidereal time).  So we now "polish" the final
1603         //position by resetting the final focus to the focusDialog point.
1604         //
1605         //Also, if the requested position was within 1 degree of the coordinate pole, this will
1606         //automatically correct the final pointing from the intermediate offset position to the final position
1607         data()->setSnapNextFocus();
1608         if (Options::useAltAz())
1609         {
1610             // N.B. We have applied unrefract() in focusDialog
1611             map()->setDestinationAltAz(focusDialog->point()->alt(),
1612                                        focusDialog->point()->az(), false);
1613         }
1614         else
1615         {
1616             map()->setDestination(focusDialog->point()->ra(),
1617                                   focusDialog->point()->dec());
1618         }
1619 
1620         //Now, if the requested point was near a pole, we need to reset the Alt/Dec of the focus.
1621         if (Options::useAltAz() && realAlt > 89.0)
1622             map()->focus()->setAlt(realAlt);
1623         if (!Options::useAltAz() && realDec > 89.0)
1624             map()->focus()->setDec(realAlt);
1625 
1626         //Don't track if we set Alt/Az coordinates.  This way, Alt/Az remain constant.
1627         if (focusDialog->usedAltAz())
1628             map()->stopTracking();
1629     }
1630     delete focusDialog;
1631 }
1632 
1633 void KStars::slotZoomChanged()
1634 {
1635     // Enable/disable actions
1636     actionCollection()->action("zoom_out")->setEnabled(Options::zoomFactor() > MINZOOM);
1637     actionCollection()->action("zoom_in")->setEnabled(Options::zoomFactor() < MAXZOOM);
1638     // Update status bar
1639     map()
1640     ->setupProjector(); // this needs to be run redundantly, so that the FOV returned below is up-to-date.
1641     float fov                      = map()->projector()->fov();
1642     KLocalizedString fovi18nstring =
1643         ki18nc("approximate field of view", "Approximate FOV: %1 degrees");
1644     if (fov < 1.0)
1645     {
1646         fov           = fov * 60.0;
1647         fovi18nstring =
1648             ki18nc("approximate field of view", "Approximate FOV: %1 arcminutes");
1649     }
1650     if (fov < 1.0)
1651     {
1652         fov           = fov * 60.0;
1653         fovi18nstring =
1654             ki18nc("approximate field of view", "Approximate FOV: %1 arcseconds");
1655     }
1656     QString fovstring = fovi18nstring.subs(QString::number(fov, 'f', 1)).toString();
1657 
1658     statusBar()->showMessage(fovstring, 0);
1659 }
1660 
1661 void KStars::slotSetZoom()
1662 {
1663     bool ok;
1664     double currentAngle = map()->width() / (Options::zoomFactor() * dms::DegToRad);
1665     double minAngle     = map()->width() / (MAXZOOM * dms::DegToRad);
1666     double maxAngle     = map()->width() / (MINZOOM * dms::DegToRad);
1667 
1668     double angSize = QInputDialog::getDouble(
1669                          nullptr,
1670                          i18nc("The user should enter an angle for the field-of-view of the display",
1671                                "Enter Desired Field-of-View Angle"),
1672                          i18n("Enter a field-of-view angle in degrees: "), currentAngle, minAngle,
1673                          maxAngle, 1, &ok);
1674 
1675     if (ok)
1676     {
1677         map()->setZoomFactor(map()->width() / (angSize * dms::DegToRad));
1678     }
1679 }
1680 
1681 void KStars::slotCoordSys()
1682 {
1683     if (Options::useAltAz())
1684     {
1685         Options::setUseAltAz(false);
1686         if (Options::useRefraction())
1687         {
1688             if (map()->focusObject()) //simply update focus to focusObject's position
1689                 map()->setFocus(map()->focusObject());
1690             else //need to recompute focus for unrefracted position
1691             {
1692                 // FIXME: Changed focus()->alt() to be unrefracted by convention; is this still necessary? -- asimha 2020/07/05
1693                 map()->setFocusAltAz(map()->focus()->alt(), map()->focus()->az());
1694                 map()->focus()->HorizontalToEquatorial(data()->lst(),
1695                                                        data()->geo()->lat());
1696             }
1697         }
1698         actionCollection()
1699         ->action("coordsys")
1700         ->setText(i18n("Switch to Horizontal View (Horizontal &Coordinates)"));
1701         actionCollection()
1702         ->action("up_orientation")
1703         ->setText(i18nc("Orientation of the sky map", "North &Up"));
1704         actionCollection()
1705         ->action("down_orientation")
1706         ->setText(i18nc("Orientation of the sky map", "North &Down"));
1707         actionCollection()
1708         ->action("erect_observer_correction")
1709         ->setEnabled(false);
1710     }
1711     else
1712     {
1713         Options::setUseAltAz(true);
1714         if (Options::useRefraction())
1715         {
1716             // FIXME: Changed focus()->alt() to be unrefracted by convention; is this still necessary? -- asimha 2020/07/05
1717             map()->setFocusAltAz(map()->focus()->alt(), map()->focus()->az());
1718         }
1719         actionCollection()
1720         ->action("coordsys")
1721         ->setText(i18n("Switch to Star Globe View (Equatorial &Coordinates)"));
1722         actionCollection()
1723         ->action("up_orientation")
1724         ->setText(i18nc("Orientation of the sky map", "Zenith &Up"));
1725         actionCollection()
1726         ->action("down_orientation")
1727         ->setText(i18nc("Orientation of the sky map", "Zenith &Down"));
1728         actionCollection()
1729         ->action("erect_observer_correction")
1730         ->setEnabled(true);
1731     }
1732     map()->forceUpdate();
1733 }
1734 
1735 void KStars::slotSkyMapOrientation()
1736 {
1737     if (sender() == actionCollection()->action("up_orientation"))
1738     {
1739         Options::setSkyRotation(0.0);
1740     }
1741     else if (sender() == actionCollection()->action("down_orientation"))
1742     {
1743         Options::setSkyRotation(180.0);
1744     }
1745     else
1746     {
1747         Q_ASSERT(false && "Unhandled orientation action");
1748         qCWarning(KSTARS) << "Unhandled orientation action";
1749     }
1750 
1751     Options::setErectObserverCorrection(actionCollection()->action("erect_observer_correction")->isChecked());
1752     map()->forceUpdate();
1753 }
1754 
1755 void KStars::slotMapProjection()
1756 {
1757     if (sender() == actionCollection()->action("project_lambert"))
1758         Options::setProjection(Projector::Lambert);
1759     if (sender() == actionCollection()->action("project_azequidistant"))
1760         Options::setProjection(Projector::AzimuthalEquidistant);
1761     if (sender() == actionCollection()->action("project_orthographic"))
1762         Options::setProjection(Projector::Orthographic);
1763     if (sender() == actionCollection()->action("project_equirectangular"))
1764         Options::setProjection(Projector::Equirectangular);
1765     if (sender() == actionCollection()->action("project_stereographic"))
1766         Options::setProjection(Projector::Stereographic);
1767     if (sender() == actionCollection()->action("project_gnomonic"))
1768         Options::setProjection(Projector::Gnomonic);
1769 
1770     //DEBUG
1771     qCDebug(KSTARS) << "Projection system: " << Options::projection();
1772 
1773     m_SkyMap->forceUpdate();
1774 }
1775 
1776 //Settings Menu:
1777 void KStars::slotColorScheme()
1778 {
1779     loadColorScheme(sender()->objectName());
1780 }
1781 
1782 void KStars::slotTargetSymbol(bool flag)
1783 {
1784     qDebug() << Q_FUNC_INFO << QString("slotTargetSymbol: %1 %2").arg(sender()->objectName()).arg(flag);
1785 
1786     QStringList names = Options::fOVNames();
1787     if (flag)
1788     {
1789         // Add FOV to list
1790         names.append(sender()->objectName());
1791     }
1792     else
1793     {
1794         // Remove FOV from list
1795         int ix = names.indexOf(sender()->objectName());
1796         if (ix >= 0)
1797             names.removeAt(ix);
1798     }
1799     Options::setFOVNames(names);
1800 
1801     // Sync visibleFOVs with fovNames
1802     data()->syncFOV();
1803 
1804     map()->forceUpdate();
1805 }
1806 
1807 void KStars::slotHIPSSource()
1808 {
1809     QAction *selectedAction = qobject_cast<QAction *>(sender());
1810     Q_ASSERT(selectedAction != nullptr);
1811 
1812     QString selectedSource = selectedAction->text().remove('&');
1813 
1814     // selectedSource could be translated, while we need to send only Latin "None"
1815     // to Hips manager.
1816     if (selectedSource == i18n("None"))
1817         HIPSManager::Instance()->setCurrentSource("None");
1818     else
1819         HIPSManager::Instance()->setCurrentSource(selectedSource);
1820 
1821     map()->forceUpdate();
1822 }
1823 
1824 void KStars::slotFOVEdit()
1825 {
1826     QPointer<FOVDialog> fovdlg = new FOVDialog(this);
1827     if (fovdlg->exec() == QDialog::Accepted)
1828     {
1829         FOVManager::save();
1830         repopulateFOV();
1831     }
1832     delete fovdlg;
1833 }
1834 
1835 void KStars::slotObsList()
1836 {
1837     m_KStarsData->observingList()->show();
1838 }
1839 
1840 void KStars::slotEquipmentWriter()
1841 {
1842     QPointer<EquipmentWriter> equipmentdlg = new EquipmentWriter();
1843     equipmentdlg->loadEquipment();
1844     equipmentdlg->exec();
1845     delete equipmentdlg;
1846 }
1847 
1848 void KStars::slotObserverManager()
1849 {
1850     QPointer<ObserverAdd> m_observerAdd = new ObserverAdd();
1851     m_observerAdd->exec();
1852     delete m_observerAdd;
1853 }
1854 
1855 void KStars::slotHorizonManager()
1856 {
1857     if (!m_HorizonManager)
1858     {
1859         m_HorizonManager = new HorizonManager(this);
1860         connect(m_SkyMap, SIGNAL(positionClicked(SkyPoint *)), m_HorizonManager,
1861                 SLOT(addSkyPoint(SkyPoint *)));
1862     }
1863 
1864     m_HorizonManager->show();
1865 }
1866 
1867 void KStars::slotEyepieceView(SkyPoint *sp, const QString &imagePath)
1868 {
1869     if (!m_EyepieceView)
1870         m_EyepieceView = new EyepieceField(this);
1871 
1872     // FIXME: Move FOV choice into the Eyepiece View tool itself.
1873     bool ok        = true;
1874     const FOV *fov = nullptr;
1875     if (!data()->getAvailableFOVs().isEmpty())
1876     {
1877         // Ask the user to choose from a list of available FOVs.
1878         //int index;
1879         const FOV *f;
1880         QMap<QString, const FOV *> nameToFovMap;
1881         foreach (f, data()->getAvailableFOVs())
1882         {
1883             nameToFovMap.insert(f->name(), f);
1884         }
1885         nameToFovMap.insert(i18n("Attempt to determine from image"), nullptr);
1886         fov = nameToFovMap[QInputDialog::getItem(
1887                                this, i18n("Eyepiece View: Choose a field-of-view"),
1888                                i18n("FOV to render eyepiece view for:"), nameToFovMap.keys(), 0, false,
1889                                &ok)];
1890     }
1891     if (ok)
1892         m_EyepieceView->showEyepieceField(sp, fov, imagePath);
1893 }
1894 
1895 void KStars::slotExecute()
1896 {
1897     KStarsData::Instance()->executeSession()->init();
1898     KStarsData::Instance()->executeSession()->show();
1899 }
1900 
1901 void KStars::slotPolarisHourAngle()
1902 {
1903     QPointer<PolarisHourAngle> pHourAngle = new PolarisHourAngle(this);
1904     pHourAngle->exec();
1905 }
1906 
1907 //Help Menu
1908 void KStars::slotTipOfDay()
1909 {
1910     KTipDialog::showTip(this, "kstars/tips", true);
1911 }
1912 
1913 // Toggle to and from full screen mode
1914 void KStars::slotFullScreen()
1915 {
1916     if (topLevelWidget()->isFullScreen())
1917     {
1918         topLevelWidget()->setWindowState(topLevelWidget()->windowState() &
1919                                          ~Qt::WindowFullScreen); // reset
1920     }
1921     else
1922     {
1923         topLevelWidget()->setWindowState(topLevelWidget()->windowState() |
1924                                          Qt::WindowFullScreen); // set
1925     }
1926 }
1927 
1928 // Toggle showing terrain on the SkyMap.
1929 void KStars::slotTerrain()
1930 {
1931     Options::setShowTerrain(!Options::showTerrain());
1932     actionCollection()->action("toggle_terrain")
1933     ->setText(Options::showTerrain() ? i18n("Hide Terrain") : i18n("Show Terrain"));
1934     opterrain->syncOptions();
1935     KStars::Instance()->map()->forceUpdate();
1936 }
1937 
1938 // Toggle showing image overlays on the SkyMap.
1939 void KStars::slotImageOverlays()
1940 {
1941     Options::setShowImageOverlays(!Options::showImageOverlays());
1942     actionCollection()->action("toggle_image_overlays")
1943     ->setText(Options::showImageOverlays() ? i18n("Hide Image Overlays") : i18n("Show Image Overlays"));
1944     opsImageOverlay->syncOptions();
1945     KStars::Instance()->map()->forceUpdate();
1946 }
1947 
1948 void KStars::slotClearAllTrails()
1949 {
1950     //Exclude object with temporary trail
1951     SkyObject *exOb(nullptr);
1952     if (map()->focusObject() && map()->focusObject()->isSolarSystem() &&
1953             data()->temporaryTrail)
1954     {
1955         exOb = map()->focusObject();
1956     }
1957 
1958     TrailObject::clearTrailsExcept(exOb);
1959 
1960     map()->forceUpdate();
1961 }
1962 
1963 //toggle display of GUI Items on/off
1964 void KStars::slotShowGUIItem(bool show)
1965 {
1966     //Toolbars
1967     if (sender() == actionCollection()->action("show_statusBar"))
1968     {
1969         Options::setShowStatusBar(show);
1970         statusBar()->setVisible(show);
1971     }
1972 
1973     if (sender() == actionCollection()->action("show_sbAzAlt"))
1974     {
1975         Options::setShowAltAzField(show);
1976         AltAzField.setHidden(!show);
1977     }
1978 
1979     if (sender() == actionCollection()->action("show_sbRADec"))
1980     {
1981         Options::setShowRADecField(show);
1982         RADecField.setHidden(!show);
1983     }
1984 
1985     if (sender() == actionCollection()->action("show_sbJ2000RADec"))
1986     {
1987         Options::setShowJ2000RADecField(show);
1988         J2000RADecField.setHidden(!show);
1989     }
1990 }
1991 void KStars::addColorMenuItem(QString name, const QString &actionName)
1992 {
1993     KToggleAction *kta     = actionCollection()->add<KToggleAction>(actionName);
1994     const QString filename = QString(actionName).mid(3) + ".colors";
1995     kta->setText(name);
1996     kta->setObjectName(filename);
1997     kta->setActionGroup(cschemeGroup);
1998 
1999     colorActionMenu->addAction(kta);
2000 
2001     KConfigGroup cg = KSharedConfig::openConfig()->group("Colors");
2002     if (actionName.mid(3) ==
2003             cg.readEntry("ColorSchemeFile", "moonless-night.colors").remove(".colors"))
2004     {
2005         kta->setChecked(true);
2006     }
2007 
2008     //use mid(3) to exclude the leading "cs_" prefix from the action name
2009     data()->add_color_scheme(filename, name.replace("&", ""));
2010     connect(kta, SIGNAL(toggled(bool)), this, SLOT(slotColorScheme()));
2011 }
2012 
2013 void KStars::removeColorMenuItem(const QString &actionName)
2014 {
2015     qCDebug(KSTARS) << "removing " << actionName;
2016     colorActionMenu->removeAction(actionCollection()->action(actionName));
2017 }
2018 
2019 void KStars::slotAboutToQuit()
2020 {
2021     if (m_SkyMap == nullptr)
2022         return;
2023 
2024 #ifdef HAVE_INDI
2025     DriverManager::Instance()->disconnectClients();
2026     INDIListener::Instance()->disconnect();
2027     GUIManager::Instance()->disconnect();
2028 #endif
2029 
2030     // Delete skymap. This required to run destructors and save
2031     // current state in the option.
2032     delete m_SkyMap;
2033     m_SkyMap = nullptr;
2034 
2035     //Store Window geometry in Options object
2036     Options::setWindowWidth(width());
2037     Options::setWindowHeight(height());
2038 
2039     //explicitly save the colorscheme data to the config file
2040     data()->colorScheme()->saveToConfig();
2041 
2042     //synch the config file with the Config object
2043     writeConfig();
2044 
2045     //Terminate Child Processes if on OS X
2046 #ifdef Q_OS_OSX
2047     QProcess *quit = new QProcess(this);
2048     quit->start("killall kdeinit5");
2049     quit->waitForFinished(1000);
2050     quit->start("killall klauncher");
2051     quit->waitForFinished(1000);
2052     quit->start("killall kioslave");
2053     quit->waitForFinished(1000);
2054     quit->start("killall kio_http_cache_cleaner");
2055     quit->waitForFinished(1000);
2056     delete quit;
2057 #endif
2058 }
2059 
2060 void KStars::slotShowPositionBar(SkyPoint *p)
2061 {
2062     if (Options::showAltAzField())
2063     {
2064         dms a = p->alt();
2065         if (Options::useAltAz())
2066             a = p->altRefracted();
2067         QString s =
2068             QString("%1, %2").arg(p->az().toDMSString(true), //true: force +/- symbol
2069                                   a.toDMSString(true));      //true: force +/- symbol
2070         //statusBar()->changeItem( s, 1 );
2071         AltAzField.setText(s);
2072     }
2073     if (Options::showRADecField())
2074     {
2075         KStarsDateTime lastUpdate;
2076         lastUpdate.setDJD(KStarsData::Instance()->updateNum()->getJD());
2077         QString sEpoch = QString::number(lastUpdate.epoch(), 'f', 1);
2078         QString s      = QString("%1, %2 (J%3)")
2079                          .arg(p->ra().toHMSString(), p->dec().toDMSString(true),
2080                               sEpoch); //true: force +/- symbol
2081         //statusBar()->changeItem( s, 2 );
2082         RADecField.setText(s);
2083     }
2084 
2085     if (Options::showJ2000RADecField())
2086     {
2087         SkyPoint p0;
2088         //p0        = p->deprecess(KStarsData::Instance()->updateNum()); // deprecess to update RA0/Dec0 from RA/Dec
2089         p0 = p->catalogueCoord(KStarsData::Instance()->updateNum()->julianDay());
2090         QString s = QString("%1, %2 (J2000)")
2091                     .arg(p0.ra().toHMSString(),
2092                          p0.dec().toDMSString(true)); //true: force +/- symbol
2093         //statusBar()->changeItem( s, 2 );
2094         J2000RADecField.setText(s);
2095     }
2096 }
2097 
2098 void KStars::slotUpdateComets(bool isAutoUpdate)
2099 {
2100     data()->skyComposite()->solarSystemComposite()->cometsComponent()->updateDataFile(
2101         isAutoUpdate);
2102 }
2103 
2104 void KStars::slotUpdateAsteroids(bool isAutoUpdate)
2105 {
2106     data()->skyComposite()->solarSystemComposite()->asteroidsComponent()->updateDataFile(
2107         isAutoUpdate);
2108 }
2109 
2110 void KStars::slotUpdateSupernovae()
2111 {
2112     data()->skyComposite()->supernovaeComponent()->slotTriggerDataFileUpdate();
2113 }
2114 
2115 void KStars::slotUpdateSatellites()
2116 {
2117     data()->skyComposite()->satellites()->updateTLEs();
2118 }
2119 
2120 void KStars::slotConfigureNotifications()
2121 {
2122 #ifdef HAVE_NOTIFYCONFIG
2123     KNotifyConfigWidget::configure(this);
2124 #endif
2125 }
2126 void KStars::slotDSOCatalogGUI()
2127 {
2128     auto *ui = new CatalogsDBUI{ this, CatalogsDB::dso_db_path() };
2129     ui->show();
2130     connect(ui, &QDialog::finished, this, [&](const auto)
2131     {
2132         KStars::Instance()->data()->skyComposite()->catalogsComponent()->dropCache();
2133     });
2134 }