1 /*
2 SPDX-FileCopyrightText: 2001 Jason Harris <jharris@30doradus.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "kstars.h"
8
9 #include "config-kstars.h"
10 #include "version.h"
11
12 #include "fov.h"
13 #include "kactionmenu.h"
14 #include "kstarsadaptor.h"
15 #include "kstarsdata.h"
16 #include "kstarssplash.h"
17 #include "observinglist.h"
18 #include "Options.h"
19 #include "skymap.h"
20 #include "skyqpainter.h"
21 #include "texturemanager.h"
22 #include "dialogs/finddialog.h"
23 #include "dialogs/exportimagedialog.h"
24 #include "skycomponents/starblockfactory.h"
25 #ifdef HAVE_INDI
26 #include "ekos/manager.h"
27 #include "indi/drivermanager.h"
28 #include "indi/guimanager.h"
29 #endif
30
31 #ifdef HAVE_CFITSIO
32 #include "fitsviewer/fitsviewer.h"
33 #endif
34
35 #include <KActionCollection>
36 #include <KToolBar>
37
38 #ifdef Q_OS_WIN
39 #include <QProcess>
40 #endif
41 #include <QStatusBar>
42 #include <QMenu>
43
44 #include <kstars_debug.h>
45
46 KStars *KStars::pinstance = nullptr;
47 bool KStars::Closing = false;
48
KStars(bool doSplash,bool clockrun,const QString & startdate)49 KStars::KStars(bool doSplash, bool clockrun, const QString &startdate)
50 : KXmlGuiWindow(), StartClockRunning(clockrun), StartDateString(startdate)
51 {
52 // FIXME Hack to set RTL direction for Arabic
53 // This is not a solution. It seems qtbase_ar.qm needs to take care of this?
54 // qttranslations5-l10n does not contain qtbase_ar.qm
55 // It seems qtbase_ar.ts does not exist for Qt 5.9 at all and needs to be translated.
56 // https://wiki.qt.io/Qt_Localization
57 if (i18n("Sky") == "السماء")
58 qApp->setLayoutDirection(Qt::RightToLeft);
59
60 setWindowTitle(i18nc("@title:window", "KStars"));
61
62 // Set thread stack size to 32MB
63 #if QT_VERSION >= QT_VERSION_CHECK(5,10,0)
64 QThreadPool::globalInstance()->setStackSize(33554432);
65 #endif
66
67 // Initialize logging settings
68 if (Options::disableLogging())
69 KSUtils::Logging::Disable();
70 else if (Options::logToFile())
71 KSUtils::Logging::UseFile();
72 else
73 KSUtils::Logging::UseDefault();
74
75 KSUtils::Logging::SyncFilterRules();
76
77 qCInfo(KSTARS) << "Welcome to KStars" << KSTARS_VERSION << KSTARS_BUILD_RELEASE;
78 qCInfo(KSTARS) << "Build:" << KSTARS_BUILD_TS;
79 qCInfo(KSTARS) << "OS:" << QSysInfo::productType();
80 qCInfo(KSTARS) << "API:" << QSysInfo::buildAbi();
81 qCInfo(KSTARS) << "Arch:" << QSysInfo::currentCpuArchitecture();
82 qCInfo(KSTARS) << "Kernel Type:" << QSysInfo::kernelType();
83 qCInfo(KSTARS) << "Kernel Version:" << QSysInfo::kernelVersion();
84 qCInfo(KSTARS) << "Qt Version:" << QT_VERSION_STR;
85
86 new KstarsAdaptor(
87 this); // NOTE the weird case convention, which cannot be changed as the file is generated by the moc.
88
89 #ifdef Q_OS_OSX
90
91 QString vlcPlugins = QDir(QCoreApplication::applicationDirPath() + "/../PlugIns/vlc").absolutePath();
92 qputenv("VLC_PLUGIN_PATH", vlcPlugins.toLatin1());
93 QString phonon_backend_path = QDir(QCoreApplication::applicationDirPath() +
94 "/../PlugIns/phonon4qt5_backend/phonon_vlc.so").absolutePath();
95 qputenv("PHONON_BACKEND", phonon_backend_path.toLatin1());
96
97 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
98 QString path = env.value("PATH", "");
99 env.insert("PATH", "/usr/bin:/usr/local/bin:\"" + QCoreApplication::applicationDirPath() + "\":" + path);
100
101 QProcess dbusCheck;
102 dbusCheck.setProcessEnvironment(env);
103
104 QString pluginsDir = QDir(QCoreApplication::applicationDirPath() + "/../PlugIns").absolutePath();
105 QString dbusPlist = pluginsDir + "/dbus/org.freedesktop.dbus-kstars.plist";
106 QFile file(dbusPlist);
107 if (file.open(QIODevice::ReadOnly))
108 {
109 QTextStream in(&file);
110 QString pListText = in.readAll();
111 file.close();
112 int programArgsLeft = pListText.indexOf("<key>ProgramArguments</key>");
113 int programArgsRight = pListText.indexOf("</array>", programArgsLeft) + 8 - programArgsLeft;
114 QString currentProgramArgs = pListText.mid(programArgsLeft, programArgsRight);
115 QString newProgramArguments = ""
116 "<key>ProgramArguments</key>\n"
117 " <array>\n"
118 " <string>" +
119 QCoreApplication::applicationDirPath() +
120 "/dbus-daemon</string>\n"
121 " <string>--nofork</string>\n"
122 " <string>--config-file=" +
123 pluginsDir +
124 "/dbus/kstars.conf</string>\n"
125 " </array>";
126 pListText.replace(currentProgramArgs, newProgramArguments);
127 if (file.open(QIODevice::WriteOnly))
128 {
129 QTextStream stream(&file);
130 stream << pListText;
131 file.close();
132
133 dbusCheck.start("chmod 775 " + dbusPlist);
134 dbusCheck.waitForFinished();
135 dbusCheck.start("launchctl load -w \"" + dbusPlist + "\"");
136 dbusCheck.waitForFinished();
137 qDebug("Starting DBus");
138 }
139 else
140 {
141 qDebug("DBus File Write Error");
142 }
143 }
144 else
145 {
146 qDebug("DBus File Read Error");
147 }
148 #endif
149
150 QDBusConnection::sessionBus().registerObject("/KStars", this);
151 QDBusConnection::sessionBus().registerService("org.kde.kstars");
152
153 #ifdef HAVE_CFITSIO
154 m_GenericFITSViewer.clear();
155 #endif
156
157 // Set pinstance to yourself
158 pinstance = this;
159
160 connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(slotAboutToQuit()));
161
162 //Initialize QActionGroups
163 projectionGroup = new QActionGroup(this);
164 cschemeGroup = new QActionGroup(this);
165 hipsGroup = new QActionGroup(this);
166 telescopeGroup = new QActionGroup(this);
167 telescopeGroup->setExclusive(false);
168 domeGroup = new QActionGroup(this);
169 domeGroup->setExclusive(false);
170
171
172 m_KStarsData = KStarsData::Create();
173 Q_ASSERT(m_KStarsData);
174 //Set Geographic Location from Options
175 m_KStarsData->setLocationFromOptions();
176
177 //Initialize Time and Date
178 bool datetimeSet = false;
179 if (StartDateString.isEmpty() == false)
180 {
181 KStarsDateTime startDate = KStarsDateTime::fromString(StartDateString);
182 if (startDate.isValid())
183 data()->changeDateTime(data()->geo()->LTtoUT(startDate));
184 else
185 data()->changeDateTime(KStarsDateTime::currentDateTimeUtc());
186
187 datetimeSet = true;
188 }
189 // JM 2016-11-15: Not need to set it again as it was initialized in the ctor of SimClock
190 /*
191 else
192 data()->changeDateTime( KStarsDateTime::currentDateTimeUtc() );
193 */
194
195 // Initialize clock. If --paused is not in the command line, look in options
196 if (clockrun)
197 StartClockRunning = Options::runClock();
198 // If we are starting paused, we need to change datetime in data
199 if (StartClockRunning == false)
200 {
201 qCInfo(KSTARS) << "KStars is started in paused state.";
202 if (datetimeSet == false)
203 data()->changeDateTime(KStarsDateTime::currentDateTimeUtc());
204 }
205
206 // Setup splash screen
207 KStarsSplash *splash = nullptr;
208 if (doSplash)
209 {
210 splash = new KStarsSplash(nullptr);
211 connect(m_KStarsData, SIGNAL(progressText(QString)), splash, SLOT(setMessage(QString)));
212 splash->show();
213 }
214 else
215 {
216 connect(m_KStarsData, SIGNAL(progressText(QString)), m_KStarsData, SLOT(slotConsoleMessage(QString)));
217 }
218
219 /*
220 //set up Dark color scheme for application windows
221 DarkPalette = QPalette(QColor("black"), QColor("black"));
222 DarkPalette.setColor(QPalette::Inactive, QPalette::WindowText, QColor("red"));
223 DarkPalette.setColor(QPalette::Normal, QPalette::WindowText, QColor("red"));
224 DarkPalette.setColor(QPalette::Normal, QPalette::Base, QColor("black"));
225 DarkPalette.setColor(QPalette::Normal, QPalette::Text, QColor(238, 0, 0));
226 DarkPalette.setColor(QPalette::Normal, QPalette::Highlight, QColor(238, 0, 0));
227 DarkPalette.setColor(QPalette::Normal, QPalette::HighlightedText, QColor("black"));
228 DarkPalette.setColor(QPalette::Inactive, QPalette::Text, QColor(238, 0, 0));
229 DarkPalette.setColor(QPalette::Inactive, QPalette::Base, QColor(30, 10, 10));
230 //store original color scheme
231 OriginalPalette = QApplication::palette();
232 */
233
234 //Initialize data. When initialization is complete, it will run dataInitFinished()
235 if (!m_KStarsData->initialize())
236 return;
237 delete splash;
238 datainitFinished();
239
240 #if (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1 && !defined(__UCLIBC__))
241 qDebug() << "glibc >= 2.1 detected. Using GNU extension sincos()";
242 #else
243 qDebug() << "Did not find glibc >= 2.1. Will use ANSI-compliant sin()/cos() functions.";
244 #endif
245 }
246
createInstance(bool doSplash,bool clockrun,const QString & startdate)247 KStars *KStars::createInstance(bool doSplash, bool clockrun, const QString &startdate)
248 {
249 delete pinstance;
250 // pinstance is set directly in constructor.
251 new KStars(doSplash, clockrun, startdate);
252 Q_ASSERT(pinstance && "pinstance must be non NULL");
253 return pinstance;
254 }
255
~KStars()256 KStars::~KStars()
257 {
258 releaseResources();
259 Q_ASSERT(pinstance);
260 pinstance = nullptr;
261 #ifdef PROFILE_COORDINATE_CONVERSION
262 qDebug() << "Spent " << SkyPoint::cpuTime_EqToHz << " seconds in " << SkyPoint::eqToHzCalls
263 << " calls to SkyPoint::EquatorialToHorizontal, for an average of "
264 << 1000. * (SkyPoint::cpuTime_EqToHz / SkyPoint::eqToHzCalls) << " ms per call";
265 #endif
266
267 #ifdef COUNT_DMS_SINCOS_CALLS
268 qDebug() << "Constructed " << dms::dms_constructor_calls << " dms objects, of which " << dms::dms_with_sincos_called
269 << " had trigonometric functions called on them = "
270 << (float(dms::dms_with_sincos_called) / float(dms::dms_constructor_calls)) * 100. << "%";
271 qDebug() << "Of the " << dms::trig_function_calls << " calls to sin/cos/sincos on dms objects, "
272 << dms::redundant_trig_function_calls << " were redundant = "
273 << ((float(dms::redundant_trig_function_calls) / float(dms::trig_function_calls)) * 100.) << "%";
274 qDebug() << "We had " << CachingDms::cachingdms_bad_uses << " bad uses of CachingDms in all, compared to "
275 << CachingDms::cachingdms_constructor_calls << " constructed CachingDms objects = "
276 << (float(CachingDms::cachingdms_bad_uses) / float(CachingDms::cachingdms_constructor_calls)) * 100.
277 << "% bad uses";
278 #endif
279
280 /* BUG 366596: Some KDE applications processes remain as background (zombie) processes after closing
281 * No solution to this bug so far using Qt 5.8 as of 2016-11-24
282 * Therefore, the only way to solve this on Windows is to explicitly kill kstars.exe
283 * Hopefully we do not need this hack once the above bug is resolved.
284 */
285 #ifdef Q_OS_WIN
286 QProcess::execute("taskkill /im kstars.exe /f");
287 #endif
288 }
289
releaseResources()290 void KStars::releaseResources()
291 {
292 delete m_KStarsData;
293 m_KStarsData = nullptr;
294 delete StarBlockFactory::Instance();
295 TextureManager::Release();
296 SkyQPainter::releaseImageCache();
297 FOVManager::releaseCache();
298
299 #ifdef HAVE_INDI
300 GUIManager::release();
301 Ekos::Manager::release();
302 #endif
303
304 #ifdef HAVE_CFITSIO
305 //if (Options::independentWindowFITS())
306 qDeleteAll(m_FITSViewers);
307 #endif
308
309 QSqlDatabase::removeDatabase("userdb");
310 QSqlDatabase::removeDatabase("skydb");
311 }
312
clearCachedFindDialog()313 void KStars::clearCachedFindDialog()
314 {
315 #if 0
316 if (m_FindDialog) // dialog is cached
317 {
318 /** Delete findDialog only if it is not opened */
319 if (m_FindDialog->isHidden())
320 {
321 delete m_FindDialog;
322 m_FindDialog = nullptr;
323 DialogIsObsolete = false;
324 }
325 else
326 DialogIsObsolete = true; // dialog was opened so it could not deleted
327 }
328 #endif
329 }
330
applyConfig(bool doApplyFocus)331 void KStars::applyConfig(bool doApplyFocus)
332 {
333 if (Options::isTracking())
334 {
335 actionCollection()->action("track_object")->setText(i18n("Stop &Tracking"));
336 actionCollection()
337 ->action("track_object")
338 ->setIcon(QIcon::fromTheme("document-encrypt"));
339 }
340
341 actionCollection()
342 ->action("coordsys")
343 ->setText(Options::useAltAz() ? i18n("Switch to Star Globe View (Equatorial &Coordinates)") :
344 i18n("Switch to Horizonal View (Horizontal &Coordinates)"));
345
346 actionCollection()->action("show_time_box")->setChecked(Options::showTimeBox());
347 actionCollection()->action("show_location_box")->setChecked(Options::showGeoBox());
348 actionCollection()->action("show_focus_box")->setChecked(Options::showFocusBox());
349 actionCollection()->action("show_statusBar")->setChecked(Options::showStatusBar());
350 actionCollection()->action("show_sbAzAlt")->setChecked(Options::showAltAzField());
351 actionCollection()->action("show_sbRADec")->setChecked(Options::showRADecField());
352 actionCollection()->action("show_sbJ2000RADec")->setChecked(Options::showJ2000RADecField());
353 actionCollection()->action("show_stars")->setChecked(Options::showStars());
354 actionCollection()->action("show_deepsky")->setChecked(Options::showDeepSky());
355 actionCollection()->action("show_planets")->setChecked(Options::showSolarSystem());
356 actionCollection()->action("show_clines")->setChecked(Options::showCLines());
357 actionCollection()->action("show_constellationart")->setChecked(Options::showConstellationArt());
358 actionCollection()->action("show_cnames")->setChecked(Options::showCNames());
359 actionCollection()->action("show_cbounds")->setChecked(Options::showCBounds());
360 actionCollection()->action("show_mw")->setChecked(Options::showMilkyWay());
361 actionCollection()->action("show_equatorial_grid")->setChecked(Options::showEquatorialGrid());
362 actionCollection()->action("show_horizontal_grid")->setChecked(Options::showHorizontalGrid());
363 actionCollection()->action("show_horizon")->setChecked(Options::showGround());
364 actionCollection()->action("show_flags")->setChecked(Options::showFlags());
365 actionCollection()->action("show_supernovae")->setChecked(Options::showSupernovae());
366 actionCollection()->action("show_satellites")->setChecked(Options::showSatellites());
367 statusBar()->setVisible(Options::showStatusBar());
368
369 //color scheme
370 m_KStarsData->colorScheme()->loadFromConfig();
371 //QApplication::setPalette(Options::darkAppColors() ? DarkPalette : OriginalPalette);
372 /**
373 //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.
374 //It might be a good idea to use stylesheets in the future instead of palettes but this will work for now for OS X.
375 //This is also in KStarsDbus.cpp. If you change it, change it in BOTH places.
376 @code
377 #ifdef Q_OS_OSX
378 if (Options::darkAppColors())
379 qApp->setStyleSheet(
380 "QWidget { background-color: black; color:red; "
381 "selection-background-color:rgb(30,30,30);selection-color:white}"
382 "QToolBar { border:none }"
383 "QTabBar::tab:selected { background-color:rgb(50,50,50) }"
384 "QTabBar::tab:!selected { background-color:rgb(30,30,30) }"
385 "QPushButton { background-color:rgb(50,50,50);border-width:1px; border-style:solid;border-color:black}"
386 "QPushButton::disabled { background-color:rgb(10,10,10);border-width:1px; "
387 "border-style:solid;border-color:black }"
388 "QToolButton:Checked { background-color:rgb(30,30,30); border:none }"
389 "QComboBox { background-color:rgb(30,30,30); }"
390 "QComboBox::disabled { background-color:rgb(10,10,10) }"
391 "QScrollBar::handle { background: rgb(30,30,30) }"
392 "QSpinBox { border-width: 1px; border-style:solid; border-color:rgb(30,30,30) }"
393 "QDoubleSpinBox { border-width:1px; border-style:solid; border-color:rgb(30,30,30) }"
394 "QLineEdit { border-width: 1px; border-style: solid; border-color:rgb(30,30,30) }"
395 "QCheckBox::indicator:unchecked { background-color:rgb(30,30,30);border-width:1px; "
396 "border-style:solid;border-color:black }"
397 "QCheckBox::indicator:checked { background-color:red;border-width:1px; "
398 "border-style:solid;border-color:black }"
399 "QRadioButton::indicator:unchecked { background-color:rgb(30,30,30) }"
400 "QRadioButton::indicator:checked { background-color:red }"
401 "QRoundProgressBar { alternate-background-color:black }"
402 "QDateTimeEdit {background-color:rgb(30,30,30); border-width: 1px; border-style:solid; "
403 "border-color:rgb(30,30,30) }"
404 "QHeaderView { color:red;background-color:black }"
405 "QHeaderView::Section { background-color:rgb(30,30,30) }"
406 "QTableCornerButton::section{ background-color:rgb(30,30,30) }"
407 "");
408 else
409 qApp->setStyleSheet("");
410 #endif
411 @endcode
412 **/
413
414 //Set toolbar options from config file
415 toolBar("kstarsToolBar")->applySettings(KSharedConfig::openConfig()->group("MainToolBar"));
416 toolBar("viewToolBar")->applySettings(KSharedConfig::openConfig()->group("ViewToolBar"));
417
418 //Geographic location
419 data()->setLocationFromOptions();
420
421 //Focus
422 if (doApplyFocus)
423 {
424 SkyObject *fo = data()->objectNamed(Options::focusObject());
425 if (fo && fo != map()->focusObject())
426 {
427 map()->setClickedObject(fo);
428 map()->setClickedPoint(fo);
429 map()->slotCenter();
430 }
431
432 if (!fo)
433 {
434 SkyPoint fp(Options::focusRA(), Options::focusDec());
435 if (fp.ra().Degrees() != map()->focus()->ra().Degrees() ||
436 fp.dec().Degrees() != map()->focus()->dec().Degrees())
437 {
438 map()->setClickedPoint(&fp);
439 map()->slotCenter();
440 }
441 }
442 }
443 }
444
showImgExportDialog()445 void KStars::showImgExportDialog()
446 {
447 if (m_ExportImageDialog)
448 m_ExportImageDialog->show();
449 }
450
syncFOVActions()451 void KStars::syncFOVActions()
452 {
453 foreach (QAction *action, fovActionMenu->menu()->actions())
454 {
455 if (action->text().isEmpty())
456 {
457 continue;
458 }
459
460 if (Options::fOVNames().contains(action->text().remove(0, 1)))
461 {
462 action->setChecked(true);
463 }
464 else
465 {
466 action->setChecked(false);
467 }
468 }
469 }
470
hideAllFovExceptFirst()471 void KStars::hideAllFovExceptFirst()
472 {
473 // When there is only one visible FOV symbol, we don't need to do anything
474 // Also, don't do anything if there are no available FOV symbols.
475 if (data()->visibleFOVs.size() == 1 || data()->availFOVs.isEmpty())
476 {
477 return;
478 }
479 else
480 {
481 // If there are no visible FOVs, select first available
482 if (data()->visibleFOVs.isEmpty())
483 {
484 Q_ASSERT(!data()->availFOVs.isEmpty());
485 Options::setFOVNames(QStringList(data()->availFOVs.first()->name()));
486 }
487 else
488 {
489 Options::setFOVNames(QStringList(data()->visibleFOVs.first()->name()));
490 }
491
492 // Sync FOV and update skymap
493 data()->syncFOV();
494 syncFOVActions();
495 map()->update(); // SkyMap::forceUpdate() is not required, as FOVs are drawn as overlays
496 }
497 }
498
selectNextFov()499 void KStars::selectNextFov()
500 {
501 if (data()->getVisibleFOVs().isEmpty())
502 return;
503
504 Q_ASSERT(!data()
505 ->getAvailableFOVs()
506 .isEmpty()); // The available FOVs had better not be empty if the visible ones are not.
507
508 FOV *currentFov = data()->getVisibleFOVs().first();
509 int currentIdx = data()->availFOVs.indexOf(currentFov);
510
511 // If current FOV is not the available FOV list or there is only 1 FOV available, then return
512 if (currentIdx == -1 || data()->availFOVs.size() < 2)
513 {
514 return;
515 }
516
517 QStringList nextFovName;
518 if (currentIdx == data()->availFOVs.size() - 1)
519 {
520 nextFovName << data()->availFOVs.first()->name();
521 }
522 else
523 {
524 nextFovName << data()->availFOVs.at(currentIdx + 1)->name();
525 }
526
527 Options::setFOVNames(nextFovName);
528 data()->syncFOV();
529 syncFOVActions();
530 map()->update();
531 }
532
selectPreviousFov()533 void KStars::selectPreviousFov()
534 {
535 if (data()->getVisibleFOVs().isEmpty())
536 return;
537
538 Q_ASSERT(!data()
539 ->getAvailableFOVs()
540 .isEmpty()); // The available FOVs had better not be empty if the visible ones are not.
541
542 FOV *currentFov = data()->getVisibleFOVs().first();
543 int currentIdx = data()->availFOVs.indexOf(currentFov);
544
545 // If current FOV is not the available FOV list or there is only 1 FOV available, then return
546 if (currentIdx == -1 || data()->availFOVs.size() < 2)
547 {
548 return;
549 }
550
551 QStringList prevFovName;
552 if (currentIdx == 0)
553 {
554 prevFovName << data()->availFOVs.last()->name();
555 }
556 else
557 {
558 prevFovName << data()->availFOVs.at(currentIdx - 1)->name();
559 }
560
561 Options::setFOVNames(prevFovName);
562 data()->syncFOV();
563 syncFOVActions();
564 map()->update();
565 }
566
567 //FIXME Port to QML2
568 //#if 0
showWISettingsUI()569 void KStars::showWISettingsUI()
570 {
571 slotWISettings();
572 }
573 //#endif
574
updateTime(const bool automaticDSTchange)575 void KStars::updateTime(const bool automaticDSTchange)
576 {
577 // Due to frequently use of this function save data and map pointers for speedup.
578 // Save options and geo() to a pointer would not speedup because most of time options
579 // and geo will accessed only one time.
580 KStarsData *Data = data();
581 // dms oldLST( Data->lst()->Degrees() );
582
583 Data->updateTime(Data->geo(), automaticDSTchange);
584
585 //We do this outside of kstarsdata just to get the coordinates
586 //displayed in the infobox to update every second.
587 // if ( !Options::isTracking() && LST()->Degrees() > oldLST.Degrees() ) {
588 // int nSec = int( 3600.*( LST()->Hours() - oldLST.Hours() ) );
589 // Map->focus()->setRA( Map->focus()->ra().Hours() + double( nSec )/3600. );
590 // if ( Options::useAltAz() ) Map->focus()->EquatorialToHorizontal( LST(), geo()->lat() );
591 // Map->showFocusCoords();
592 // }
593
594 //If time is accelerated beyond slewTimescale, then the clock's timer is stopped,
595 //so that it can be ticked manually after each update, in order to make each time
596 //step exactly equal to the timeScale setting.
597 //Wrap the call to manualTick() in a singleshot timer so that it doesn't get called until
598 //the skymap has been completely updated.
599 if (Data->clock()->isManualMode() && Data->clock()->isActive())
600 {
601 // Jasem 2017-11-13: Time for each update varies.
602 // Ideally we want to advance the simulation clock by
603 // the current clock scale (e.g. 1 hour) every 1 second
604 // of real time. However, the sky map update, depending on calculations and
605 // drawing of objects, takes variable time to complete.
606 //QTimer::singleShot(0, Data->clock(), SLOT(manualTick()));
607 QTimer::singleShot(1000, Data->clock(), SLOT(manualTick()));
608 }
609 }
610
611 #ifdef HAVE_CFITSIO
createFITSViewer()612 const QPointer<FITSViewer> &KStars::createFITSViewer()
613 {
614 if (Options::singleWindowCapturedFITS())
615 return KStars::Instance()->genericFITSViewer();
616 else
617 {
618 QPointer<FITSViewer> newFITSViewer = new FITSViewer(Options::independentWindowFITS() ? nullptr : KStars::Instance());
619
620 connect(newFITSViewer.data(), &FITSViewer::destroyed, [ = ]()
621 {
622 m_FITSViewers.removeOne(newFITSViewer);
623 });
624
625 m_FITSViewers.append(newFITSViewer);
626 return m_FITSViewers.constLast();
627 }
628 }
629
genericFITSViewer()630 const QPointer<FITSViewer> &KStars::genericFITSViewer()
631 {
632 if (m_GenericFITSViewer.isNull())
633 {
634 m_GenericFITSViewer = new FITSViewer(Options::independentWindowFITS() ? nullptr : this);
635 //m_GenericFITSViewer->setAttribute(Qt::WA_DeleteOnClose);
636 m_FITSViewers.append(m_GenericFITSViewer);
637 }
638
639 return m_GenericFITSViewer;
640 }
641
642 //void KStars::addFITSViewer(const QSharedPointer<FITSViewer> &fv)
643 //{
644 // m_FITSViewers.append(fv);
645 // connect(fv.data(), &FITSViewer::terminated, [ = ]()
646 // {
647 // m_FITSViewers.removeOne(fv);
648 // });
649 //}
650
clearAllViewers()651 void KStars::clearAllViewers()
652 {
653 for (auto &fv : m_FITSViewers)
654 fv->close();
655
656 m_FITSViewers.clear();
657 }
658 #endif
659
closeEvent(QCloseEvent * event)660 void KStars::closeEvent(QCloseEvent *event)
661 {
662 KStars::Closing = true;
663 QWidget::closeEvent(event);
664 }
665