1 /*
2     SPDX-FileCopyrightText: 2016 Artem Fedoskin <afedoskin3@gmail.com>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "kstarslite.h"
8 
9 #include "klocalizedcontext.h"
10 #include "kspaths.h"
11 #include "ksplanetbase.h"
12 #include "kstarsdata.h"
13 #include "ksutils.h"
14 #include "Options.h"
15 #include "skymaplite.h"
16 #include "version.h"
17 #include "indi/clientmanagerlite.h"
18 #include "indi/inditelescopelite.h"
19 #include "kstarslite/imageprovider.h"
20 #include "kstarslite/dialogs/finddialoglite.h"
21 #include "kstarslite/dialogs/detaildialoglite.h"
22 #include "kstarslite/dialogs/locationdialoglite.h"
23 
24 #include <QGuiApplication>
25 #include <QQmlContext>
26 #include <QQuickStyle>
27 #include <QScreen>
28 #include <QSurfaceFormat>
29 
30 KStarsLite *KStarsLite::pinstance = nullptr;
31 
KStarsLite(bool doSplash,bool startClock,const QString & startDateString)32 KStarsLite::KStarsLite(bool doSplash, bool startClock, const QString &startDateString)
33 {
34     QCoreApplication::setOrganizationName("KStarsLite");
35     // Initialize logging settings
36     /*if (Options::disableLogging())
37         KSUtils::Logging::Disable();
38     else if (Options::logToFile() && Options::verboseLogFile().isEmpty() == false)
39         KSUtils::Logging::UseFile(Options::verboseLogFile());
40     else
41         KSUtils::Logging::UseDefault();*/
42 
43     // Set pinstance to yourself
44     // Unlike KStars class we set pinstance at the beginning because SkyMapLite needs access to ClientManagerLite
45     pinstance = this;
46 
47     if (doSplash)
48         showSplash();
49 
50     m_KStarsData = KStarsData::Create();
51     Q_ASSERT(m_KStarsData);
52 
53     //INDI Android Client
54     m_clientManager = new ClientManagerLite(*m_Engine.rootContext());
55     m_Engine.rootContext()->setContextProperty("ClientManagerLite", m_clientManager);
56 
57     //Make instance of KStarsLite and KStarsData available to QML
58     m_Engine.rootContext()->setContextProperty("KStarsLite", this);
59     m_Engine.rootContext()->setContextProperty("KStarsData", m_KStarsData);
60     m_Engine.rootContext()->setContextProperty("Options", Options::self());
61     m_Engine.rootContext()->setContextProperty("SimClock", m_KStarsData->clock());
62     m_Engine.rootContext()->setContextObject(new KLocalizedContext());
63     qmlRegisterUncreatableType<Projector>("KStarsLiteEnums", 1, 0, "Projection", "Provides Projection enum");
64     qmlRegisterUncreatableType<KStarsLite>("KStarsLiteEnums", 1, 0, "ObjectsToToggle",
65                                            "Enum for togglint the visibility of sky objects");
66 
67     //Dialogs
68     m_findDialogLite     = new FindDialogLite;
69     m_detailDialogLite   = new DetailDialogLite;
70     m_locationDialogLite = new LocationDialogLite;
71 
72     m_Engine.rootContext()->setContextProperty("FindDialogLite", m_findDialogLite);
73     m_Engine.rootContext()->setContextProperty("DetailDialogLite", m_detailDialogLite);
74     m_Engine.rootContext()->setContextProperty("LocationDialogLite", m_locationDialogLite);
75 
76     //Set Geographic Location from Options
77     m_KStarsData->setLocationFromOptions();
78 
79     //Set style - default is Material
80     QQuickStyle::setStyle("Material");
81 #ifdef Q_OS_ANDROID
82     QString main = KSPaths::locate(QStandardPaths::AppDataLocation, "kstarslite/qml/main.qml");
83 #else
84     QString main = QString(QML_IMPORT) + QString("/kstarslite/qml/main.qml");
85 #endif
86 
87     /*SkyMapLite has to be loaded before KStarsData is initialized because SkyComponents derived classes
88     have to add SkyItems to the SkyMapLite*/
89     m_SkyMapLite = SkyMapLite::createInstance();
90     m_Engine.rootContext()->setContextProperty("SkyMapLite", m_SkyMapLite);
91 
92     m_Engine.load(QUrl(main));
93     Q_ASSERT_X(m_Engine.rootObjects().size(), "loading root object of main.qml",
94                "QML file was not loaded. Probably syntax error or failed module import.");
95 
96     m_RootObject = m_Engine.rootObjects()[0];
97     m_clientManager->setIndiControlPage(*m_RootObject->findChild<QObject*>("indiControlPanel"));
98     connect(m_clientManager, &ClientManagerLite::telescopeConnected,
99             [=](TelescopeLite *telescope) {
100                 m_RootObject->findChild<QObject*>("bottomMenu")->setProperty("telescope", true);
101                 connect(telescope, &TelescopeLite::slewRateUpdate,
102                         [=](int index, int count) {
103                             m_RootObject->findChild<QObject*>("bottomMenu")->setProperty("slewCount", count);
104                             m_RootObject->findChild<QObject*>("bottomMenu")->setProperty("sliderValue", index);
105                         } );
106             } );
107     connect(m_clientManager, &ClientManagerLite::telescopeDisconnected,
108             [=]() {
109                 m_RootObject->findChild<QObject*>("bottomMenu")->setProperty("telescope", false);
110             } );
111 
112     // Set the About information
113     QObject *aboutDialog = m_RootObject->findChild<QObject*>("aboutDialog");
114 
115     aboutDialog->setProperty("versionText", i18n("Version: %1", QStringLiteral(KSTARS_VERSION)));
116     aboutDialog->setProperty("buildText", i18n("Build: %1", QStringLiteral(KSTARS_BUILD_TS)));
117     aboutDialog->setProperty("teamText", QString("2001-" + QString::number(QDate::currentDate().year()) + i18n("(c), The KStars Team")));
118     aboutDialog->setProperty("licenseText", i18n("License: GPLv2"));
119 
120     QQuickItem *skyMapLiteWrapper = m_RootObject->findChild<QQuickItem *>("skyMapLiteWrapper");
121 
122     m_SkyMapLite->initialize(skyMapLiteWrapper);
123     m_detailDialogLite->initialize();
124 
125     m_imgProvider.reset(new ImageProvider);
126     m_Engine.addImageProvider(QLatin1String("images"), m_imgProvider.get());
127 
128 #ifdef Q_OS_ANDROID
129     QQuickWindow *mainWindow = static_cast<QQuickWindow *>(m_Engine.rootObjects()[0]);
130     QSurfaceFormat format = mainWindow->format();
131 
132     format.setSamples(4);
133     format.setSwapBehavior(QSurfaceFormat::TripleBuffer);
134     mainWindow->setFormat(format);
135 #endif
136 
137     connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)), SLOT(handleStateChange(Qt::ApplicationState)));
138 
139     //Initialize Time and Date
140     if (startDateString.isEmpty() == false)
141     {
142         KStarsDateTime startDate = KStarsDateTime::fromString(startDateString);
143         if (startDate.isValid())
144             data()->changeDateTime(data()->geo()->LTtoUT(startDate));
145         else
146             data()->changeDateTime(KStarsDateTime::currentDateTimeUtc());
147     }
148     else
149         data()->changeDateTime(KStarsDateTime::currentDateTimeUtc());
150 
151     // Initialize clock. If --paused is not in the command line, look in options
152     if (startClock)
153         StartClockRunning = Options::runClock();
154 
155     // Setup splash screen
156     connect(m_KStarsData, SIGNAL(progressText(QString)), m_KStarsData, SLOT(slotConsoleMessage(QString)));
157 
158     //set up Dark color scheme for application windows
159     DarkPalette = QPalette(QColor("darkred"), QColor("darkred"));
160     DarkPalette.setColor(QPalette::Normal, QPalette::Base, QColor("black"));
161     DarkPalette.setColor(QPalette::Normal, QPalette::Text, QColor(238, 0, 0));
162     DarkPalette.setColor(QPalette::Normal, QPalette::Highlight, QColor(238, 0, 0));
163     DarkPalette.setColor(QPalette::Normal, QPalette::HighlightedText, QColor("black"));
164     DarkPalette.setColor(QPalette::Inactive, QPalette::Text, QColor(238, 0, 0));
165     DarkPalette.setColor(QPalette::Inactive, QPalette::Base, QColor(30, 10, 10));
166     //store original color scheme
167     OriginalPalette = QGuiApplication::palette();
168     if (!m_KStarsData->initialize())
169         return;
170     datainitFinished();
171 }
172 
~KStarsLite()173 KStarsLite::~KStarsLite()
174 {
175 }
176 
getMainWindow()177 QQuickWindow *KStarsLite::getMainWindow()
178 {
179     if (!m_Engine.rootContext() || m_Engine.rootObjects().isEmpty())
180         return nullptr;
181 
182     return static_cast<QQuickWindow *>(m_Engine.rootObjects()[0]);
183 }
184 
slotTrack()185 void KStarsLite::slotTrack()
186 {
187     if (Options::isTracking())
188     {
189         Options::setIsTracking(false);
190         /*actionCollection()->action("track_object")->setText( i18n( "Engage &Tracking" ) );
191         actionCollection()->action("track_object")->setIcon( QIcon::fromTheme("document-decrypt") );
192 
193         KSPlanetBase* planet = dynamic_cast<KSPlanetBase*>( map()->focusObject() );
194         if( planet && data()->temporaryTrail ) {
195             planet->clearTrail();
196             data()->temporaryTrail = false;
197         }*/ // No trail support yet
198 
199         map()->setClickedObject(nullptr);
200         map()->setFocusObject(nullptr); //no longer tracking focusObject
201         map()->setFocusPoint(nullptr);
202     }
203     else
204     {
205         map()->setClickedPoint(map()->focus());
206         map()->setClickedObject(nullptr);
207         map()->setFocusObject(nullptr); //no longer tracking focusObject
208         map()->setFocusPoint(map()->clickedPoint());
209         Options::setIsTracking(true);
210         /*actionCollection()->action("track_object")->setText( i18n( "Stop &Tracking" ) );
211         actionCollection()->action("track_object")->setIcon( QIcon::fromTheme("document-encrypt") );*/
212     }
213 
214     map()->forceUpdate();
215 }
216 
createInstance(bool doSplash,bool clockrunning,const QString & startDateString)217 KStarsLite *KStarsLite::createInstance(bool doSplash, bool clockrunning, const QString &startDateString)
218 {
219     delete pinstance;
220     // pinstance is set directly in constructor.
221     new KStarsLite(doSplash, clockrunning, startDateString);
222     Q_ASSERT(pinstance && "pinstance must be non NULL");
223     return pinstance;
224 }
225 
fullUpdate()226 void KStarsLite::fullUpdate()
227 {
228     m_KStarsData->setFullTimeUpdate();
229     updateTime();
230 
231     m_SkyMapLite->forceUpdate();
232 }
233 
updateTime(const bool automaticDSTchange)234 void KStarsLite::updateTime(const bool automaticDSTchange)
235 {
236     // Due to frequently use of this function save data and map pointers for speedup.
237     // Save options and geo() to a pointer would not speedup because most of time options
238     // and geo will accessed only one time.
239     KStarsData *Data = data();
240     // dms oldLST( Data->lst()->Degrees() );
241 
242     Data->updateTime(Data->geo(), automaticDSTchange);
243 
244     //We do this outside of kstarsdata just to get the coordinates
245     //displayed in the infobox to update every second.
246     //	if ( !Options::isTracking() && LST()->Degrees() > oldLST.Degrees() ) {
247     //		int nSec = int( 3600.*( LST()->Hours() - oldLST.Hours() ) );
248     //		Map->focus()->setRA( Map->focus()->ra().Hours() + double( nSec )/3600. );
249     //		if ( Options::useAltAz() ) Map->focus()->EquatorialToHorizontal( LST(), geo()->lat() );
250     //		Map->showFocusCoords();
251     //	}
252 
253     //If time is accelerated beyond slewTimescale, then the clock's timer is stopped,
254     //so that it can be ticked manually after each update, in order to make each time
255     //step exactly equal to the timeScale setting.
256     //Wrap the call to manualTick() in a singleshot timer so that it doesn't get called until
257     //the skymap has been completely updated.
258     if (Data->clock()->isManualMode() && Data->clock()->isActive())
259     {
260         QTimer::singleShot(0, Data->clock(), SLOT(manualTick()));
261     }
262 }
263 
writeConfig()264 bool KStarsLite::writeConfig()
265 {
266     // It seems two config files are saved to android. Must call them both to save all options
267     // First one save color information, 2nd one rest of config. Bug?
268     // /data/user/0/org.kde.kstars/files/settings/kstarsrc is used by KSharedConfig::openConfig()
269     KSharedConfig::openConfig()->sync();
270     // /data/data/org.kde.kstars/files/settings/kstarsrc is used by Options::self()
271     return Options::self()->save();
272 
273     //Store current simulation time
274     //Refer to // FIXME: Used in kstarsdcop.cpp only in kstarsdata.cpp
275     //data()->StoredDate = data()->lt();
276 }
277 
handleStateChange(Qt::ApplicationState state)278 void KStarsLite::handleStateChange(Qt::ApplicationState state)
279 {
280     if (state == Qt::ApplicationSuspended)
281     {
282         // Delete skymaplite. This required to run destructors and save
283         // current state in the option.
284         //delete m_SkyMapLite;
285 
286         //Store Window geometry in Options object
287         //Options::setWindowWidth( m_RootObject->width() );
288         //Options::setWindowHeight( m_RootObject->height() );
289 
290         //explicitly save the colorscheme data to the config file
291         //data()->colorScheme()->saveToConfig();
292         //synch the config file with the Config object
293         writeConfig();
294     }
295 }
296 
loadColorScheme(const QString & name)297 void KStarsLite::loadColorScheme(const QString &name)
298 {
299     bool ok          = data()->colorScheme()->load(name);
300     QString filename = data()->colorScheme()->fileName();
301 
302     if (ok)
303     {
304         //set the application colors for the Night Vision scheme
305         if (filename == "night.colors")
306         {
307             OriginalPalette = QGuiApplication::palette();
308             QGuiApplication::setPalette(DarkPalette);
309         }
310         else
311             QGuiApplication::setPalette(OriginalPalette);
312 
313         Options::setColorSchemeFile(name);
314 
315         data()->colorScheme()->saveToConfig();
316 
317         //writeConfig();
318 
319         //Reinitialize stars textures
320         map()->initStarImages();
321 
322         map()->forceUpdate();
323     }
324 }
325 
slotSetTime(QDateTime time)326 void KStarsLite::slotSetTime(QDateTime time)
327 {
328     KStarsDateTime selectedDateTime(time);
329     data()->changeDateTime(data()->geo()->LTtoUT(selectedDateTime));
330 
331     if (Options::useAltAz())
332     {
333         if (map()->focusObject())
334         {
335             map()->focusObject()->EquatorialToHorizontal(data()->lst(), data()->geo()->lat());
336             map()->setFocus(map()->focusObject());
337         }
338         else
339             map()->focus()->HorizontalToEquatorial(data()->lst(), data()->geo()->lat());
340     }
341 
342     map()->forceUpdateNow();
343 
344     //If focusObject has a Planet Trail, clear it and start anew.
345     /*KSPlanetBase* planet = dynamic_cast<KSPlanetBase*>( map()->focusObject() );
346         if( planet && planet->hasTrail() ) {
347             planet->clearTrail();
348             planet->addToTrail();
349         }*/
350 }
351 
slotToggleTimer()352 void KStarsLite::slotToggleTimer()
353 {
354     if (data()->clock()->isActive())
355     {
356         data()->clock()->stop();
357         updateTime();
358     }
359     else
360     {
361         if (fabs(data()->clock()->scale()) > Options::slewTimeScale())
362             data()->clock()->setManualMode(true);
363         data()->clock()->start();
364         if (data()->clock()->isManualMode())
365             map()->forceUpdate();
366     }
367 
368     // Update clock state in options
369     Options::setRunClock(data()->clock()->isActive());
370 }
371 
slotStepForward()372 void KStarsLite::slotStepForward()
373 {
374     if (data()->clock()->isActive())
375         data()->clock()->stop();
376     data()->clock()->manualTick(true);
377     map()->forceUpdate();
378 }
379 
slotStepBackward()380 void KStarsLite::slotStepBackward()
381 {
382     if (data()->clock()->isActive())
383         data()->clock()->stop();
384     data()->clock()->setClockScale(-1.0 * data()->clock()->scale()); //temporarily need negative time step
385     data()->clock()->manualTick(true);
386     data()->clock()->setClockScale(-1.0 * data()->clock()->scale()); //reset original sign of time step
387     map()->forceUpdate();
388 }
389 
applyConfig(bool doApplyFocus)390 void KStarsLite::applyConfig(bool doApplyFocus)
391 {
392     Q_UNUSED(doApplyFocus);
393     //color scheme
394     m_KStarsData->colorScheme()->loadFromConfig();
395     QGuiApplication::setPalette(m_KStarsData->colorScheme()->useDarkPalette() ? DarkPalette : OriginalPalette);
396 }
397 
setProjection(uint proj)398 void KStarsLite::setProjection(uint proj)
399 {
400     Options::setProjection(proj);
401     //We update SkyMapLite 2 times because of the bug in Projector::updateClipPoly()
402     SkyMapLite::Instance()->forceUpdate();
403 }
404 
getColor(QString schemeColor)405 QColor KStarsLite::getColor(QString schemeColor)
406 {
407     return KStarsData::Instance()->colorScheme()->colorNamed(schemeColor);
408 }
409 
getConfigCScheme()410 QString KStarsLite::getConfigCScheme()
411 {
412     return Options::colorSchemeFile();
413 }
414 
toggleObjects(ObjectsToToggle toToggle,bool toggle)415 void KStarsLite::toggleObjects(ObjectsToToggle toToggle, bool toggle)
416 {
417     switch (toToggle)
418     {
419         case ObjectsToToggle::Stars:
420             Options::setShowStars(toggle);
421             break;
422         case ObjectsToToggle::DeepSky:
423             Options::setShowDeepSky(toggle);
424             break;
425         case ObjectsToToggle::Planets:
426             Options::setShowSolarSystem(toggle);
427             break;
428         case ObjectsToToggle::CLines:
429             Options::setShowCLines(toggle);
430             break;
431         case ObjectsToToggle::CBounds:
432             Options::setShowCBounds(toggle);
433             break;
434         case ObjectsToToggle::ConstellationArt:
435             Options::setShowConstellationArt(toggle);
436             break;
437         case ObjectsToToggle::MilkyWay:
438             Options::setShowMilkyWay(toggle);
439             break;
440         case ObjectsToToggle::CNames:
441             Options::setShowCNames(toggle);
442             break;
443         case ObjectsToToggle::EquatorialGrid:
444             Options::setShowEquatorialGrid(toggle);
445             break;
446         case ObjectsToToggle::HorizontalGrid:
447             Options::setShowHorizontalGrid(toggle);
448             break;
449         case ObjectsToToggle::Ground:
450             Options::setShowGround(toggle);
451             break;
452         case ObjectsToToggle::Flags:
453             Options::setShowFlags(toggle);
454             break;
455         case ObjectsToToggle::Satellites:
456             Options::setShowSatellites(toggle);
457             break;
458         case ObjectsToToggle::Supernovae:
459             Options::setShowSupernovae(toggle);
460             break;
461     };
462 
463     // update time for all objects because they might be not initialized
464     // it's needed when using horizontal coordinates
465     data()->setFullTimeUpdate();
466     updateTime();
467 
468     map()->forceUpdate();
469 }
470 
isToggled(ObjectsToToggle toToggle)471 bool KStarsLite::isToggled(ObjectsToToggle toToggle)
472 {
473     switch (toToggle)
474     {
475         case ObjectsToToggle::Stars:
476             return Options::showStars();
477         case ObjectsToToggle::DeepSky:
478             return Options::showDeepSky();
479         case ObjectsToToggle::Planets:
480             return Options::showSolarSystem();
481         case ObjectsToToggle::CLines:
482             return Options::showCLines();
483         case ObjectsToToggle::CBounds:
484             return Options::showCBounds();
485         case ObjectsToToggle::ConstellationArt:
486             return Options::showConstellationArt();
487         case ObjectsToToggle::MilkyWay:
488             return Options::showMilkyWay();
489         case ObjectsToToggle::CNames:
490             return Options::showCNames();
491         case ObjectsToToggle::EquatorialGrid:
492             return Options::showEquatorialGrid();
493         case ObjectsToToggle::HorizontalGrid:
494             return Options::showHorizontalGrid();
495         case ObjectsToToggle::Ground:
496             return Options::showGround();
497         case ObjectsToToggle::Flags:
498             return Options::showFlags();
499         case ObjectsToToggle::Satellites:
500             return Options::showSatellites();
501         case ObjectsToToggle::Supernovae:
502             return Options::showSupernovae();
503         default:
504             return false;
505     };
506 }
507 
setRunTutorial(bool runTutorial)508 void KStarsLite::setRunTutorial(bool runTutorial)
509 {
510     if (Options::runStartupWizard() != runTutorial)
511     {
512         Options::setRunStartupWizard(runTutorial);
513         emit runTutorialChanged();
514     }
515 }
516 
getRunTutorial()517 bool KStarsLite::getRunTutorial()
518 {
519     return Options::runStartupWizard();
520 }
521