1 /*
2     SPDX-FileCopyrightText: 2002 Thomas Kabelmann <tk78@gmx.de>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 //KStars DBUS functions
8 
9 #include "kstars.h"
10 
11 #include "colorscheme.h"
12 #include "eyepiecefield.h"
13 #include "imageexporter.h"
14 #include "ksdssdownloader.h"
15 #include "kstarsdata.h"
16 #include "observinglist.h"
17 #include "Options.h"
18 #include "skymap.h"
19 #include "skycomponents/constellationboundarylines.h"
20 #include "skycomponents/skymapcomposite.h"
21 #include "skyobjects/catalogobject.h"
22 #include "catalogsdb.h"
23 #include "skyobjects/ksplanetbase.h"
24 #include "skyobjects/starobject.h"
25 #include "tools/whatsinteresting/wiview.h"
26 #include "dialogs/finddialog.h"
27 #include "tools/nameresolver.h"
28 
29 #ifdef HAVE_CFITSIO
30 #include "fitsviewer/fitsviewer.h"
31 #ifdef HAVE_INDI
32 #include "ekos/manager.h"
33 #endif
34 #endif
35 
36 #include <KActionCollection>
37 
38 #include <QPrintDialog>
39 #include <QPrinter>
40 #include <QElapsedTimer>
41 
42 #include "kstars_debug.h"
43 
setRaDec(double ra,double dec)44 void KStars::setRaDec(double ra, double dec)
45 {
46     SkyPoint p(ra, dec);
47     map()->setClickedPoint(&p);
48     map()->slotCenter();
49 }
50 
setRaDecJ2000(double ra0,double dec0)51 void KStars::setRaDecJ2000(double ra0, double dec0)
52 {
53     SkyPoint p;
54     p.setRA0(ra0);
55     p.setDec0(dec0);
56     p.updateCoordsNow(data()->updateNum());
57     map()->setClickedPoint(&p);
58     map()->slotCenter();
59 }
60 
setAltAz(double alt,double az,bool altIsRefracted)61 void KStars::setAltAz(double alt, double az, bool altIsRefracted)
62 {
63     SkyPoint p;
64     if (altIsRefracted)
65     {
66         alt = SkyPoint::unrefract(alt);
67     }
68     p.setAlt(alt);
69     p.setAz(az);
70     p.HorizontalToEquatorial(data()->lst(), data()->geo()->lat());
71     map()->setClickedPoint(&p);
72     map()->slotCenter();
73 }
74 
lookTowards(const QString & direction)75 void KStars::lookTowards(const QString &direction)
76 {
77     QString dir = direction.toLower();
78     if (dir == i18n("zenith") || dir == "z")
79     {
80         actionCollection()->action("zenith")->trigger();
81     }
82     else if (dir == i18n("north") || dir == "n")
83     {
84         actionCollection()->action("north")->trigger();
85     }
86     else if (dir == i18n("east") || dir == "e")
87     {
88         actionCollection()->action("east")->trigger();
89     }
90     else if (dir == i18n("south") || dir == "s")
91     {
92         actionCollection()->action("south")->trigger();
93     }
94     else if (dir == i18n("west") || dir == "w")
95     {
96         actionCollection()->action("west")->trigger();
97     }
98     else if (dir == i18n("northeast") || dir == "ne")
99     {
100         map()->stopTracking();
101         map()->clickedPoint()->setAlt(15.0);
102         map()->clickedPoint()->setAz(45.0);
103         map()->clickedPoint()->HorizontalToEquatorial(data()->lst(), data()->geo()->lat());
104         map()->slotCenter();
105     }
106     else if (dir == i18n("southeast") || dir == "se")
107     {
108         map()->stopTracking();
109         map()->clickedPoint()->setAlt(15.0);
110         map()->clickedPoint()->setAz(135.0);
111         map()->clickedPoint()->HorizontalToEquatorial(data()->lst(), data()->geo()->lat());
112         map()->slotCenter();
113     }
114     else if (dir == i18n("southwest") || dir == "sw")
115     {
116         map()->stopTracking();
117         map()->clickedPoint()->setAlt(15.0);
118         map()->clickedPoint()->setAz(225.0);
119         map()->clickedPoint()->HorizontalToEquatorial(data()->lst(), data()->geo()->lat());
120         map()->slotCenter();
121     }
122     else if (dir == i18n("northwest") || dir == "nw")
123     {
124         map()->stopTracking();
125         map()->clickedPoint()->setAlt(15.0);
126         map()->clickedPoint()->setAz(315.0);
127         map()->clickedPoint()->HorizontalToEquatorial(data()->lst(), data()->geo()->lat());
128         map()->slotCenter();
129     }
130     else
131     {
132         SkyObject *target = data()->objectNamed(direction);
133         if (target != nullptr)
134         {
135             map()->setClickedObject(target);
136             map()->setClickedPoint(target);
137             map()->slotCenter();
138         }
139     }
140 }
141 
addLabel(const QString & name)142 void KStars::addLabel(const QString &name)
143 {
144     SkyObject *target = data()->objectNamed(name);
145     if (target != nullptr)
146     {
147         data()->skyComposite()->addNameLabel(target);
148         map()->forceUpdate();
149     }
150 }
151 
removeLabel(const QString & name)152 void KStars::removeLabel(const QString &name)
153 {
154     SkyObject *target = data()->objectNamed(name);
155     if (target != nullptr)
156     {
157         data()->skyComposite()->removeNameLabel(target);
158         map()->forceUpdate();
159     }
160 }
161 
addTrail(const QString & name)162 void KStars::addTrail(const QString &name)
163 {
164     TrailObject *target = dynamic_cast<TrailObject *>(data()->objectNamed(name));
165     if (target)
166     {
167         target->addToTrail();
168         map()->forceUpdate();
169     }
170 }
171 
removeTrail(const QString & name)172 void KStars::removeTrail(const QString &name)
173 {
174     TrailObject *target = dynamic_cast<TrailObject *>(data()->objectNamed(name));
175     if (target)
176     {
177         target->clearTrail();
178         map()->forceUpdate();
179     }
180 }
181 
zoom(double z)182 void KStars::zoom(double z)
183 {
184     map()->setZoomFactor(z);
185 }
186 
zoomIn()187 void KStars::zoomIn()
188 {
189     map()->slotZoomIn();
190 }
191 
zoomOut()192 void KStars::zoomOut()
193 {
194     map()->slotZoomOut();
195 }
196 
defaultZoom()197 void KStars::defaultZoom()
198 {
199     map()->slotZoomDefault();
200 }
201 
setLocalTime(int yr,int mth,int day,int hr,int min,int sec)202 void KStars::setLocalTime(int yr, int mth, int day, int hr, int min, int sec)
203 {
204     data()->changeDateTime(data()->geo()->LTtoUT(KStarsDateTime(QDate(yr, mth, day), QTime(hr, min, sec))));
205 }
206 
setTimeToNow()207 void KStars::setTimeToNow()
208 {
209     slotSetTimeToNow();
210 }
211 
waitFor(double sec)212 void KStars::waitFor(double sec)
213 {
214     QElapsedTimer tm;
215     tm.start();
216     while (tm.elapsed() < int(1000. * sec))
217     {
218         qApp->processEvents();
219     }
220 }
221 
waitForKey(const QString & k)222 void KStars::waitForKey(const QString &k)
223 {
224     data()->resumeKey = QKeySequence::fromString(k);
225     if (!data()->resumeKey.isEmpty())
226     {
227         //When the resumeKey is pressed, resumeKey is set to empty
228         while (!data()->resumeKey.isEmpty())
229             qApp->processEvents();
230     }
231     else
232     {
233         qDebug() << "Error [D-Bus waitForKey()]: Invalid key requested.";
234     }
235 }
236 
setTracking(bool track)237 void KStars::setTracking(bool track)
238 {
239     if (track != Options::isTracking())
240         slotTrack();
241 }
242 
popupMessage(int,int,const QString &)243 void KStars::popupMessage(int /*x*/, int /*y*/, const QString & /*message*/)
244 {
245     //Show a small popup window at (x,y) with a text message
246 }
247 
drawLine(int,int,int,int,int)248 void KStars::drawLine(int /*x1*/, int /*y1*/, int /*x2*/, int /*y2*/, int /*speed*/)
249 {
250     //Draw a line on the skymap display
251 }
252 
location()253 QString KStars::location()
254 {
255     GeoLocation *currentLocation = data()->geo();
256 
257     QJsonObject locationInfo =
258     {
259         {"name", currentLocation->name()},
260         {"province", currentLocation->province()},
261         {"country", currentLocation->country()},
262         {"longitude", currentLocation->lng()->Degrees()},
263         {"latitude", currentLocation->lat()->Degrees()},
264         {"tz0", currentLocation->TZ0()},
265         {"tz", currentLocation->TZ()}
266     };
267 
268     return QJsonDocument(locationInfo).toJson(QJsonDocument::Compact);
269 }
270 
setGeoLocation(const QString & city,const QString & province,const QString & country)271 bool KStars::setGeoLocation(const QString &city, const QString &province, const QString &country)
272 {
273     //Set the geographic location
274     bool cityFound(false);
275 
276     foreach (GeoLocation *loc, data()->geoList)
277     {
278         if (loc->translatedName() == city && (province.isEmpty() || loc->translatedProvince() == province) &&
279                 loc->translatedCountry() == country)
280         {
281             cityFound = true;
282 
283             data()->setLocation(*loc);
284 
285             //configure time zone rule
286             KStarsDateTime ltime = loc->UTtoLT(data()->ut());
287             loc->tzrule()->reset_with_ltime(ltime, loc->TZ0(), data()->isTimeRunningForward());
288             data()->setNextDSTChange(loc->tzrule()->nextDSTChange());
289 
290             //reset LST
291             data()->syncLST();
292 
293             //make sure planets, etc. are updated immediately
294             data()->setFullTimeUpdate();
295 
296             // If the sky is in Horizontal mode and not tracking, reset focus such that
297             // Alt/Az remain constant.
298             if (!Options::isTracking() && Options::useAltAz())
299             {
300                 map()->focus()->HorizontalToEquatorial(data()->lst(), data()->geo()->lat());
301             }
302 
303             // recalculate new times and objects
304             data()->setSnapNextFocus();
305             updateTime();
306 
307             //no need to keep looking, we're done.
308             break;
309         }
310     }
311 
312     if (!cityFound)
313     {
314         if (province.isEmpty())
315             qDebug()
316                     << QString("Error [D-Bus setGeoLocation]: city %1, %2 not found in database.").arg(city, country);
317         else
318             qDebug() << QString("Error [D-Bus setGeoLocation]: city %1, %2, %3 not found in database.")
319                      .arg(city, province, country);
320     }
321 
322     return cityFound;
323 }
324 
setGPSLocation(double longitude,double latitude,double elevation,double tz0)325 bool KStars::setGPSLocation(double longitude, double latitude, double elevation, double tz0)
326 {
327     GeoLocation *geo = data()->geo();
328     std::unique_ptr<GeoLocation> tempGeo;
329 
330     QString newLocationName("GPS Location");
331 
332     dms lng(longitude), lat(latitude);
333 
334     GeoLocation *nearest = data()->nearestLocation(longitude, latitude);
335 
336     if (nearest)
337         tempGeo.reset(new GeoLocation(lng, lat, newLocationName, "", "", nearest->TZ0(), nearest->tzrule(), elevation));
338     else
339         tempGeo.reset(new GeoLocation(lng, lat, newLocationName, "", "", tz0, new TimeZoneRule(), elevation));
340 
341     geo = tempGeo.get();
342 
343     qCInfo(KSTARS) << "Setting location from DBus. Longitude:" << longitude << "Latitude:" << latitude;
344 
345     data()->setLocation(*geo);
346 
347     return true;
348 }
349 
readConfig()350 void KStars::readConfig()
351 {
352     //Load config file values into Options object
353     Options::self()->load();
354 
355     applyConfig();
356 
357     //Reset date, if one was stored
358     if (data()->StoredDate.isValid())
359     {
360         data()->changeDateTime(data()->geo()->LTtoUT(data()->StoredDate));
361         data()->StoredDate = KStarsDateTime(QDateTime()); //invalidate StoredDate
362     }
363 
364     map()->forceUpdate();
365 }
366 
writeConfig()367 void KStars::writeConfig()
368 {
369     Options::self()->save();
370 
371     //Store current simulation time
372     data()->StoredDate = data()->lt();
373 }
374 
getOption(const QString & name)375 QString KStars::getOption(const QString &name)
376 {
377     //Some config items are not stored in the Options object while
378     //the program is running; catch these here and returntheir current value.
379     if (name == "FocusRA")
380     {
381         return QString::number(map()->focus()->ra().Hours(), 'f', 6);
382     }
383     if (name == "FocusDec")
384     {
385         return QString::number(map()->focus()->dec().Degrees(), 'f', 6);
386     }
387 
388     KConfigSkeletonItem *it = Options::self()->findItem(name);
389     if (it)
390         return it->property().toString();
391     else
392         return QString();
393 }
394 
changeViewOption(const QString & op,const QString & val)395 void KStars::changeViewOption(const QString &op, const QString &val)
396 {
397     bool bOk(false), dOk(false);
398 
399     //parse bool value
400     bool bVal(false);
401     if (val.toLower() == "true")
402     {
403         bOk  = true;
404         bVal = true;
405     }
406     if (val.toLower() == "false")
407     {
408         bOk  = true;
409         bVal = false;
410     }
411     if (val == "1")
412     {
413         bOk  = true;
414         bVal = true;
415     }
416     if (val == "0")
417     {
418         bOk  = true;
419         bVal = false;
420     }
421 
422     //parse double value
423     double dVal = val.toDouble(&dOk);
424 
425     //[GUI]
426     if (op == "ShowInfoBoxes" && bOk)
427         Options::setShowInfoBoxes(bVal);
428     if (op == "ShowTimeBox" && bOk)
429         Options::setShowTimeBox(bVal);
430     if (op == "ShowGeoBox" && bOk)
431         Options::setShowGeoBox(bVal);
432     if (op == "ShowFocusBox" && bOk)
433         Options::setShowFocusBox(bVal);
434     if (op == "ShadeTimeBox" && bOk)
435         Options::setShadeTimeBox(bVal);
436     if (op == "ShadeGeoBox" && bOk)
437         Options::setShadeGeoBox(bVal);
438     if (op == "ShadeFocusBox" && bOk)
439         Options::setShadeFocusBox(bVal);
440 
441     //[View]
442     // FIXME: REGRESSION
443     //     if ( op == "FOVName"                ) Options::setFOVName(         val );
444     //     if ( op == "FOVSizeX"         && dOk ) Options::setFOVSizeX( (float)dVal );
445     //     if ( op == "FOVSizeY"         && dOk ) Options::setFOVSizeY( (float)dVal );
446     //     if ( op == "FOVShape"        && nOk ) Options::setFOVShape(       nVal );
447     //     if ( op == "FOVColor"               ) Options::setFOVColor(        val );
448     if (op == "ShowStars" && bOk)
449         Options::setShowStars(bVal);
450     if (op == "ShowCLines" && bOk)
451         Options::setShowCLines(bVal);
452     if (op == "ShowCBounds" && bOk)
453         Options::setShowCBounds(bVal);
454     if (op == "ShowCNames" && bOk)
455         Options::setShowCNames(bVal);
456     if (op == "ShowMilkyWay" && bOk)
457         Options::setShowMilkyWay(bVal);
458     if (op == "AutoSelectGrid" && bOk)
459         Options::setAutoSelectGrid(bVal);
460     if (op == "ShowEquatorialGrid" && bOk)
461         Options::setShowEquatorialGrid(bVal);
462     if (op == "ShowHorizontalGrid" && bOk)
463         Options::setShowHorizontalGrid(bVal);
464     if (op == "ShowEquator" && bOk)
465         Options::setShowEquator(bVal);
466     if (op == "ShowEcliptic" && bOk)
467         Options::setShowEcliptic(bVal);
468     if (op == "ShowHorizon" && bOk)
469         Options::setShowHorizon(bVal);
470     if (op == "ShowGround" && bOk)
471         Options::setShowGround(bVal);
472     if (op == "ShowSun" && bOk)
473         Options::setShowSun(bVal);
474     if (op == "ShowMoon" && bOk)
475         Options::setShowMoon(bVal);
476     if (op == "ShowMercury" && bOk)
477         Options::setShowMercury(bVal);
478     if (op == "ShowVenus" && bOk)
479         Options::setShowVenus(bVal);
480     if (op == "ShowMars" && bOk)
481         Options::setShowMars(bVal);
482     if (op == "ShowJupiter" && bOk)
483         Options::setShowJupiter(bVal);
484     if (op == "ShowSaturn" && bOk)
485         Options::setShowSaturn(bVal);
486     if (op == "ShowUranus" && bOk)
487         Options::setShowUranus(bVal);
488     if (op == "ShowNeptune" && bOk)
489         Options::setShowNeptune(bVal);
490     //if ( op == "ShowPluto"       && bOk ) Options::setShowPluto(    bVal );
491     if (op == "ShowAsteroids" && bOk)
492         Options::setShowAsteroids(bVal);
493     if (op == "ShowComets" && bOk)
494         Options::setShowComets(bVal);
495     if (op == "ShowSolarSystem" && bOk)
496         Options::setShowSolarSystem(bVal);
497     if (op == "ShowDeepSky" && bOk)
498         Options::setShowDeepSky(bVal);
499     if (op == "ShowSupernovae" && bOk)
500         Options::setShowSupernovae(bVal);
501     if (op == "ShowStarNames" && bOk)
502         Options::setShowStarNames(bVal);
503     if (op == "ShowStarMagnitudes" && bOk)
504         Options::setShowStarMagnitudes(bVal);
505     if (op == "ShowAsteroidNames" && bOk)
506         Options::setShowAsteroidNames(bVal);
507     if (op == "ShowCometNames" && bOk)
508         Options::setShowCometNames(bVal);
509     if (op == "ShowPlanetNames" && bOk)
510         Options::setShowPlanetNames(bVal);
511     if (op == "ShowPlanetImages" && bOk)
512         Options::setShowPlanetImages(bVal);
513     if (op == "HideOnSlew" && bOk)
514         Options::setHideOnSlew(bVal);
515     if (op == "HideStars" && bOk)
516         Options::setHideStars(bVal);
517     if (op == "HidePlanets" && bOk)
518         Options::setHidePlanets(bVal);
519     if (op == "HideMilkyWay" && bOk)
520         Options::setHideMilkyWay(bVal);
521     if (op == "HideCNames" && bOk)
522         Options::setHideCNames(bVal);
523     if (op == "HideCLines" && bOk)
524         Options::setHideCLines(bVal);
525     if (op == "HideCBounds" && bOk)
526         Options::setHideCBounds(bVal);
527     if (op == "HideGrids" && bOk)
528         Options::setHideGrids(bVal);
529     if (op == "HideLabels" && bOk)
530         Options::setHideLabels(bVal);
531 
532     if (op == "UseAltAz" && bOk)
533         Options::setUseAltAz(bVal);
534     if (op == "UseRefraction" && bOk)
535         Options::setUseRefraction(bVal);
536     if (op == "UseAutoLabel" && bOk)
537         Options::setUseAutoLabel(bVal);
538     if (op == "UseHoverLabel" && bOk)
539         Options::setUseHoverLabel(bVal);
540     if (op == "UseAutoTrail" && bOk)
541         Options::setUseAutoTrail(bVal);
542     if (op == "UseAnimatedSlewing" && bOk)
543         Options::setUseAnimatedSlewing(bVal);
544     if (op == "FadePlanetTrails" && bOk)
545         Options::setFadePlanetTrails(bVal);
546     if (op == "SlewTimeScale" && dOk)
547         Options::setSlewTimeScale(dVal);
548     if (op == "ZoomFactor" && dOk)
549         Options::setZoomFactor(dVal);
550     //    if ( op == "MagLimitDrawStar"     && dOk )    Options::setMagLimitDrawStar(    dVal );
551     if (op == "MagLimitDrawDeepSky" && dOk)
552         Options::setMagLimitDrawDeepSky(dVal);
553     if (op == "StarDensity" && dOk)
554         Options::setStarDensity(dVal);
555     //    if ( op == "MagLimitDrawStarZoomOut" && dOk ) Options::setMagLimitDrawStarZoomOut(        dVal );
556     if (op == "MagLimitDrawDeepSkyZoomOut" && dOk)
557         Options::setMagLimitDrawDeepSkyZoomOut(dVal);
558     if (op == "StarLabelDensity" && dOk)
559         Options::setStarLabelDensity(dVal);
560     if (op == "MagLimitHideStar" && dOk)
561         Options::setMagLimitHideStar(dVal);
562     if (op == "MagLimitAsteroid" && dOk)
563         Options::setMagLimitAsteroid(dVal);
564     if (op == "AsteroidLabelDensity" && dOk)
565         Options::setAsteroidLabelDensity(dVal);
566     if (op == "MaxRadCometName" && dOk)
567         Options::setMaxRadCometName(dVal);
568 
569     //these three are a "radio group"
570     if (op == "UseLatinConstellationNames" && bOk)
571     {
572         Options::setUseLatinConstellNames(true);
573         Options::setUseLocalConstellNames(false);
574         Options::setUseAbbrevConstellNames(false);
575     }
576     if (op == "UseLocalConstellationNames" && bOk)
577     {
578         Options::setUseLatinConstellNames(false);
579         Options::setUseLocalConstellNames(true);
580         Options::setUseAbbrevConstellNames(false);
581     }
582     if (op == "UseAbbrevConstellationNames" && bOk)
583     {
584         Options::setUseLatinConstellNames(false);
585         Options::setUseLocalConstellNames(false);
586         Options::setUseAbbrevConstellNames(true);
587     }
588 
589     map()->forceUpdate();
590 }
591 
setColor(const QString & name,const QString & value)592 void KStars::setColor(const QString &name, const QString &value)
593 {
594     ColorScheme *cs = data()->colorScheme();
595     if (cs->hasColorNamed(name))
596     {
597         cs->setColor(name, value);
598         map()->forceUpdate();
599     }
600 }
601 
colorScheme() const602 QString KStars::colorScheme() const
603 {
604     return data()->colorScheme()->fileName();
605 }
606 
loadColorScheme(const QString & name)607 void KStars::loadColorScheme(const QString &name)
608 {
609     data()->colorScheme()->load(name);
610 
611 #if 0
612     if (ok)
613     {
614         //set the application colors for the Night Vision scheme
615         if (Options::darkAppColors())
616         {
617             //OriginalPalette = QApplication::palette();
618             QApplication::setPalette(DarkPalette);
619             if (KStars::Instance()->wiView())
620                 KStars::Instance()->wiView()->setNightVisionOn(true);
621             //Note:  This uses style sheets to set the dark colors, this is cross platform.  Palettes have a different behavior on OS X and Windows as opposed to Linux.
622             //It might be a good idea to use stylesheets in the future instead of palettes but this will work for now for OS X.
623             //This is also in KStars.cpp.  If you change it, change it in BOTH places.
624 #ifdef Q_OS_OSX
625             qDebug() << "setting dark stylesheet";
626             qApp->setStyleSheet(
627                 "QWidget { background-color: black; color:red; "
628                 "selection-background-color:rgb(30,30,30);selection-color:white}"
629                 "QToolBar { border:none }"
630                 "QTabBar::tab:selected { background-color:rgb(50,50,50) }"
631                 "QTabBar::tab:!selected { background-color:rgb(30,30,30) }"
632                 "QPushButton { background-color:rgb(50,50,50);border-width:1px; border-style:solid;border-color:black}"
633                 "QPushButton::disabled { background-color:rgb(10,10,10);border-width:1px; "
634                 "border-style:solid;border-color:black }"
635                 "QToolButton:Checked { background-color:rgb(30,30,30); border:none }"
636                 "QComboBox { background-color:rgb(30,30,30); }"
637                 "QComboBox::disabled { background-color:rgb(10,10,10) }"
638                 "QScrollBar::handle { background: rgb(30,30,30) }"
639                 "QSpinBox { border-width: 1px; border-style:solid; border-color:rgb(30,30,30) }"
640                 "QDoubleSpinBox { border-width:1px; border-style:solid; border-color:rgb(30,30,30) }"
641                 "QLineEdit { border-width: 1px; border-style: solid; border-color:rgb(30,30,30) }"
642                 "QCheckBox::indicator:unchecked { background-color:rgb(30,30,30);border-width:1px; "
643                 "border-style:solid;border-color:black }"
644                 "QCheckBox::indicator:checked { background-color:red;border-width:1px; "
645                 "border-style:solid;border-color:black }"
646                 "QRadioButton::indicator:unchecked { background-color:rgb(30,30,30) }"
647                 "QRadioButton::indicator:checked { background-color:red }"
648                 "QRoundProgressBar { alternate-background-color:black }"
649                 "QDateTimeEdit {background-color:rgb(30,30,30); border-width: 1px; border-style:solid; "
650                 "border-color:rgb(30,30,30) }"
651                 "QHeaderView { color:red;background-color:black }"
652                 "QHeaderView::Section { background-color:rgb(30,30,30) }"
653                 "QTableCornerButton::section{ background-color:rgb(30,30,30) }"
654                 "");
655             qDebug() << "stylesheet set";
656 #endif
657         }
658         else
659         {
660             if (KStars::Instance()->wiView())
661                 KStars::Instance()->wiView()->setNightVisionOn(false);
662             QApplication::setPalette(OriginalPalette);
663 #ifdef Q_OS_OSX
664             qDebug() << "setting light stylesheet";
665             qApp->setStyleSheet("");
666             qDebug() << "stylesheet set";
667 #endif
668         }
669     }
670 #endif
671 
672     Options::setColorSchemeFile(name);
673 
674     emit colorSchemeChanged();
675 
676     map()->forceUpdate();
677 }
678 
exportImage(const QString & url,int w,int h,bool includeLegend)679 void KStars::exportImage(const QString &url, int w, int h, bool includeLegend)
680 {
681     ImageExporter *m_ImageExporter = m_KStarsData->imageExporter();
682 
683     if (w <= 0)
684         w = map()->width();
685     if (h <= 0)
686         h = map()->height();
687 
688     QSize size(w, h);
689 
690     m_ImageExporter->includeLegend(includeLegend);
691     m_ImageExporter->setRasterOutputSize(&size);
692     m_ImageExporter->exportImage(url);
693 }
694 
getDSSURL(const QString & objectName)695 QString KStars::getDSSURL(const QString &objectName)
696 {
697     SkyObject *target = data()->objectNamed(objectName);
698     if (!target)
699     {
700         return QString("ERROR");
701     }
702     else
703     {
704         return KSDssDownloader::getDSSURL(target);
705     }
706 }
707 
getDSSURL(double RA_J2000,double Dec_J2000,float width,float height)708 QString KStars::getDSSURL(double RA_J2000, double Dec_J2000, float width, float height)
709 {
710     dms ra(RA_J2000), dec(Dec_J2000);
711     return KSDssDownloader::getDSSURL(ra, dec, width, height);
712 }
713 
getObjectDataXML(const QString & objectName,bool fallbackToInternet,bool storeInternetResolved)714 QString KStars::getObjectDataXML(const QString &objectName, bool fallbackToInternet, bool storeInternetResolved)
715 {
716     bool deleteTargetAfterUse = false;
717     const SkyObject *target = data()->objectNamed(objectName);
718     if (!target && fallbackToInternet)
719     {
720         if (!storeInternetResolved)
721         {
722             const auto &cedata = NameResolver::resolveName(objectName);
723             if (cedata.first)
724             {
725                 target = cedata.second.clone(); // We have to free this since we own the pointer
726                 deleteTargetAfterUse = true; // so note that down
727             }
728         }
729         else
730         {
731             CatalogsDB::DBManager db_manager { CatalogsDB::dso_db_path() };
732             target = FindDialog::resolveAndAdd(db_manager, objectName);
733         }
734 
735     }
736     if (!target)
737     {
738         return QString("<xml></xml>");
739     }
740     QString output;
741     QXmlStreamWriter stream(&output);
742     stream.setAutoFormatting(true);
743     stream.writeStartDocument();
744     stream.writeStartElement("object");
745     stream.writeTextElement("Name", target->name());
746     stream.writeTextElement("Alt_Name", target->name2());
747     stream.writeTextElement("Long_Name", target->longname());
748     stream.writeTextElement("Constellation",
749                             KStarsData::Instance()->skyComposite()->constellationBoundary()->constellationName(target));
750     stream.writeTextElement("RA_Dec_Epoch_JD", QString::number(target->getLastPrecessJD(), 'f', 3));
751     stream.writeTextElement("RA_HMS", target->ra().toHMSString());
752     stream.writeTextElement("Dec_DMS", target->dec().toDMSString());
753     stream.writeTextElement("RA_J2000_HMS", target->ra0().toHMSString());
754     stream.writeTextElement("Dec_J2000_DMS", target->dec0().toDMSString());
755     stream.writeTextElement("RA_Degrees", QString::number(target->ra().Degrees()));
756     stream.writeTextElement("Dec_Degrees", QString::number(target->dec().Degrees()));
757     stream.writeTextElement("RA_J2000_Degrees", QString::number(target->ra0().Degrees()));
758     stream.writeTextElement("Dec_J2000_Degrees", QString::number(target->dec0().Degrees()));
759     stream.writeTextElement("Type", target->typeName());
760     stream.writeTextElement("Magnitude", QString::number(target->mag(), 'g', 2));
761     stream.writeTextElement("Position_Angle", QString::number(target->pa(), 'g', 3));
762     auto *star = dynamic_cast<const StarObject *>(target);
763     auto *dso = dynamic_cast<const CatalogObject *>(target);
764     if (star)
765     {
766         stream.writeTextElement("Spectral_Type", star->sptype());
767         stream.writeTextElement("Genetive_Name", star->gname());
768         stream.writeTextElement("Greek_Letter", star->greekLetter());
769         stream.writeTextElement("Proper_Motion", QString::number(star->pmMagnitude()));
770         stream.writeTextElement("Proper_Motion_RA", QString::number(star->pmRA()));
771         stream.writeTextElement("Proper_Motion_Dec", QString::number(star->pmDec()));
772         stream.writeTextElement("Parallax_mas", QString::number(star->parallax()));
773         stream.writeTextElement("Distance_pc", QString::number(star->distance()));
774         stream.writeTextElement("Henry_Draper", QString::number(star->getHDIndex()));
775         stream.writeTextElement("BV_Index", QString::number(star->getBVIndex()));
776     }
777     else if (dso)
778     {
779         stream.writeTextElement("Catalog", dso->getCatalog().name);
780         stream.writeTextElement("Major_Axis", QString::number(dso->a()));
781         stream.writeTextElement("Minor_Axis", QString::number(dso->a() * dso->e()));
782     }
783     stream.writeEndElement(); // object
784     stream.writeEndDocument();
785 
786     if (deleteTargetAfterUse)
787     {
788         Q_ASSERT(!!target);
789         delete target;
790     }
791 
792     return output;
793 }
794 
getObjectPositionInfo(const QString & objectName)795 QString KStars::getObjectPositionInfo(const QString &objectName)
796 {
797     Q_ASSERT(data());
798     const SkyObject *obj = data()->objectNamed(objectName); // make sure we work with a clone
799     if (!obj)
800     {
801         return QString("<xml></xml>");
802     }
803     SkyObject *target = obj->clone();
804     if (!target) // should not happen
805     {
806         qWarning() << "ERROR: Could not clone SkyObject " << objectName << "!";
807         return QString("<xml></xml>");
808     }
809 
810     const KSNumbers *updateNum = data()->updateNum();
811     const KStarsDateTime ut    = data()->ut();
812     const GeoLocation *geo     = data()->geo();
813     QString riseTimeString, setTimeString, transitTimeString;
814     QString riseAzString, setAzString, transitAltString;
815 
816     // Make sure the coordinates of the SkyObject are updated
817     target->updateCoords(updateNum, true, geo->lat(), data()->lst(), true);
818     target->EquatorialToHorizontal(data()->lst(), geo->lat());
819 
820     // Compute rise, set and transit times and parameters -- Code pulled from DetailDialog
821     QTime riseTime    = target->riseSetTime(ut, geo, true);   //true = use rise time
822     dms riseAz        = target->riseSetTimeAz(ut, geo, true); //true = use rise time
823     QTime transitTime = target->transitTime(ut, geo);
824     dms transitAlt    = target->transitAltitude(ut, geo);
825     if (transitTime < riseTime)
826     {
827         transitTime = target->transitTime(ut.addDays(1), geo);
828         transitAlt  = target->transitAltitude(ut.addDays(1), geo);
829     }
830     //If set time is before rise time, use set time for tomorrow
831     QTime setTime = target->riseSetTime(ut, geo, false);   //false = use set time
832     dms setAz     = target->riseSetTimeAz(ut, geo, false); //false = use set time
833     if (setTime < riseTime)
834     {
835         setTime = target->riseSetTime(ut.addDays(1), geo, false);   //false = use set time
836         setAz   = target->riseSetTimeAz(ut.addDays(1), geo, false); //false = use set time
837     }
838     if (riseTime.isValid())
839     {
840         riseTimeString = QString::asprintf("%02d:%02d", riseTime.hour(), riseTime.minute());
841         setTimeString  = QString::asprintf("%02d:%02d", setTime.hour(), setTime.minute());
842         riseAzString   = riseAz.toDMSString(true, true);
843         setAzString    = setAz.toDMSString(true, true);
844     }
845     else
846     {
847         if (target->alt().Degrees() > 0.0)
848         {
849             riseTimeString = setTimeString = QString("Circumpolar");
850         }
851         else
852         {
853             riseTimeString = setTimeString = QString("Never Rises");
854         }
855         riseAzString = setAzString = QString("N/A");
856     }
857 
858     transitTimeString = QString::asprintf("%02d:%02d", transitTime.hour(), transitTime.minute());
859     transitAltString  = transitAlt.toDMSString(true, true);
860 
861     QString output;
862     QXmlStreamWriter stream(&output);
863     stream.setAutoFormatting(true);
864     stream.writeStartDocument();
865     stream.writeStartElement("object");
866     stream.writeTextElement("Name", target->name());
867     stream.writeTextElement("RA_Dec_Epoch_JD", QString::number(target->getLastPrecessJD(), 'f', 3));
868     stream.writeTextElement("AltAz_JD", QString::number(data()->ut().djd(), 'f', 3));
869     stream.writeTextElement("RA_HMS", target->ra().toHMSString(true));
870     stream.writeTextElement("Dec_DMS", target->dec().toDMSString(true, true));
871     stream.writeTextElement("RA_J2000_HMS", target->ra0().toHMSString(true));
872     stream.writeTextElement("Dec_J2000_DMS", target->dec0().toDMSString(true, true));
873     stream.writeTextElement("RA_Degrees", QString::number(target->ra().Degrees()));
874     stream.writeTextElement("Dec_Degrees", QString::number(target->dec().Degrees()));
875     stream.writeTextElement("RA_J2000_Degrees", QString::number(target->ra0().Degrees()));
876     stream.writeTextElement("Dec_J2000_Degrees", QString::number(target->dec0().Degrees()));
877     stream.writeTextElement("Altitude_DMS", target->alt().toDMSString(true, true));
878     stream.writeTextElement("Azimuth_DMS", target->az().toDMSString(true, true));
879     stream.writeTextElement("Altitude_Degrees", QString::number(target->alt().Degrees()));
880     stream.writeTextElement("Azimuth_Degrees", QString::number(target->az().Degrees()));
881     stream.writeTextElement("Rise", riseTimeString);
882     stream.writeTextElement("Rise_Az_DMS", riseAzString);
883     stream.writeTextElement("Set", setTimeString);
884     stream.writeTextElement("Set_Az_DMS", setAzString);
885     stream.writeTextElement("Transit", transitTimeString);
886     stream.writeTextElement("Transit_Alt_DMS", transitAltString);
887     stream.writeTextElement("Time_Zone_Offset", QString::asprintf("%02.2f", geo->TZ()));
888 
889     stream.writeEndElement(); // object
890     stream.writeEndDocument();
891     return output;
892 }
893 
renderEyepieceView(const QString & objectName,const QString & destPathChart,const double fovWidth,const double fovHeight,const double rotation,const double scale,const bool flip,const bool invert,QString imagePath,const QString & destPathImage,const bool overlay,const bool invertColors)894 void KStars::renderEyepieceView(const QString &objectName, const QString &destPathChart, const double fovWidth,
895                                 const double fovHeight, const double rotation, const double scale, const bool flip,
896                                 const bool invert, QString imagePath, const QString &destPathImage, const bool overlay,
897                                 const bool invertColors)
898 {
899     const SkyObject *obj = data()->objectNamed(objectName);
900     if (!obj)
901     {
902         qCWarning(KSTARS) << "Object named " << objectName << " was not found!";
903         return;
904     }
905     SkyObject *target          = obj->clone();
906     const KSNumbers *updateNum = data()->updateNum();
907     const KStarsDateTime ut    = data()->ut();
908     const GeoLocation *geo     = data()->geo();
909     QPixmap *renderChart       = new QPixmap();
910     QPixmap *renderImage       = nullptr;
911     QTemporaryFile tempFile;
912     if (overlay || (!destPathImage.isEmpty()))
913     {
914         if (!QFile::exists(imagePath))
915         {
916             // We must download a DSS image
917             tempFile.open();
918             QEventLoop loop;
919             std::function<void(bool)> slot = [&loop](bool unused)
920             {
921                 Q_UNUSED(unused);
922                 loop.quit();
923             };
924             new KSDssDownloader(target, tempFile.fileName(), slot, this);
925             qDebug() << "DSS download requested. Waiting for download to complete...";
926             loop.exec(); // wait for download to complete
927             imagePath = tempFile.fileName();
928         }
929         if (QFile::exists(imagePath)) // required because DSS Download may fail
930             renderImage = new QPixmap();
931     }
932 
933     // Make sure the coordinates of the SkyObject are updated
934     target->updateCoords(updateNum, true, geo->lat(), data()->lst(), true);
935     target->EquatorialToHorizontal(data()->lst(), geo->lat());
936 
937     EyepieceField::renderEyepieceView(target, renderChart, fovWidth, fovHeight, rotation, scale, flip, invert,
938                                       imagePath, renderImage, overlay, invertColors);
939     renderChart->save(destPathChart);
940     delete renderChart;
941     if (renderImage)
942     {
943         renderImage->save(destPathImage);
944         delete renderImage;
945     }
946 }
getObservingWishListObjectNames()947 QString KStars::getObservingWishListObjectNames()
948 {
949     QString output;
950 
951     for (auto &object : KStarsData::Instance()->observingList()->obsList())
952     {
953         output.append(object->name() + '\n');
954     }
955     return output;
956 }
957 
getObservingSessionPlanObjectNames()958 QString KStars::getObservingSessionPlanObjectNames()
959 {
960     QString output;
961 
962     for (auto &object : KStarsData::Instance()->observingList()->sessionList())
963     {
964         output.append(object->name() + '\n');
965     }
966     return output;
967 }
968 
setApproxFOV(double FOV_Degrees)969 void KStars::setApproxFOV(double FOV_Degrees)
970 {
971     zoom(map()->width() / (FOV_Degrees * dms::DegToRad));
972 }
973 
getSkyMapDimensions()974 QString KStars::getSkyMapDimensions()
975 {
976     return (QString::number(map()->width()) + 'x' + QString::number(map()->height()));
977 }
printImage(bool usePrintDialog,bool useChartColors)978 void KStars::printImage(bool usePrintDialog, bool useChartColors)
979 {
980     //QPRINTER_FOR_NOW
981     //    KPrinter printer( true, QPrinter::HighResolution );
982     QPrinter printer(QPrinter::HighResolution);
983     printer.setFullPage(false);
984 
985     //Set up the printer (either with the Print Dialog,
986     //or using the default settings)
987     bool ok(false);
988     if (usePrintDialog)
989     {
990         //QPRINTER_FOR_NOW
991         //        ok = printer.setup( this, i18n("Print Sky") );
992         //QPrintDialog *dialog = KdePrint::createPrintDialog(&printer, this);
993         QPrintDialog *dialog = new QPrintDialog(&printer, this);
994         dialog->setWindowTitle(i18nc("@title:window", "Print Sky"));
995         if (dialog->exec() == QDialog::Accepted)
996             ok = true;
997         delete dialog;
998     }
999     else
1000     {
1001         //QPRINTER_FOR_NOW
1002         //        ok = printer.autoConfigure();
1003         ok = true;
1004     }
1005 
1006     if (ok)
1007     {
1008         QApplication::setOverrideCursor(Qt::WaitCursor);
1009 
1010         //Save current ColorScheme file name and switch to Star Chart
1011         //scheme (if requested)
1012         QString schemeName = data()->colorScheme()->fileName();
1013         if (useChartColors)
1014         {
1015             loadColorScheme("chart.colors");
1016         }
1017 
1018         map()->setupProjector();
1019         map()->exportSkyImage(&printer, true);
1020 
1021         //Restore old color scheme if necessary
1022         //(if printing was aborted, the ColorScheme is still restored)
1023         if (useChartColors)
1024         {
1025             loadColorScheme(schemeName);
1026             map()->forceUpdate();
1027         }
1028 
1029         QApplication::restoreOverrideCursor();
1030     }
1031 }
1032 
openFITS(const QUrl & imageURL)1033 void KStars::openFITS(const QUrl &imageURL)
1034 {
1035 #ifndef HAVE_CFITSIO
1036     qWarning() << "KStars does not support loading FITS. Please recompile KStars with FITS support.";
1037 #else
1038     QPointer<FITSViewer> fv = createFITSViewer();
1039     //    auto m_Loaded = std::make_shared<QMetaObject::Connection>();
1040     //    *m_Loaded = connect(fv.get(), &FITSViewer::loaded, [fv, m_Loaded]()
1041     //    {
1042     //        fv->show();
1043 
1044     //        QObject::disconnect(*m_Loaded);
1045     //    });
1046 
1047     //    auto m_Failed = std::make_shared<QMetaObject::Connection>();
1048     //    *m_Failed = connect(fv.get(), &FITSViewer::failed, [fv, m_Failed]()
1049     //    {
1050     //        QObject::disconnect(*m_Failed);
1051     //    });
1052 
1053     fv->loadFile(imageURL);
1054 #endif
1055 }
1056