File indexing completed on 2024-06-16 09:53:38

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     skymapOrientationGroup = new QActionGroup(this);
0170 
0171 
0172     m_KStarsData = KStarsData::Create();
0173     Q_ASSERT(m_KStarsData);
0174     //Set Geographic Location from Options
0175     m_KStarsData->setLocationFromOptions();
0176 
0177     //Initialize Time and Date
0178     bool datetimeSet = false;
0179     if (StartDateString.isEmpty() == false)
0180     {
0181         KStarsDateTime startDate = KStarsDateTime::fromString(StartDateString);
0182         if (startDate.isValid())
0183             data()->changeDateTime(data()->geo()->LTtoUT(startDate));
0184         else
0185             data()->changeDateTime(KStarsDateTime::currentDateTimeUtc());
0186 
0187         datetimeSet = true;
0188     }
0189     // JM 2016-11-15: Not need to set it again as it was initialized in the ctor of SimClock
0190     /*
0191     else
0192         data()->changeDateTime( KStarsDateTime::currentDateTimeUtc() );
0193     */
0194 
0195     // If we are starting paused (--paused is not in the command line) change datetime in data
0196     if (StartClockRunning == false)
0197     {
0198         qCInfo(KSTARS) << "KStars is started in paused state.";
0199         if (datetimeSet == false)
0200             data()->changeDateTime(KStarsDateTime::currentDateTimeUtc());
0201     }
0202 
0203     // Setup splash screen
0204     KStarsSplash *splash = nullptr;
0205     if (doSplash)
0206     {
0207         splash = new KStarsSplash(nullptr);
0208         connect(m_KStarsData, SIGNAL(progressText(QString)), splash, SLOT(setMessage(QString)));
0209         splash->show();
0210     }
0211     else
0212     {
0213         connect(m_KStarsData, SIGNAL(progressText(QString)), m_KStarsData, SLOT(slotConsoleMessage(QString)));
0214     }
0215 
0216     /*
0217     //set up Dark color scheme for application windows
0218     DarkPalette = QPalette(QColor("black"), QColor("black"));
0219     DarkPalette.setColor(QPalette::Inactive, QPalette::WindowText, QColor("red"));
0220     DarkPalette.setColor(QPalette::Normal, QPalette::WindowText, QColor("red"));
0221     DarkPalette.setColor(QPalette::Normal, QPalette::Base, QColor("black"));
0222     DarkPalette.setColor(QPalette::Normal, QPalette::Text, QColor(238, 0, 0));
0223     DarkPalette.setColor(QPalette::Normal, QPalette::Highlight, QColor(238, 0, 0));
0224     DarkPalette.setColor(QPalette::Normal, QPalette::HighlightedText, QColor("black"));
0225     DarkPalette.setColor(QPalette::Inactive, QPalette::Text, QColor(238, 0, 0));
0226     DarkPalette.setColor(QPalette::Inactive, QPalette::Base, QColor(30, 10, 10));
0227     //store original color scheme
0228     OriginalPalette = QApplication::palette();
0229     */
0230 
0231     //Initialize data.  When initialization is complete, it will run dataInitFinished()
0232     if (!m_KStarsData->initialize())
0233         return;
0234     delete splash;
0235     datainitFinished();
0236 }
0237 
0238 KStars *KStars::createInstance(bool doSplash, bool clockrun, const QString &startdate)
0239 {
0240     delete pinstance;
0241     // pinstance is set directly in constructor.
0242     new KStars(doSplash, clockrun, startdate);
0243     Q_ASSERT(pinstance && "pinstance must be non NULL");
0244     return pinstance;
0245 }
0246 
0247 KStars::~KStars()
0248 {
0249     releaseResources();
0250     Q_ASSERT(pinstance);
0251     pinstance = nullptr;
0252 #ifdef PROFILE_COORDINATE_CONVERSION
0253     qDebug() << Q_FUNC_INFO << "Spent " << SkyPoint::cpuTime_EqToHz << " seconds in " << SkyPoint::eqToHzCalls
0254              << " calls to SkyPoint::EquatorialToHorizontal, for an average of "
0255              << 1000. * (SkyPoint::cpuTime_EqToHz / SkyPoint::eqToHzCalls) << " ms per call";
0256 #endif
0257 
0258 #ifdef COUNT_DMS_SINCOS_CALLS
0259     qDebug() << Q_FUNC_INFO << "Constructed " << dms::dms_constructor_calls << " dms objects, of which " <<
0260              dms::dms_with_sincos_called
0261              << " had trigonometric functions called on them = "
0262              << (float(dms::dms_with_sincos_called) / float(dms::dms_constructor_calls)) * 100. << "%";
0263     qDebug() << Q_FUNC_INFO << "Of the " << dms::trig_function_calls << " calls to sin/cos/sincos on dms objects, "
0264              << dms::redundant_trig_function_calls << " were redundant = "
0265              << ((float(dms::redundant_trig_function_calls) / float(dms::trig_function_calls)) * 100.) << "%";
0266     qDebug() << Q_FUNC_INFO << "We had " << CachingDms::cachingdms_bad_uses << " bad uses of CachingDms in all, compared to "
0267              << CachingDms::cachingdms_constructor_calls << " constructed CachingDms objects = "
0268              << (float(CachingDms::cachingdms_bad_uses) / float(CachingDms::cachingdms_constructor_calls)) * 100.
0269              << "% bad uses";
0270 #endif
0271 }
0272 
0273 void KStars::releaseResources()
0274 {
0275     delete m_KStarsData;
0276     m_KStarsData = nullptr;
0277     delete StarBlockFactory::Instance();
0278     TextureManager::Release();
0279     SkyQPainter::releaseImageCache();
0280     FOVManager::releaseCache();
0281 
0282 #ifdef HAVE_INDI
0283     GUIManager::release();
0284     Ekos::Manager::release();
0285     DriverManager::release();
0286 #endif
0287 
0288     QSqlDatabase::removeDatabase("userdb");
0289     QSqlDatabase::removeDatabase("skydb");
0290 }
0291 
0292 void KStars::clearCachedFindDialog()
0293 {
0294 #if 0
0295     if (m_FindDialog) // dialog is cached
0296     {
0297         /** Delete findDialog only if it is not opened */
0298         if (m_FindDialog->isHidden())
0299         {
0300             delete m_FindDialog;
0301             m_FindDialog     = nullptr;
0302             DialogIsObsolete = false;
0303         }
0304         else
0305             DialogIsObsolete = true; // dialog was opened so it could not deleted
0306     }
0307 #endif
0308 }
0309 
0310 void KStars::applyConfig(bool doApplyFocus)
0311 {
0312     if (Options::isTracking())
0313     {
0314         actionCollection()->action("track_object")->setText(i18n("Stop &Tracking"));
0315         actionCollection()
0316         ->action("track_object")
0317         ->setIcon(QIcon::fromTheme("document-encrypt"));
0318     }
0319 
0320     actionCollection()
0321     ->action("coordsys")
0322     ->setText(Options::useAltAz() ? i18n("Switch to Star Globe View (Equatorial &Coordinates)") :
0323               i18n("Switch to Horizontal View (Horizontal &Coordinates)"));
0324 
0325     actionCollection()->action("show_time_box")->setChecked(Options::showTimeBox());
0326     actionCollection()->action("show_location_box")->setChecked(Options::showGeoBox());
0327     actionCollection()->action("show_focus_box")->setChecked(Options::showFocusBox());
0328     actionCollection()->action("show_statusBar")->setChecked(Options::showStatusBar());
0329     actionCollection()->action("show_sbAzAlt")->setChecked(Options::showAltAzField());
0330     actionCollection()->action("show_sbRADec")->setChecked(Options::showRADecField());
0331     actionCollection()->action("show_sbJ2000RADec")->setChecked(Options::showJ2000RADecField());
0332     actionCollection()->action("show_stars")->setChecked(Options::showStars());
0333     actionCollection()->action("show_deepsky")->setChecked(Options::showDeepSky());
0334     actionCollection()->action("show_planets")->setChecked(Options::showSolarSystem());
0335     actionCollection()->action("show_clines")->setChecked(Options::showCLines());
0336     actionCollection()->action("show_constellationart")->setChecked(Options::showConstellationArt());
0337     actionCollection()->action("show_cnames")->setChecked(Options::showCNames());
0338     actionCollection()->action("show_cbounds")->setChecked(Options::showCBounds());
0339     actionCollection()->action("show_mw")->setChecked(Options::showMilkyWay());
0340     actionCollection()->action("show_equatorial_grid")->setChecked(Options::showEquatorialGrid());
0341     actionCollection()->action("show_horizontal_grid")->setChecked(Options::showHorizontalGrid());
0342     actionCollection()->action("show_horizon")->setChecked(Options::showGround());
0343     actionCollection()->action("show_flags")->setChecked(Options::showFlags());
0344     actionCollection()->action("show_supernovae")->setChecked(Options::showSupernovae());
0345     actionCollection()->action("show_satellites")->setChecked(Options::showSatellites());
0346     actionCollection()->action("erect_observer_correction")->setChecked(Options::erectObserverCorrection());
0347     actionCollection()->action("erect_observer_correction")->setEnabled(Options::useAltAz());
0348     statusBar()->setVisible(Options::showStatusBar());
0349 
0350     //color scheme
0351     m_KStarsData->colorScheme()->loadFromConfig();
0352     //QApplication::setPalette(Options::darkAppColors() ? DarkPalette : OriginalPalette);
0353     /**
0354     //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.
0355     //It might be a good idea to use stylesheets in the future instead of palettes but this will work for now for OS X.
0356     //This is also in KStarsDbus.cpp.  If you change it, change it in BOTH places.
0357     @code
0358     #ifdef Q_OS_OSX
0359         if (Options::darkAppColors())
0360             qApp->setStyleSheet(
0361                 "QWidget { background-color: black; color:red; "
0362                 "selection-background-color:rgb(30,30,30);selection-color:white}"
0363                 "QToolBar { border:none }"
0364                 "QTabBar::tab:selected { background-color:rgb(50,50,50) }"
0365                 "QTabBar::tab:!selected { background-color:rgb(30,30,30) }"
0366                 "QPushButton { background-color:rgb(50,50,50);border-width:1px; border-style:solid;border-color:black}"
0367                 "QPushButton::disabled { background-color:rgb(10,10,10);border-width:1px; "
0368                 "border-style:solid;border-color:black }"
0369                 "QToolButton:Checked { background-color:rgb(30,30,30); border:none }"
0370                 "QComboBox { background-color:rgb(30,30,30); }"
0371                 "QComboBox::disabled { background-color:rgb(10,10,10) }"
0372                 "QScrollBar::handle { background: rgb(30,30,30) }"
0373                 "QSpinBox { border-width: 1px; border-style:solid; border-color:rgb(30,30,30) }"
0374                 "QDoubleSpinBox { border-width:1px; border-style:solid; border-color:rgb(30,30,30) }"
0375                 "QLineEdit { border-width: 1px; border-style: solid; border-color:rgb(30,30,30) }"
0376                 "QCheckBox::indicator:unchecked { background-color:rgb(30,30,30);border-width:1px; "
0377                 "border-style:solid;border-color:black }"
0378                 "QCheckBox::indicator:checked { background-color:red;border-width:1px; "
0379                 "border-style:solid;border-color:black }"
0380                 "QRadioButton::indicator:unchecked { background-color:rgb(30,30,30) }"
0381                 "QRadioButton::indicator:checked { background-color:red }"
0382                 "QRoundProgressBar { alternate-background-color:black }"
0383                 "QDateTimeEdit {background-color:rgb(30,30,30); border-width: 1px; border-style:solid; "
0384                 "border-color:rgb(30,30,30) }"
0385                 "QHeaderView { color:red;background-color:black }"
0386                 "QHeaderView::Section { background-color:rgb(30,30,30) }"
0387                 "QTableCornerButton::section{ background-color:rgb(30,30,30) }"
0388                 "");
0389         else
0390             qApp->setStyleSheet("");
0391     #endif
0392     @endcode
0393     **/
0394 
0395     //Set toolbar options from config file
0396     toolBar("kstarsToolBar")->applySettings(KSharedConfig::openConfig()->group("MainToolBar"));
0397     toolBar("viewToolBar")->applySettings(KSharedConfig::openConfig()->group("ViewToolBar"));
0398 
0399     //Geographic location
0400     data()->setLocationFromOptions();
0401 
0402     //Focus
0403     if (doApplyFocus)
0404     {
0405         SkyObject *fo = data()->objectNamed(Options::focusObject());
0406         if (fo && fo != map()->focusObject())
0407         {
0408             map()->setClickedObject(fo);
0409             map()->setClickedPoint(fo);
0410             map()->slotCenter();
0411         }
0412 
0413         if (!fo)
0414         {
0415             SkyPoint fp(Options::focusRA(), Options::focusDec());
0416             if (fp.ra().Degrees() != map()->focus()->ra().Degrees() ||
0417                     fp.dec().Degrees() != map()->focus()->dec().Degrees())
0418             {
0419                 map()->setClickedPoint(&fp);
0420                 map()->slotCenter();
0421             }
0422         }
0423     }
0424 }
0425 
0426 void KStars::showImgExportDialog()
0427 {
0428     if (m_ExportImageDialog)
0429         m_ExportImageDialog->show();
0430 }
0431 
0432 void KStars::syncFOVActions()
0433 {
0434     foreach (QAction *action, fovActionMenu->menu()->actions())
0435     {
0436         if (action->text().isEmpty())
0437         {
0438             continue;
0439         }
0440 
0441         if (Options::fOVNames().contains(action->text().remove(0, 1)))
0442         {
0443             action->setChecked(true);
0444         }
0445         else
0446         {
0447             action->setChecked(false);
0448         }
0449     }
0450 }
0451 
0452 void KStars::hideAllFovExceptFirst()
0453 {
0454     // When there is only one visible FOV symbol, we don't need to do anything
0455     // Also, don't do anything if there are no available FOV symbols.
0456     if (data()->visibleFOVs.size() == 1 || data()->availFOVs.isEmpty())
0457     {
0458         return;
0459     }
0460     else
0461     {
0462         // If there are no visible FOVs, select first available
0463         if (data()->visibleFOVs.isEmpty())
0464         {
0465             Q_ASSERT(!data()->availFOVs.isEmpty());
0466             Options::setFOVNames(QStringList(data()->availFOVs.first()->name()));
0467         }
0468         else
0469         {
0470             Options::setFOVNames(QStringList(data()->visibleFOVs.first()->name()));
0471         }
0472 
0473         // Sync FOV and update skymap
0474         data()->syncFOV();
0475         syncFOVActions();
0476         map()->update(); // SkyMap::forceUpdate() is not required, as FOVs are drawn as overlays
0477     }
0478 }
0479 
0480 void KStars::selectNextFov()
0481 {
0482     if (data()->getVisibleFOVs().isEmpty())
0483         return;
0484 
0485     Q_ASSERT(!data()
0486              ->getAvailableFOVs()
0487              .isEmpty()); // The available FOVs had better not be empty if the visible ones are not.
0488 
0489     FOV *currentFov = data()->getVisibleFOVs().first();
0490     int currentIdx  = data()->availFOVs.indexOf(currentFov);
0491 
0492     // If current FOV is not the available FOV list or there is only 1 FOV available, then return
0493     if (currentIdx == -1 || data()->availFOVs.size() < 2)
0494     {
0495         return;
0496     }
0497 
0498     QStringList nextFovName;
0499     if (currentIdx == data()->availFOVs.size() - 1)
0500     {
0501         nextFovName << data()->availFOVs.first()->name();
0502     }
0503     else
0504     {
0505         nextFovName << data()->availFOVs.at(currentIdx + 1)->name();
0506     }
0507 
0508     Options::setFOVNames(nextFovName);
0509     data()->syncFOV();
0510     syncFOVActions();
0511     map()->update();
0512 }
0513 
0514 void KStars::selectPreviousFov()
0515 {
0516     if (data()->getVisibleFOVs().isEmpty())
0517         return;
0518 
0519     Q_ASSERT(!data()
0520              ->getAvailableFOVs()
0521              .isEmpty()); // The available FOVs had better not be empty if the visible ones are not.
0522 
0523     FOV *currentFov = data()->getVisibleFOVs().first();
0524     int currentIdx  = data()->availFOVs.indexOf(currentFov);
0525 
0526     // If current FOV is not the available FOV list or there is only 1 FOV available, then return
0527     if (currentIdx == -1 || data()->availFOVs.size() < 2)
0528     {
0529         return;
0530     }
0531 
0532     QStringList prevFovName;
0533     if (currentIdx == 0)
0534     {
0535         prevFovName << data()->availFOVs.last()->name();
0536     }
0537     else
0538     {
0539         prevFovName << data()->availFOVs.at(currentIdx - 1)->name();
0540     }
0541 
0542     Options::setFOVNames(prevFovName);
0543     data()->syncFOV();
0544     syncFOVActions();
0545     map()->update();
0546 }
0547 
0548 //FIXME Port to QML2
0549 //#if 0
0550 void KStars::showWISettingsUI()
0551 {
0552     slotWISettings();
0553 }
0554 //#endif
0555 
0556 void KStars::updateTime(const bool automaticDSTchange)
0557 {
0558     // Due to frequently use of this function save data and map pointers for speedup.
0559     // Save options and geo() to a pointer would not speedup because most of time options
0560     // and geo will accessed only one time.
0561     KStarsData *Data = data();
0562     // dms oldLST( Data->lst()->Degrees() );
0563 
0564     Data->updateTime(Data->geo(), automaticDSTchange);
0565 
0566     //We do this outside of kstarsdata just to get the coordinates
0567     //displayed in the infobox to update every second.
0568     //  if ( !Options::isTracking() && LST()->Degrees() > oldLST.Degrees() ) {
0569     //      int nSec = int( 3600.*( LST()->Hours() - oldLST.Hours() ) );
0570     //      Map->focus()->setRA( Map->focus()->ra().Hours() + double( nSec )/3600. );
0571     //      if ( Options::useAltAz() ) Map->focus()->EquatorialToHorizontal( LST(), geo()->lat() );
0572     //      Map->showFocusCoords();
0573     //  }
0574 
0575     //If time is accelerated beyond slewTimescale, then the clock's timer is stopped,
0576     //so that it can be ticked manually after each update, in order to make each time
0577     //step exactly equal to the timeScale setting.
0578     //Wrap the call to manualTick() in a singleshot timer so that it doesn't get called until
0579     //the skymap has been completely updated.
0580     if (Data->clock()->isManualMode() && Data->clock()->isActive())
0581     {
0582         // Jasem 2017-11-13: Time for each update varies.
0583         // Ideally we want to advance the simulation clock by
0584         // the current clock scale (e.g. 1 hour) every 1 second
0585         // of real time. However, the sky map update, depending on calculations and
0586         // drawing of objects, takes variable time to complete.
0587         //QTimer::singleShot(0, Data->clock(), SLOT(manualTick()));
0588         QTimer::singleShot(1000, Data->clock(), SLOT(manualTick()));
0589     }
0590 }
0591 
0592 #ifdef HAVE_CFITSIO
0593 const QSharedPointer<FITSViewer> &KStars::createFITSViewer()
0594 {
0595     if (Options::singleWindowCapturedFITS())
0596         return KStars::Instance()->genericFITSViewer();
0597     else
0598     {
0599         QSharedPointer<FITSViewer> newFITSViewer(new FITSViewer(Options::independentWindowFITS() ? nullptr : KStars::Instance()),
0600                 &QObject::deleteLater);
0601         m_FITSViewers.append(newFITSViewer);
0602         connect(newFITSViewer.get(), &FITSViewer::terminated, this, [this]()
0603         {
0604             auto rawPointer = dynamic_cast<FITSViewer*>(sender());
0605             m_FITSViewers.erase(std::remove_if(m_FITSViewers.begin(), m_FITSViewers.end(), [rawPointer](auto & viewer)
0606             {
0607                 return viewer.get() == rawPointer;
0608             }));
0609         });
0610         return m_FITSViewers.constLast();
0611     }
0612 }
0613 
0614 const QSharedPointer<FITSViewer> &KStars::genericFITSViewer()
0615 {
0616     if (m_GenericFITSViewer.isNull())
0617     {
0618         m_GenericFITSViewer.reset(new FITSViewer(Options::independentWindowFITS() ? nullptr : this), &QObject::deleteLater);
0619         connect(m_GenericFITSViewer.get(), &FITSViewer::terminated, this, [this]()
0620         {
0621             m_FITSViewers.removeOne(m_GenericFITSViewer);
0622             m_GenericFITSViewer.clear();
0623         });
0624         m_FITSViewers.append(m_GenericFITSViewer);
0625     }
0626 
0627     return m_GenericFITSViewer;
0628 }
0629 
0630 void KStars::clearAllViewers()
0631 {
0632     for (auto &fv : m_FITSViewers)
0633         fv->close();
0634 
0635     m_FITSViewers.clear();
0636 }
0637 #endif
0638 
0639 void KStars::closeEvent(QCloseEvent *event)
0640 {
0641     KStars::Closing = true;
0642     QWidget::closeEvent(event);
0643     slotAboutToQuit();
0644 }