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

0001 /*
0002     SPDX-FileCopyrightText: 2001 Jason Harris <jharris@30doradus.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "kstars.h"
0008 
0009 #include "config-kstars.h"
0010 #include "version.h"
0011 
0012 #include "fov.h"
0013 #include "kactionmenu.h"
0014 #include "kstarsadaptor.h"
0015 #include "kstarsdata.h"
0016 #include "kstarssplash.h"
0017 #include "observinglist.h"
0018 #include "Options.h"
0019 #include "skymap.h"
0020 #include "skyqpainter.h"
0021 #include "texturemanager.h"
0022 #include "dialogs/finddialog.h"
0023 #include "dialogs/exportimagedialog.h"
0024 #include "skycomponents/starblockfactory.h"
0025 #ifdef HAVE_INDI
0026 #include "ekos/manager.h"
0027 #include "indi/drivermanager.h"
0028 #include "indi/guimanager.h"
0029 #include "indi/indilistener.h"
0030 #endif
0031 
0032 #ifdef HAVE_CFITSIO
0033 #include "fitsviewer/fitsviewer.h"
0034 #endif
0035 
0036 #include <KActionCollection>
0037 #include <KToolBar>
0038 
0039 #ifdef Q_OS_WIN
0040 #include <QProcess>
0041 #endif
0042 #include <QStatusBar>
0043 #include <QMenu>
0044 
0045 #include <kstars_debug.h>
0046 
0047 KStars *KStars::pinstance = nullptr;
0048 bool KStars::Closing = false;
0049 
0050 KStars::KStars(bool doSplash, bool clockrun, const QString &startdate)
0051     : KXmlGuiWindow(), StartClockRunning(clockrun), StartDateString(startdate)
0052 {
0053     // FIXME Hack to set RTL direction for Arabic
0054     // This is not a solution. It seems qtbase_ar.qm needs to take care of this?
0055     // qttranslations5-l10n does not contain qtbase_ar.qm
0056     // It seems qtbase_ar.ts does not exist for Qt 5.9 at all and needs to be translated.
0057     // https://wiki.qt.io/Qt_Localization
0058     if (i18n("Sky") == "السماء")
0059         qApp->setLayoutDirection(Qt::RightToLeft);
0060 
0061     setWindowTitle(i18nc("@title:window", "KStars"));
0062 
0063     // Set thread stack size to 32MB
0064     QThreadPool::globalInstance()->setStackSize(33554432);
0065 
0066     // Initialize logging settings
0067     if (Options::disableLogging())
0068         KSUtils::Logging::Disable();
0069     else if (Options::logToFile())
0070         KSUtils::Logging::UseFile();
0071     else
0072         KSUtils::Logging::UseDefault();
0073 
0074     KSUtils::Logging::SyncFilterRules();
0075 
0076     qCInfo(KSTARS) << "Welcome to KStars" << KSTARS_VERSION << KSTARS_BUILD_RELEASE;
0077     qCInfo(KSTARS) << "Build:" << KSTARS_BUILD_TS;
0078     qCInfo(KSTARS) << "OS:" << QSysInfo::productType();
0079     qCInfo(KSTARS) << "API:" << QSysInfo::buildAbi();
0080     qCInfo(KSTARS) << "Arch:" << QSysInfo::currentCpuArchitecture();
0081     qCInfo(KSTARS) << "Kernel Type:" << QSysInfo::kernelType();
0082     qCInfo(KSTARS) << "Kernel Version:" << QSysInfo::kernelVersion();
0083     qCInfo(KSTARS) << "Qt Version:" << QT_VERSION_STR;
0084 
0085     new KstarsAdaptor(
0086         this); // NOTE the weird case convention, which cannot be changed as the file is generated by the moc.
0087 
0088 #ifdef Q_OS_OSX
0089 
0090     QString vlcPlugins = QDir(QCoreApplication::applicationDirPath() + "/../PlugIns/vlc").absolutePath();
0091     qputenv("VLC_PLUGIN_PATH", vlcPlugins.toLatin1());
0092     QString phonon_backend_path = QDir(QCoreApplication::applicationDirPath() +
0093                                        "/../PlugIns/phonon4qt5_backend/phonon_vlc.so").absolutePath();
0094     qputenv("PHONON_BACKEND", phonon_backend_path.toLatin1());
0095 
0096     QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
0097     QString path            = env.value("PATH", "");
0098     env.insert("PATH", "/usr/bin:/usr/local/bin:\"" + QCoreApplication::applicationDirPath() + "\":" + path);
0099 
0100     QProcess dbusCheck;
0101     dbusCheck.setProcessEnvironment(env);
0102 
0103     QString pluginsDir = QDir(QCoreApplication::applicationDirPath() + "/../PlugIns").absolutePath();
0104     QString dbusPlist  = pluginsDir + "/dbus/org.freedesktop.dbus-kstars.plist";
0105     QFile file(dbusPlist);
0106     if (file.open(QIODevice::ReadOnly))
0107     {
0108         QTextStream in(&file);
0109         QString pListText = in.readAll();
0110         file.close();
0111         int programArgsLeft         = pListText.indexOf("<key>ProgramArguments</key>");
0112         int programArgsRight        = pListText.indexOf("</array>", programArgsLeft) + 8 - programArgsLeft;
0113         QString currentProgramArgs  = pListText.mid(programArgsLeft, programArgsRight);
0114         QString newProgramArguments = ""
0115                                       "<key>ProgramArguments</key>\n"
0116                                       "    <array>\n"
0117                                       "        <string>" +
0118                                       QCoreApplication::applicationDirPath() +
0119                                       "/dbus-daemon</string>\n"
0120                                       "        <string>--nofork</string>\n"
0121                                       "        <string>--config-file=" +
0122                                       pluginsDir +
0123                                       "/dbus/kstars.conf</string>\n"
0124                                       "    </array>";
0125         pListText.replace(currentProgramArgs, newProgramArguments);
0126         if (file.open(QIODevice::WriteOnly))
0127         {
0128             QTextStream stream(&file);
0129             stream << pListText;
0130             file.close();
0131 
0132             dbusCheck.start("chmod 775 " + dbusPlist);
0133             dbusCheck.waitForFinished();
0134             dbusCheck.start("launchctl load -w \"" + dbusPlist + "\"");
0135             dbusCheck.waitForFinished();
0136             qDebug("Starting DBus");
0137         }
0138         else
0139         {
0140             qDebug("DBus File Write Error");
0141         }
0142     }
0143     else
0144     {
0145         qDebug("DBus File Read Error");
0146     }
0147 #endif
0148 
0149     QDBusConnection::sessionBus().registerObject("/KStars", this);
0150     QDBusConnection::sessionBus().registerService("org.kde.kstars");
0151 
0152 #ifdef HAVE_CFITSIO
0153     m_GenericFITSViewer.clear();
0154 #endif
0155 
0156     // Set pinstance to yourself
0157     pinstance = this;
0158 
0159     connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(slotAboutToQuit()));
0160 
0161     //Initialize QActionGroups
0162     projectionGroup = new QActionGroup(this);
0163     cschemeGroup    = new QActionGroup(this);
0164     hipsGroup       = new QActionGroup(this);
0165     telescopeGroup  = new QActionGroup(this);
0166     telescopeGroup->setExclusive(false);
0167     domeGroup       = new QActionGroup(this);
0168     domeGroup->setExclusive(false);
0169 
0170 
0171     m_KStarsData = KStarsData::Create();
0172     Q_ASSERT(m_KStarsData);
0173     //Set Geographic Location from Options
0174     m_KStarsData->setLocationFromOptions();
0175 
0176     //Initialize Time and Date
0177     bool datetimeSet = false;
0178     if (StartDateString.isEmpty() == false)
0179     {
0180         KStarsDateTime startDate = KStarsDateTime::fromString(StartDateString);
0181         if (startDate.isValid())
0182             data()->changeDateTime(data()->geo()->LTtoUT(startDate));
0183         else
0184             data()->changeDateTime(KStarsDateTime::currentDateTimeUtc());
0185 
0186         datetimeSet = true;
0187     }
0188     // JM 2016-11-15: Not need to set it again as it was initialized in the ctor of SimClock
0189     /*
0190     else
0191         data()->changeDateTime( KStarsDateTime::currentDateTimeUtc() );
0192     */
0193 
0194     // If we are starting paused (--paused is not in the command line) change datetime in data
0195     if (StartClockRunning == false)
0196     {
0197         qCInfo(KSTARS) << "KStars is started in paused state.";
0198         if (datetimeSet == false)
0199             data()->changeDateTime(KStarsDateTime::currentDateTimeUtc());
0200     }
0201 
0202     // Setup splash screen
0203     KStarsSplash *splash = nullptr;
0204     if (doSplash)
0205     {
0206         splash = new KStarsSplash(nullptr);
0207         connect(m_KStarsData, SIGNAL(progressText(QString)), splash, SLOT(setMessage(QString)));
0208         splash->show();
0209     }
0210     else
0211     {
0212         connect(m_KStarsData, SIGNAL(progressText(QString)), m_KStarsData, SLOT(slotConsoleMessage(QString)));
0213     }
0214 
0215     /*
0216     //set up Dark color scheme for application windows
0217     DarkPalette = QPalette(QColor("black"), QColor("black"));
0218     DarkPalette.setColor(QPalette::Inactive, QPalette::WindowText, QColor("red"));
0219     DarkPalette.setColor(QPalette::Normal, QPalette::WindowText, QColor("red"));
0220     DarkPalette.setColor(QPalette::Normal, QPalette::Base, QColor("black"));
0221     DarkPalette.setColor(QPalette::Normal, QPalette::Text, QColor(238, 0, 0));
0222     DarkPalette.setColor(QPalette::Normal, QPalette::Highlight, QColor(238, 0, 0));
0223     DarkPalette.setColor(QPalette::Normal, QPalette::HighlightedText, QColor("black"));
0224     DarkPalette.setColor(QPalette::Inactive, QPalette::Text, QColor(238, 0, 0));
0225     DarkPalette.setColor(QPalette::Inactive, QPalette::Base, QColor(30, 10, 10));
0226     //store original color scheme
0227     OriginalPalette = QApplication::palette();
0228     */
0229 
0230     //Initialize data.  When initialization is complete, it will run dataInitFinished()
0231     if (!m_KStarsData->initialize())
0232         return;
0233     delete splash;
0234     datainitFinished();
0235 }
0236 
0237 KStars *KStars::createInstance(bool doSplash, bool clockrun, const QString &startdate)
0238 {
0239     delete pinstance;
0240     // pinstance is set directly in constructor.
0241     new KStars(doSplash, clockrun, startdate);
0242     Q_ASSERT(pinstance && "pinstance must be non NULL");
0243     return pinstance;
0244 }
0245 
0246 KStars::~KStars()
0247 {
0248     releaseResources();
0249     Q_ASSERT(pinstance);
0250     pinstance = nullptr;
0251 #ifdef PROFILE_COORDINATE_CONVERSION
0252     qDebug() << Q_FUNC_INFO << "Spent " << SkyPoint::cpuTime_EqToHz << " seconds in " << SkyPoint::eqToHzCalls
0253              << " calls to SkyPoint::EquatorialToHorizontal, for an average of "
0254              << 1000. * (SkyPoint::cpuTime_EqToHz / SkyPoint::eqToHzCalls) << " ms per call";
0255 #endif
0256 
0257 #ifdef COUNT_DMS_SINCOS_CALLS
0258     qDebug() << Q_FUNC_INFO << "Constructed " << dms::dms_constructor_calls << " dms objects, of which " <<
0259              dms::dms_with_sincos_called
0260              << " had trigonometric functions called on them = "
0261              << (float(dms::dms_with_sincos_called) / float(dms::dms_constructor_calls)) * 100. << "%";
0262     qDebug() << Q_FUNC_INFO << "Of the " << dms::trig_function_calls << " calls to sin/cos/sincos on dms objects, "
0263              << dms::redundant_trig_function_calls << " were redundant = "
0264              << ((float(dms::redundant_trig_function_calls) / float(dms::trig_function_calls)) * 100.) << "%";
0265     qDebug() << Q_FUNC_INFO << "We had " << CachingDms::cachingdms_bad_uses << " bad uses of CachingDms in all, compared to "
0266              << CachingDms::cachingdms_constructor_calls << " constructed CachingDms objects = "
0267              << (float(CachingDms::cachingdms_bad_uses) / float(CachingDms::cachingdms_constructor_calls)) * 100.
0268              << "% bad uses";
0269 #endif
0270 
0271     /* BUG 366596: Some KDE applications processes remain as background (zombie) processes after closing
0272          * No solution to this bug so far using Qt 5.8 as of 2016-11-24
0273          * Therefore, the only way to solve this on Windows is to explicitly kill kstars.exe
0274          * Hopefully we do not need this hack once the above bug is resolved.
0275          */
0276 #ifdef Q_OS_WIN
0277     QProcess::execute("taskkill /im kstars.exe /f");
0278 #endif
0279 }
0280 
0281 void KStars::releaseResources()
0282 {
0283     delete m_KStarsData;
0284     m_KStarsData = nullptr;
0285     delete StarBlockFactory::Instance();
0286     TextureManager::Release();
0287     SkyQPainter::releaseImageCache();
0288     FOVManager::releaseCache();
0289 
0290 #ifdef HAVE_INDI
0291     GUIManager::release();
0292     Ekos::Manager::release();
0293     DriverManager::release();
0294 #endif
0295 
0296 #ifdef HAVE_CFITSIO
0297     //if (Options::independentWindowFITS())
0298     qDeleteAll(m_FITSViewers);
0299 #endif
0300 
0301     QSqlDatabase::removeDatabase("userdb");
0302     QSqlDatabase::removeDatabase("skydb");
0303 }
0304 
0305 void KStars::clearCachedFindDialog()
0306 {
0307 #if 0
0308     if (m_FindDialog) // dialog is cached
0309     {
0310         /** Delete findDialog only if it is not opened */
0311         if (m_FindDialog->isHidden())
0312         {
0313             delete m_FindDialog;
0314             m_FindDialog     = nullptr;
0315             DialogIsObsolete = false;
0316         }
0317         else
0318             DialogIsObsolete = true; // dialog was opened so it could not deleted
0319     }
0320 #endif
0321 }
0322 
0323 void KStars::applyConfig(bool doApplyFocus)
0324 {
0325     if (Options::isTracking())
0326     {
0327         actionCollection()->action("track_object")->setText(i18n("Stop &Tracking"));
0328         actionCollection()
0329         ->action("track_object")
0330         ->setIcon(QIcon::fromTheme("document-encrypt"));
0331     }
0332 
0333     actionCollection()
0334     ->action("coordsys")
0335     ->setText(Options::useAltAz() ? i18n("Switch to Star Globe View (Equatorial &Coordinates)") :
0336               i18n("Switch to Horizonal View (Horizontal &Coordinates)"));
0337 
0338     actionCollection()->action("show_time_box")->setChecked(Options::showTimeBox());
0339     actionCollection()->action("show_location_box")->setChecked(Options::showGeoBox());
0340     actionCollection()->action("show_focus_box")->setChecked(Options::showFocusBox());
0341     actionCollection()->action("show_statusBar")->setChecked(Options::showStatusBar());
0342     actionCollection()->action("show_sbAzAlt")->setChecked(Options::showAltAzField());
0343     actionCollection()->action("show_sbRADec")->setChecked(Options::showRADecField());
0344     actionCollection()->action("show_sbJ2000RADec")->setChecked(Options::showJ2000RADecField());
0345     actionCollection()->action("show_stars")->setChecked(Options::showStars());
0346     actionCollection()->action("show_deepsky")->setChecked(Options::showDeepSky());
0347     actionCollection()->action("show_planets")->setChecked(Options::showSolarSystem());
0348     actionCollection()->action("show_clines")->setChecked(Options::showCLines());
0349     actionCollection()->action("show_constellationart")->setChecked(Options::showConstellationArt());
0350     actionCollection()->action("show_cnames")->setChecked(Options::showCNames());
0351     actionCollection()->action("show_cbounds")->setChecked(Options::showCBounds());
0352     actionCollection()->action("show_mw")->setChecked(Options::showMilkyWay());
0353     actionCollection()->action("show_equatorial_grid")->setChecked(Options::showEquatorialGrid());
0354     actionCollection()->action("show_horizontal_grid")->setChecked(Options::showHorizontalGrid());
0355     actionCollection()->action("show_horizon")->setChecked(Options::showGround());
0356     actionCollection()->action("show_flags")->setChecked(Options::showFlags());
0357     actionCollection()->action("show_supernovae")->setChecked(Options::showSupernovae());
0358     actionCollection()->action("show_satellites")->setChecked(Options::showSatellites());
0359     statusBar()->setVisible(Options::showStatusBar());
0360 
0361     //color scheme
0362     m_KStarsData->colorScheme()->loadFromConfig();
0363     //QApplication::setPalette(Options::darkAppColors() ? DarkPalette : OriginalPalette);
0364     /**
0365     //Note:  This uses style sheets to set the dark colors, this should be cross platform.  Palettes have a different behavior on OS X and Windows as opposed to Linux.
0366     //It might be a good idea to use stylesheets in the future instead of palettes but this will work for now for OS X.
0367     //This is also in KStarsDbus.cpp.  If you change it, change it in BOTH places.
0368     @code
0369     #ifdef Q_OS_OSX
0370         if (Options::darkAppColors())
0371             qApp->setStyleSheet(
0372                 "QWidget { background-color: black; color:red; "
0373                 "selection-background-color:rgb(30,30,30);selection-color:white}"
0374                 "QToolBar { border:none }"
0375                 "QTabBar::tab:selected { background-color:rgb(50,50,50) }"
0376                 "QTabBar::tab:!selected { background-color:rgb(30,30,30) }"
0377                 "QPushButton { background-color:rgb(50,50,50);border-width:1px; border-style:solid;border-color:black}"
0378                 "QPushButton::disabled { background-color:rgb(10,10,10);border-width:1px; "
0379                 "border-style:solid;border-color:black }"
0380                 "QToolButton:Checked { background-color:rgb(30,30,30); border:none }"
0381                 "QComboBox { background-color:rgb(30,30,30); }"
0382                 "QComboBox::disabled { background-color:rgb(10,10,10) }"
0383                 "QScrollBar::handle { background: rgb(30,30,30) }"
0384                 "QSpinBox { border-width: 1px; border-style:solid; border-color:rgb(30,30,30) }"
0385                 "QDoubleSpinBox { border-width:1px; border-style:solid; border-color:rgb(30,30,30) }"
0386                 "QLineEdit { border-width: 1px; border-style: solid; border-color:rgb(30,30,30) }"
0387                 "QCheckBox::indicator:unchecked { background-color:rgb(30,30,30);border-width:1px; "
0388                 "border-style:solid;border-color:black }"
0389                 "QCheckBox::indicator:checked { background-color:red;border-width:1px; "
0390                 "border-style:solid;border-color:black }"
0391                 "QRadioButton::indicator:unchecked { background-color:rgb(30,30,30) }"
0392                 "QRadioButton::indicator:checked { background-color:red }"
0393                 "QRoundProgressBar { alternate-background-color:black }"
0394                 "QDateTimeEdit {background-color:rgb(30,30,30); border-width: 1px; border-style:solid; "
0395                 "border-color:rgb(30,30,30) }"
0396                 "QHeaderView { color:red;background-color:black }"
0397                 "QHeaderView::Section { background-color:rgb(30,30,30) }"
0398                 "QTableCornerButton::section{ background-color:rgb(30,30,30) }"
0399                 "");
0400         else
0401             qApp->setStyleSheet("");
0402     #endif
0403     @endcode
0404     **/
0405 
0406     //Set toolbar options from config file
0407     toolBar("kstarsToolBar")->applySettings(KSharedConfig::openConfig()->group("MainToolBar"));
0408     toolBar("viewToolBar")->applySettings(KSharedConfig::openConfig()->group("ViewToolBar"));
0409 
0410     //Geographic location
0411     data()->setLocationFromOptions();
0412 
0413     //Focus
0414     if (doApplyFocus)
0415     {
0416         SkyObject *fo = data()->objectNamed(Options::focusObject());
0417         if (fo && fo != map()->focusObject())
0418         {
0419             map()->setClickedObject(fo);
0420             map()->setClickedPoint(fo);
0421             map()->slotCenter();
0422         }
0423 
0424         if (!fo)
0425         {
0426             SkyPoint fp(Options::focusRA(), Options::focusDec());
0427             if (fp.ra().Degrees() != map()->focus()->ra().Degrees() ||
0428                     fp.dec().Degrees() != map()->focus()->dec().Degrees())
0429             {
0430                 map()->setClickedPoint(&fp);
0431                 map()->slotCenter();
0432             }
0433         }
0434     }
0435 }
0436 
0437 void KStars::showImgExportDialog()
0438 {
0439     if (m_ExportImageDialog)
0440         m_ExportImageDialog->show();
0441 }
0442 
0443 void KStars::syncFOVActions()
0444 {
0445     foreach (QAction *action, fovActionMenu->menu()->actions())
0446     {
0447         if (action->text().isEmpty())
0448         {
0449             continue;
0450         }
0451 
0452         if (Options::fOVNames().contains(action->text().remove(0, 1)))
0453         {
0454             action->setChecked(true);
0455         }
0456         else
0457         {
0458             action->setChecked(false);
0459         }
0460     }
0461 }
0462 
0463 void KStars::hideAllFovExceptFirst()
0464 {
0465     // When there is only one visible FOV symbol, we don't need to do anything
0466     // Also, don't do anything if there are no available FOV symbols.
0467     if (data()->visibleFOVs.size() == 1 || data()->availFOVs.isEmpty())
0468     {
0469         return;
0470     }
0471     else
0472     {
0473         // If there are no visible FOVs, select first available
0474         if (data()->visibleFOVs.isEmpty())
0475         {
0476             Q_ASSERT(!data()->availFOVs.isEmpty());
0477             Options::setFOVNames(QStringList(data()->availFOVs.first()->name()));
0478         }
0479         else
0480         {
0481             Options::setFOVNames(QStringList(data()->visibleFOVs.first()->name()));
0482         }
0483 
0484         // Sync FOV and update skymap
0485         data()->syncFOV();
0486         syncFOVActions();
0487         map()->update(); // SkyMap::forceUpdate() is not required, as FOVs are drawn as overlays
0488     }
0489 }
0490 
0491 void KStars::selectNextFov()
0492 {
0493     if (data()->getVisibleFOVs().isEmpty())
0494         return;
0495 
0496     Q_ASSERT(!data()
0497              ->getAvailableFOVs()
0498              .isEmpty()); // The available FOVs had better not be empty if the visible ones are not.
0499 
0500     FOV *currentFov = data()->getVisibleFOVs().first();
0501     int currentIdx  = data()->availFOVs.indexOf(currentFov);
0502 
0503     // If current FOV is not the available FOV list or there is only 1 FOV available, then return
0504     if (currentIdx == -1 || data()->availFOVs.size() < 2)
0505     {
0506         return;
0507     }
0508 
0509     QStringList nextFovName;
0510     if (currentIdx == data()->availFOVs.size() - 1)
0511     {
0512         nextFovName << data()->availFOVs.first()->name();
0513     }
0514     else
0515     {
0516         nextFovName << data()->availFOVs.at(currentIdx + 1)->name();
0517     }
0518 
0519     Options::setFOVNames(nextFovName);
0520     data()->syncFOV();
0521     syncFOVActions();
0522     map()->update();
0523 }
0524 
0525 void KStars::selectPreviousFov()
0526 {
0527     if (data()->getVisibleFOVs().isEmpty())
0528         return;
0529 
0530     Q_ASSERT(!data()
0531              ->getAvailableFOVs()
0532              .isEmpty()); // The available FOVs had better not be empty if the visible ones are not.
0533 
0534     FOV *currentFov = data()->getVisibleFOVs().first();
0535     int currentIdx  = data()->availFOVs.indexOf(currentFov);
0536 
0537     // If current FOV is not the available FOV list or there is only 1 FOV available, then return
0538     if (currentIdx == -1 || data()->availFOVs.size() < 2)
0539     {
0540         return;
0541     }
0542 
0543     QStringList prevFovName;
0544     if (currentIdx == 0)
0545     {
0546         prevFovName << data()->availFOVs.last()->name();
0547     }
0548     else
0549     {
0550         prevFovName << data()->availFOVs.at(currentIdx - 1)->name();
0551     }
0552 
0553     Options::setFOVNames(prevFovName);
0554     data()->syncFOV();
0555     syncFOVActions();
0556     map()->update();
0557 }
0558 
0559 //FIXME Port to QML2
0560 //#if 0
0561 void KStars::showWISettingsUI()
0562 {
0563     slotWISettings();
0564 }
0565 //#endif
0566 
0567 void KStars::updateTime(const bool automaticDSTchange)
0568 {
0569     // Due to frequently use of this function save data and map pointers for speedup.
0570     // Save options and geo() to a pointer would not speedup because most of time options
0571     // and geo will accessed only one time.
0572     KStarsData *Data = data();
0573     // dms oldLST( Data->lst()->Degrees() );
0574 
0575     Data->updateTime(Data->geo(), automaticDSTchange);
0576 
0577     //We do this outside of kstarsdata just to get the coordinates
0578     //displayed in the infobox to update every second.
0579     //  if ( !Options::isTracking() && LST()->Degrees() > oldLST.Degrees() ) {
0580     //      int nSec = int( 3600.*( LST()->Hours() - oldLST.Hours() ) );
0581     //      Map->focus()->setRA( Map->focus()->ra().Hours() + double( nSec )/3600. );
0582     //      if ( Options::useAltAz() ) Map->focus()->EquatorialToHorizontal( LST(), geo()->lat() );
0583     //      Map->showFocusCoords();
0584     //  }
0585 
0586     //If time is accelerated beyond slewTimescale, then the clock's timer is stopped,
0587     //so that it can be ticked manually after each update, in order to make each time
0588     //step exactly equal to the timeScale setting.
0589     //Wrap the call to manualTick() in a singleshot timer so that it doesn't get called until
0590     //the skymap has been completely updated.
0591     if (Data->clock()->isManualMode() && Data->clock()->isActive())
0592     {
0593         // Jasem 2017-11-13: Time for each update varies.
0594         // Ideally we want to advance the simulation clock by
0595         // the current clock scale (e.g. 1 hour) every 1 second
0596         // of real time. However, the sky map update, depending on calculations and
0597         // drawing of objects, takes variable time to complete.
0598         //QTimer::singleShot(0, Data->clock(), SLOT(manualTick()));
0599         QTimer::singleShot(1000, Data->clock(), SLOT(manualTick()));
0600     }
0601 }
0602 
0603 #ifdef HAVE_CFITSIO
0604 const QPointer<FITSViewer> &KStars::createFITSViewer()
0605 {
0606     if (Options::singleWindowCapturedFITS())
0607         return KStars::Instance()->genericFITSViewer();
0608     else
0609     {
0610         QPointer<FITSViewer> newFITSViewer = new FITSViewer(Options::independentWindowFITS() ? nullptr : KStars::Instance());
0611 
0612         connect(newFITSViewer.data(), &FITSViewer::destroyed, [ = ]()
0613         {
0614             m_FITSViewers.removeOne(newFITSViewer);
0615         });
0616 
0617         m_FITSViewers.append(newFITSViewer);
0618         return m_FITSViewers.constLast();
0619     }
0620 }
0621 
0622 const QPointer<FITSViewer> &KStars::genericFITSViewer()
0623 {
0624     if (m_GenericFITSViewer.isNull())
0625     {
0626         m_GenericFITSViewer = new FITSViewer(Options::independentWindowFITS() ? nullptr : this);
0627         //m_GenericFITSViewer->setAttribute(Qt::WA_DeleteOnClose);
0628         m_FITSViewers.append(m_GenericFITSViewer);
0629     }
0630 
0631     return m_GenericFITSViewer;
0632 }
0633 
0634 //void KStars::addFITSViewer(const QSharedPointer<FITSViewer> &fv)
0635 //{
0636 //    m_FITSViewers.append(fv);
0637 //    connect(fv.data(), &FITSViewer::terminated, [ = ]()
0638 //    {
0639 //        m_FITSViewers.removeOne(fv);
0640 //    });
0641 //}
0642 
0643 void KStars::clearAllViewers()
0644 {
0645     for (auto &fv : m_FITSViewers)
0646         fv->close();
0647 
0648     m_FITSViewers.clear();
0649 }
0650 #endif
0651 
0652 void KStars::closeEvent(QCloseEvent *event)
0653 {
0654     KStars::Closing = true;
0655     QWidget::closeEvent(event);
0656     slotAboutToQuit();
0657 }