File indexing completed on 2022-12-06 18:58:46

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