File indexing completed on 2024-03-24 15:15:24

0001 /*
0002     SPDX-FileCopyrightText: Thomas Kabelmann
0003     SPDX-FileCopyrightText: 2018 Robert Lancaster <rlancaste@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "xplanetimageviewer.h"
0009 #include "Options.h"
0010 #include "dialogs/timedialog.h"
0011 #include "ksnotification.h"
0012 
0013 #include <QtConcurrent>
0014 
0015 #ifndef KSTARS_LITE
0016 #include "kstars.h"
0017 #endif
0018 
0019 #ifndef KSTARS_LITE
0020 #include <KMessageBox>
0021 #endif
0022 
0023 #include <QFileDialog>
0024 #include <QPainter>
0025 #include <QResizeEvent>
0026 #include <QStatusBar>
0027 #include <QTemporaryFile>
0028 #include <QVBoxLayout>
0029 #include <QPushButton>
0030 #include <QApplication>
0031 #include <QScreen>
0032 #include <QSlider>
0033 #include "skymap.h"
0034 #include "kspaths.h"
0035 #include "fov.h"
0036 
0037 #include <QUuid>
0038 #include <sys/stat.h>
0039 #include <QInputDialog>
0040 
0041 typedef enum
0042 {
0043     SUN, MERCURY, VENUS,
0044     EARTH, MOON,
0045     MARS, PHOBOS, DEIMOS,
0046     JUPITER, GANYMEDE, IO, CALLISTO, EUROPA,
0047     SATURN, TITAN, MIMAS, ENCELADUS, TETHYS, DIONE, RHEA, HYPERION, IAPETUS, PHOEBE,
0048     URANUS, UMBRIEL, ARIEL, MIRANDA, TITANIA, OBERON,
0049     NEPTUNE, TRITON
0050 } objects;
0051 
0052 XPlanetImageLabel::XPlanetImageLabel(QWidget *parent) : QFrame(parent)
0053 {
0054 #ifndef KSTARS_LITE
0055     grabGesture(Qt::PinchGesture);
0056     setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
0057     setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
0058     setLineWidth(2);
0059 #endif
0060 }
0061 
0062 void XPlanetImageLabel::setImage(const QImage &img)
0063 {
0064 #ifndef KSTARS_LITE
0065     m_Image = img;
0066     m_Pix     = QPixmap::fromImage(m_Image);
0067 #endif
0068 }
0069 
0070 void XPlanetImageLabel::invertPixels()
0071 {
0072 #ifndef KSTARS_LITE
0073     m_Image.invertPixels();
0074     m_Pix = QPixmap::fromImage(m_Image.scaled(width(), height(), Qt::KeepAspectRatio));
0075 #endif
0076 }
0077 
0078 void XPlanetImageLabel::paintEvent(QPaintEvent *)
0079 {
0080 #ifndef KSTARS_LITE
0081     QPainter p;
0082     p.begin(this);
0083     int x = 0;
0084     if (m_Pix.width() < width())
0085         x = (width() - m_Pix.width()) / 2;
0086     p.drawPixmap(x, 0, m_Pix);
0087     p.end();
0088 #endif
0089 }
0090 
0091 void XPlanetImageLabel::resizeEvent(QResizeEvent *event)
0092 {
0093     if (event->size() == m_Pix.size())
0094         return;
0095 
0096     m_Pix = QPixmap::fromImage(m_Image.scaled(event->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
0097 }
0098 
0099 void XPlanetImageLabel::refreshImage()
0100 {
0101     m_Pix = QPixmap::fromImage(m_Image.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
0102     update();
0103 }
0104 
0105 void XPlanetImageLabel::wheelEvent(QWheelEvent *e)
0106 {
0107     //This attempts to send the wheel event back to the Scroll Area if it was taken from a trackpad
0108     //It should still do the zoom if it is a mouse wheel
0109     if (e->source() == Qt::MouseEventSynthesizedBySystem)
0110     {
0111         QFrame::wheelEvent(e);
0112     }
0113     else
0114     {
0115         if (e->angleDelta().y() > 0)
0116             emit zoomIn();
0117         else if (e->angleDelta().y() < 0)
0118             emit zoomOut();
0119         e->accept();
0120     }
0121 }
0122 
0123 bool XPlanetImageLabel::event(QEvent *event)
0124 {
0125     if (event->type() == QEvent::Gesture)
0126         return gestureEvent(dynamic_cast<QGestureEvent *>(event));
0127     return QFrame::event(event);
0128 }
0129 
0130 bool XPlanetImageLabel::gestureEvent(QGestureEvent *event)
0131 {
0132     if (QGesture *pinch = event->gesture(Qt::PinchGesture))
0133         pinchTriggered(dynamic_cast<QPinchGesture *>(pinch));
0134     return true;
0135 }
0136 
0137 
0138 void XPlanetImageLabel::pinchTriggered(QPinchGesture *gesture)
0139 {
0140     if (gesture->totalScaleFactor() > 1)
0141         emit zoomIn();
0142     else
0143         emit zoomOut();
0144 }
0145 
0146 
0147 void XPlanetImageLabel::mousePressEvent(QMouseEvent *e)
0148 {
0149     m_MouseButtonDown = true;
0150     m_LastMousePoint = e->globalPos();
0151     e->accept();
0152 }
0153 
0154 void XPlanetImageLabel::mouseReleaseEvent(QMouseEvent *e)
0155 {
0156     m_MouseButtonDown = false;
0157     e->accept();
0158 }
0159 
0160 void XPlanetImageLabel::mouseMoveEvent(QMouseEvent *e)
0161 {
0162     if(m_MouseButtonDown)
0163     {
0164         QPoint newPoint = e->globalPos();
0165         int dx = newPoint.x() - m_LastMousePoint.x();
0166         int dy = newPoint.y() - m_LastMousePoint.y();
0167         if(e->buttons() & Qt::RightButton)
0168             emit changeLocation(QPoint(dx, dy));
0169         if(e->buttons() & Qt::LeftButton)
0170             emit changePosition(QPoint(dx, dy));
0171         m_LastMousePoint = newPoint;
0172     }
0173     e->accept();
0174 }
0175 
0176 XPlanetImageViewer::XPlanetImageViewer(const QString &obj, QWidget *parent): QDialog(parent)
0177 {
0178 #ifndef KSTARS_LITE
0179     m_LastFile = QDir::homePath();
0180 
0181 #ifdef Q_OS_OSX
0182     setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
0183 #endif
0184     setAttribute(Qt::WA_DeleteOnClose, true);
0185     setModal(false);
0186     setWindowTitle(i18nc("@title:window", "XPlanet Solar System Simulator: %1", obj));
0187 
0188     setXPlanetDate(KStarsData::Instance()->ut());
0189 
0190     // Create widget
0191     QFrame *page = new QFrame(this);
0192 
0193     //setMainWidget( page );
0194     QVBoxLayout *mainLayout = new QVBoxLayout(this);
0195     mainLayout->addWidget(page);
0196     setLayout(mainLayout);
0197 
0198     QWidget *selectorsWidget = new QWidget(this);
0199     QHBoxLayout *selectorsLayout = new QHBoxLayout(selectorsWidget);
0200     selectorsLayout->setContentsMargins(0, 0, 0, 0);
0201     mainLayout->addWidget(selectorsWidget);
0202 
0203     m_ObjectNames       << i18n("Sun")      << i18n("Mercury")  << i18n("Venus");
0204     m_objectDefaultFOVs << 0.74818          << 0.004          << 0.02;
0205     m_ObjectNames       << i18n("Earth")    << i18n("Moon");
0206     m_objectDefaultFOVs << 1.0              << 0.74818;
0207     m_ObjectNames       << i18n("Mars")     << i18n("Phobos")   << i18n("Deimos");
0208     m_objectDefaultFOVs << 0.00865          << 0.00002          << 0.00002;
0209     m_ObjectNames       << i18n("Jupiter")  << i18n("Ganymede") << i18n("Io")       << i18n("Callisto") << i18n("Europa");
0210     m_objectDefaultFOVs << 0.02             << 0.0005           << 0.0004           << 0.0005           << 0.0003;
0211     m_ObjectNames       << i18n("Saturn")   << i18n("Titan")    << i18n("Mimas")    << i18n("Enceladus") << i18n("Tethys")   << i18n("Dione")    << i18n("Rhea")     << i18n("Hyperion") << i18n("Iapetus")  << i18n("Phoebe");
0212     m_objectDefaultFOVs << 0.02             << 0.0003            << 0.00002            << 0.00003            << 0.00007            << 0.00007            << 0.0001            << 0.00002            << 0.0001            << 0.00002;
0213     m_ObjectNames       << i18n("Uranus")   << i18n("Umbriel")  << i18n("Ariel")    << i18n("Miranda")  << i18n("Titania")  << i18n("Oberon");
0214     m_objectDefaultFOVs << 0.00256          << 0.00004            << 0.00004            << 0.00002            << 0.00005            << 0.00005;
0215     m_ObjectNames       << i18n("Neptune")  << i18n("Triton");
0216     m_objectDefaultFOVs << 0.00114          << 0.0001;
0217 
0218     m_CurrentObjectIndex = m_ObjectNames.indexOf(obj);
0219     if (m_CurrentObjectIndex < 0)
0220         // Set to Saturn if current object is not in the list.
0221         m_CurrentObjectIndex = 13;
0222     m_ObjectName = m_ObjectNames.at(m_CurrentObjectIndex);
0223 
0224     QComboBox *objectSelector = new QComboBox(this);
0225     objectSelector->addItems(m_ObjectNames);
0226     objectSelector->setToolTip(i18n("This allows you to select a new object/target for XPlanet to view"));
0227     selectorsLayout->addWidget(objectSelector);
0228     objectSelector->setCurrentIndex(m_CurrentObjectIndex);
0229     connect(objectSelector,  SIGNAL(currentIndexChanged(int)), this, SLOT(updateXPlanetObject(int)));
0230 
0231     m_CurrentOriginIndex = EARTH;
0232     m_OriginName = m_ObjectNames.at(EARTH);
0233 
0234     selectorsLayout->addWidget(new QLabel(i18n("from"), this));
0235     m_OriginSelector = new QComboBox(this);
0236     m_OriginSelector->addItems(m_ObjectNames);
0237     m_OriginSelector->setToolTip(i18n("This allows you to select a viewing location"));
0238     selectorsLayout->addWidget(m_OriginSelector);
0239     m_OriginSelector->setCurrentIndex(EARTH);
0240     connect(m_OriginSelector,  SIGNAL(currentIndexChanged(int)), this, SLOT(updateXPlanetOrigin(int)));
0241 
0242     m_lat = Options::xplanetLatitude().toDouble();
0243     m_lon = Options::xplanetLongitude().toDouble();
0244     m_Radius = 45;
0245 
0246     selectorsLayout->addWidget(new QLabel(i18n("Location:"), this));
0247 
0248     m_PositionDisplay = new QLabel(this);
0249     m_PositionDisplay->setToolTip(i18n("XPlanet Latitude, Longitude, and object radius in %. This is only valid when viewing the object from the same object"));
0250     updatePositionDisplay();
0251     m_PositionDisplay->setDisabled(true);
0252     selectorsLayout->addWidget(m_PositionDisplay);
0253 
0254     QPushButton *resetXPlanetLocation = new QPushButton(this);
0255     resetXPlanetLocation->setIcon(QIcon::fromTheme("system-reboot"));
0256     resetXPlanetLocation->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0257     resetXPlanetLocation->setMaximumSize(QSize(32, 32));
0258     resetXPlanetLocation->setMinimumSize(QSize(32, 32));
0259     resetXPlanetLocation->setToolTip(i18n("Reset XPlanet Location to the location specified in the XPlanet Options"));
0260     selectorsLayout->addWidget(resetXPlanetLocation);
0261     connect(resetXPlanetLocation, SIGNAL(clicked()), this, SLOT(resetLocation()));
0262 
0263     m_FreeRotate = new QPushButton(this);
0264     m_FreeRotate->setIcon(QIcon::fromTheme("object-rotate-left"));
0265     m_FreeRotate->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0266     m_FreeRotate->setMaximumSize(QSize(32, 32));
0267     m_FreeRotate->setMinimumSize(QSize(32, 32));
0268     m_FreeRotate->setCheckable(true);
0269     m_FreeRotate->setToolTip(i18n("Hover over target and freely rotate view with mouse in XPlanet Viewer"));
0270     selectorsLayout->addWidget(m_FreeRotate);
0271     connect(m_FreeRotate, SIGNAL(clicked()), this, SLOT(slotFreeRotate()));
0272 
0273     QPushButton *reCenterB = new QPushButton(this);
0274     reCenterB->setIcon(QIcon::fromTheme("snap-bounding-box-center"));
0275     reCenterB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0276     reCenterB->setMaximumSize(QSize(32, 32));
0277     reCenterB->setMinimumSize(QSize(32, 32));
0278     reCenterB->setToolTip(i18n("Recenters the XPlanet image once it has been moved"));
0279     selectorsLayout->addWidget(reCenterB);
0280     connect(reCenterB, SIGNAL(clicked()), this, SLOT(reCenterXPlanet()));
0281 
0282     QPushButton *saveB = new QPushButton(this);
0283     saveB->setIcon(QIcon::fromTheme("document-save"));
0284     saveB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0285     saveB->setMaximumSize(QSize(32, 32));
0286     saveB->setMinimumSize(QSize(32, 32));
0287     saveB->setToolTip(i18n("Save the image to disk"));
0288     selectorsLayout->addWidget(saveB);
0289     connect(saveB, SIGNAL(clicked()), this, SLOT(saveFileToDisk()));
0290 
0291     QWidget *viewControlsWidget = new QWidget(this);
0292     QHBoxLayout *viewControlsLayout = new QHBoxLayout(viewControlsWidget);
0293     viewControlsLayout->setContentsMargins(0, 0, 0, 0);
0294     mainLayout->addWidget(viewControlsWidget);
0295 
0296     viewControlsLayout->addWidget(new QLabel(i18n("FOV:"), this));
0297 
0298     m_FOVEdit = new NonLinearDoubleSpinBox();
0299     m_FOVEdit->setDecimals(5);
0300     QList<double> possibleValues;
0301     possibleValues << 0;
0302     for(double i = .0001; i < 100; i *= 1.5)
0303         possibleValues << i;
0304     m_FOVEdit->setRecommendedValues(possibleValues);
0305     m_FOVEdit->setToolTip(i18n("Sets the FOV to the Specified value.   Note: has no effect if hovering over object."));
0306     viewControlsLayout->addWidget(m_FOVEdit);
0307 
0308     if (Options::xplanetFOV())
0309         m_FOV = KStars::Instance()->map()->fov();
0310     else
0311         m_FOV = m_objectDefaultFOVs.at( m_CurrentObjectIndex);
0312     m_FOVEdit->setValue(m_FOV);
0313 
0314     connect(m_FOVEdit,  SIGNAL(valueChanged(double)), this, SLOT(updateXPlanetFOVEdit()));
0315 
0316     m_KStarsFOV = new QPushButton(this);
0317     m_KStarsFOV->setIcon(QIcon::fromTheme("zoom-fit-width"));
0318     m_KStarsFOV->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0319     m_KStarsFOV->setMaximumSize(QSize(32, 32));
0320     m_KStarsFOV->setMinimumSize(QSize(32, 32));
0321     m_KStarsFOV->setToolTip(i18n("Zoom to the current KStars FOV.   Note: has no effect if hovering over object."));
0322     viewControlsLayout->addWidget(m_KStarsFOV);
0323     connect(m_KStarsFOV, SIGNAL(clicked()), this, SLOT(setKStarsXPlanetFOV()));
0324 
0325     m_setFOV = new QPushButton(this);
0326     m_setFOV->setIcon(QIcon::fromTheme("view-list-details"));
0327     m_setFOV->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0328     m_setFOV->setMaximumSize(QSize(32, 32));
0329     m_setFOV->setMinimumSize(QSize(32, 32));
0330     m_setFOV->setToolTip(i18n("Zoom to a specific FOV. This has no effect when hovering over an object"));
0331     viewControlsLayout->addWidget(m_setFOV);
0332     connect(m_setFOV, SIGNAL(clicked()), this, SLOT(setFOVfromList()));
0333 
0334     m_NoFOV = new QPushButton(this);
0335     m_NoFOV->setIcon(QIcon::fromTheme("system-reboot"));
0336     m_NoFOV->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0337     m_NoFOV->setMaximumSize(QSize(32, 32));
0338     m_NoFOV->setMinimumSize(QSize(32, 32));
0339     m_NoFOV->setToolTip(i18n("Optimum FOV for the target, FOV parameter not specified.  Note: has no effect if hovering over object."));
0340     viewControlsLayout->addWidget(m_NoFOV);
0341     connect(m_NoFOV, SIGNAL(clicked()), this, SLOT(resetXPlanetFOV()));
0342 
0343     m_Rotation = 0;
0344 
0345     viewControlsLayout->addWidget(new QLabel(i18n("Rotation:"), this));
0346 
0347     m_RotateEdit = new QSpinBox();
0348 
0349     m_RotateEdit->setRange(-180, 180);
0350     m_RotateEdit->setValue(0);
0351     m_RotateEdit->setSingleStep(10);
0352     m_RotateEdit->setToolTip(i18n("Set the view rotation to the desired angle"));
0353     viewControlsLayout->addWidget(m_RotateEdit);
0354     connect(m_RotateEdit,  SIGNAL(valueChanged(int)), this, SLOT(updateXPlanetRotationEdit()));
0355 
0356     QPushButton *invertRotation = new QPushButton(this);
0357     invertRotation->setIcon(QIcon::fromTheme("object-flip-vertical"));
0358     invertRotation->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0359     invertRotation->setMaximumSize(QSize(32, 32));
0360     invertRotation->setMinimumSize(QSize(32, 32));
0361     invertRotation->setToolTip(i18n("Rotate the view 180 degrees"));
0362     viewControlsLayout->addWidget(invertRotation);
0363     connect(invertRotation, SIGNAL(clicked()), this, SLOT(invertXPlanetRotation()));
0364 
0365     QPushButton *resetRotation = new QPushButton(this);
0366     resetRotation->setIcon(QIcon::fromTheme("system-reboot"));
0367     resetRotation->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0368     resetRotation->setMaximumSize(QSize(32, 32));
0369     resetRotation->setMinimumSize(QSize(32, 32));
0370     resetRotation->setToolTip(i18n("Reset view rotation to 0"));
0371     viewControlsLayout->addWidget(resetRotation);
0372     connect(resetRotation, SIGNAL(clicked()), this, SLOT(resetXPlanetRotation()));
0373 
0374     QPushButton *optionsB = new QPushButton(this);
0375     optionsB->setIcon(QIcon::fromTheme("configure"));
0376     optionsB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0377     optionsB->setMaximumSize(QSize(32, 32));
0378     optionsB->setMinimumSize(QSize(32, 32));
0379     optionsB->setToolTip(i18n("Bring up XPlanet Options"));
0380     viewControlsLayout->addWidget(optionsB);
0381     connect(optionsB, SIGNAL(clicked()), KStars::Instance(), SLOT(slotViewOps()));
0382 
0383     QPushButton *invertB = new QPushButton(this);
0384     invertB->setIcon(QIcon::fromTheme("edit-select-invert"));
0385     invertB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0386     invertB->setMaximumSize(QSize(32, 32));
0387     invertB->setMinimumSize(QSize(32, 32));
0388     invertB->setToolTip(i18n("Reverse colors of the image. This is useful to enhance contrast at times. This affects "
0389                              "only the display and not the saving."));
0390     viewControlsLayout->addWidget(invertB);
0391     connect(invertB, SIGNAL(clicked()), this, SLOT(invertColors()));
0392 
0393     QWidget *timeWidget = new QWidget(this);
0394     QHBoxLayout *timeLayout = new QHBoxLayout(timeWidget);
0395     mainLayout->addWidget(timeWidget);
0396     timeLayout->setContentsMargins(0, 0, 0, 0);
0397 
0398     m_XPlanetTime = KStarsData::Instance()->lt();
0399 
0400     QPushButton *setTime = new QPushButton(this);
0401     setTime->setIcon(QIcon::fromTheme("clock"));
0402     setTime->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0403     setTime->setMaximumSize(QSize(32, 32));
0404     setTime->setMinimumSize(QSize(32, 32));
0405     setTime->setToolTip(i18n("Allows you to set the XPlanet time to a different date/time from KStars"));
0406     timeLayout->addWidget(setTime);
0407     connect(setTime, SIGNAL(clicked()), this, SLOT(setXPlanetTime()));
0408 
0409     QPushButton *kstarsTime = new QPushButton(this);
0410     kstarsTime->setIcon(QIcon::fromTheme("system-reboot"));
0411     kstarsTime->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0412     kstarsTime->setMaximumSize(QSize(32, 32));
0413     kstarsTime->setMinimumSize(QSize(32, 32));
0414     kstarsTime->setToolTip(i18n("Sets the XPlanet time to the current KStars time"));
0415     timeLayout->addWidget(kstarsTime);
0416     connect(kstarsTime, SIGNAL(clicked()), this, SLOT(setXPlanetTimetoKStarsTime()));
0417 
0418     m_XPlanetTimeDisplay = new QLabel(this);
0419     m_XPlanetTimeDisplay->setToolTip(i18n("Current XPlanet Time"));
0420     timeLayout->addWidget(m_XPlanetTimeDisplay);
0421 
0422     m_XPlanetTimeDisplay->setText(i18n("%1, %2", m_XPlanetTime.date().toString(), m_XPlanetTime.time().toString()));
0423 
0424     m_TimeSlider = new QSlider(Qt::Horizontal, this);
0425     m_TimeSlider->setTracking(false);
0426     connect(m_TimeSlider, SIGNAL(sliderMoved(int)), this, SLOT(timeSliderDisplay(int)));
0427     timeLayout->addWidget(m_TimeSlider);
0428     m_TimeSlider->setRange(-100, 100);
0429     m_TimeSlider->setToolTip(i18n("This sets the time step from the current XPlanet time, good for viewing events"));
0430     connect(m_TimeSlider,  SIGNAL(valueChanged(int)), this, SLOT(updateXPlanetTime(int)));
0431 
0432     m_TimeEdit = new QSpinBox(this);
0433     m_TimeEdit->setRange(-10000, 10000);
0434     m_TimeEdit->setMaximumWidth(50);
0435     m_TimeEdit->setToolTip(i18n("This sets the time step from the current XPlanet time"));
0436     timeLayout->addWidget(m_TimeEdit);
0437     connect(m_TimeEdit,  SIGNAL(valueChanged(int)), this, SLOT(updateXPlanetTimeEdit()));
0438 
0439     m_CurrentTimeUnitIndex = MINS;
0440     m_TimeUnitsSelect = new QComboBox(this);
0441     timeLayout->addWidget(m_TimeUnitsSelect);
0442     m_TimeUnitsSelect->addItem(i18n("years"));
0443     m_TimeUnitsSelect->addItem(i18n("months"));
0444     m_TimeUnitsSelect->addItem(i18n("days"));
0445     m_TimeUnitsSelect->addItem(i18n("hours"));
0446     m_TimeUnitsSelect->addItem(i18n("minutes"));
0447     m_TimeUnitsSelect->addItem(i18n("seconds"));
0448     m_TimeUnitsSelect->setCurrentIndex(MINS);
0449     m_TimeUnitsSelect->setToolTip(i18n("Lets you change the units for the timestep in the animation"));
0450     connect(m_TimeUnitsSelect,  SIGNAL(currentIndexChanged(int)), this, SLOT(updateXPlanetTimeUnits(int)));
0451 
0452     m_XPlanetTimer = new QTimer(this);
0453     m_XPlanetTimer->setInterval(Options::xplanetAnimationDelay());
0454     connect(m_XPlanetTimer, SIGNAL(timeout()), this, SLOT(incrementXPlanetTime()));
0455 
0456     m_RunTime = new QPushButton(this);
0457     m_RunTime->setIcon(QIcon::fromTheme("media-playback-start"));
0458     m_RunTime->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0459     m_RunTime->setCheckable(true);
0460     m_RunTime->setMaximumSize(QSize(32, 32));
0461     m_RunTime->setMinimumSize(QSize(32, 32));
0462     m_RunTime->setToolTip(i18n("Lets you run the animation"));
0463     timeLayout->addWidget(m_RunTime);
0464     connect(m_RunTime, SIGNAL(clicked()), this, SLOT(toggleXPlanetRun()));
0465 
0466     QPushButton *resetTime = new QPushButton(this);
0467     resetTime->setIcon(QIcon::fromTheme("system-reboot"));
0468     resetTime->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0469     resetTime->setMaximumSize(QSize(32, 32));
0470     resetTime->setMinimumSize(QSize(32, 32));
0471     resetTime->setToolTip(i18n("Resets the animation to 0 timesteps from the current XPlanet Time"));
0472     timeLayout->addWidget(resetTime);
0473     connect(resetTime, SIGNAL(clicked()), this, SLOT(resetXPlanetTime()));
0474 
0475     m_View = new XPlanetImageLabel(page);
0476     m_View->setAutoFillBackground(false);
0477     m_Caption = new QLabel(page);
0478     m_Caption->setAutoFillBackground(true);
0479     m_Caption->setFrameShape(QFrame::StyledPanel);
0480     m_Caption->setText(m_ObjectName);
0481     // Add layout
0482     QVBoxLayout *vlay = new QVBoxLayout(page);
0483     vlay->setSpacing(0);
0484     vlay->setContentsMargins(0, 0, 0, 0);
0485     vlay->addWidget(m_View);
0486     vlay->addWidget(m_Caption);
0487 
0488 
0489     connect(m_View, SIGNAL(zoomIn()), this, SLOT(zoomInXPlanetFOV()));
0490     connect(m_View, SIGNAL(zoomOut()), this, SLOT(zoomOutXPlanetFOV()));
0491     connect(m_View, SIGNAL(changePosition(QPoint)), this, SLOT(changeXPlanetPosition(QPoint)));
0492     connect(m_View, SIGNAL(changeLocation(QPoint)), this, SLOT(changeXPlanetLocation(QPoint)));
0493 
0494     //Reverse colors
0495     QPalette p = palette();
0496     p.setColor(QPalette::Window, palette().color(QPalette::WindowText));
0497     p.setColor(QPalette::WindowText, palette().color(QPalette::Window));
0498     m_Caption->setPalette(p);
0499     m_View->setPalette(p);
0500 
0501 #ifndef Q_OS_WIN
0502     if(Options::xplanetUseFIFO())
0503     {
0504         connect(&watcherTimeout, SIGNAL(timeout()), &fifoImageLoadWatcher, SLOT(cancel()));
0505         connect(&fifoImageLoadWatcher, SIGNAL(finished()), this, SLOT(showImage()));
0506     }
0507 #endif
0508 
0509 
0510 #ifdef Q_OS_OSX
0511     QList<QPushButton *> qButtons = findChildren<QPushButton *>();
0512     for (auto &button : qButtons)
0513         button->setAutoDefault(false);
0514 #endif
0515     updateXPlanetTime(0);
0516 #endif
0517 }
0518 
0519 XPlanetImageViewer::~XPlanetImageViewer()
0520 {
0521     QApplication::restoreOverrideCursor();
0522 }
0523 
0524 void XPlanetImageViewer::startXplanet()
0525 {
0526     if(m_XPlanetRunning)
0527         return;
0528 
0529     //This means something failed in the file output
0530     if(!setupOutputFile())
0531         return;
0532 
0533     QString xPlanetLocation = Options::xplanetPath();
0534 #ifdef Q_OS_OSX
0535     if (Options::xplanetIsInternal())
0536         xPlanetLocation   = QCoreApplication::applicationDirPath() + QDir::separator() + "xplanet";
0537 #endif
0538 
0539     // If Options::xplanetPath() is empty, return
0540     if (xPlanetLocation.isEmpty())
0541     {
0542         KSNotification::error(i18n("Xplanet binary path is empty in config panel."));
0543         return;
0544     }
0545 
0546     // If Options::xplanetPath() does not exist, return
0547     const QFileInfo xPlanetLocationInfo(xPlanetLocation);
0548     if (!xPlanetLocationInfo.exists() || !xPlanetLocationInfo.isExecutable())
0549     {
0550         KSNotification::error(i18n("The configured Xplanet binary does not exist or is not executable."));
0551         return;
0552     }
0553 
0554     // Create xplanet process
0555     QProcess *xplanetProc = new QProcess(this);
0556 
0557     // Add some options
0558     QStringList args;
0559 
0560     //This specifies the object to be viewed
0561     args << "-body" << m_ObjectName.toLower();
0562     //This is the date and time requested
0563     args << "-date" << m_Date;
0564     //This is the glare from the sun
0565     args << "-glare" << Options::xplanetGlare();
0566     args << "-base_magnitude" << Options::xplanetMagnitude();
0567     //This is the correction for light's travel time.
0568     args << "-light_time";
0569 
0570     args << "-geometry" << QString::number(Options::xplanetWidth()) + 'x' + QString::number(Options::xplanetHeight());
0571 
0572     if(m_FOV != 0)
0573         args << "-fov" << QString::number(m_FOV);
0574     //Need to convert to locale for places that don't use decimals??
0575     //args << "-fov" << fov.setNum(fov());//.replace('.', ',');
0576 
0577     //This rotates the view for different object angles
0578     args << "-rotate" << QString::number(m_Rotation);
0579 
0580     if (Options::xplanetConfigFile())
0581         args << "-config" << Options::xplanetConfigFilePath();
0582     if (Options::xplanetStarmap())
0583         args << "-starmap" << Options::xplanetStarmapPath();
0584     if (Options::xplanetArcFile())
0585         args << "-arc_file" << Options::xplanetArcFilePath();
0586     if (!m_File.fileName().isEmpty())
0587         args << "-output" << m_File.fileName() << "-quality" << Options::xplanetQuality();
0588 
0589     // Labels
0590     if (Options::xplanetLabel())
0591     {
0592         args << "-fontsize" << Options::xplanetFontSize() << "-color"
0593              << "0x" + Options::xplanetColor().mid(1) << "-date_format" << Options::xplanetDateFormat();
0594 
0595         if (Options::xplanetLabelGMT())
0596             args << "-gmtlabel";
0597         else
0598             args << "-label";
0599         if (!Options::xplanetLabelString().isEmpty())
0600             args << "-label_string"
0601                  << "\"" + Options::xplanetLabelString() + "\"";
0602         if (Options::xplanetLabelTL())
0603             args << "-labelpos"
0604                  << "+15+15";
0605         else if (Options::xplanetLabelTR())
0606             args << "-labelpos"
0607                  << "-15+15";
0608         else if (Options::xplanetLabelBR())
0609             args << "-labelpos"
0610                  << "-15-15";
0611         else if (Options::xplanetLabelBL())
0612             args << "-labelpos"
0613                  << "+15-15";
0614     }
0615 
0616     // Markers
0617     if (Options::xplanetMarkerFile())
0618         args << "-marker_file" << Options::xplanetMarkerFilePath();
0619     if (Options::xplanetMarkerBounds())
0620         args << "-markerbounds" << Options::xplanetMarkerBoundsPath();
0621 
0622     // Position
0623     // This sets the position from which the planet is viewed.
0624     // Note that setting Latitude and Longitude means that position above the SAME object
0625 
0626     if(m_CurrentObjectIndex == m_CurrentOriginIndex)
0627     {
0628         if (Options::xplanetRandom())
0629             args << "-random";
0630         else
0631             args << "-latitude" << QString::number(m_lat) << "-longitude" << QString::number(m_lon) << "-radius" << QString::number(m_Radius);
0632     }
0633     else
0634         args << "-origin" << m_OriginName;
0635 
0636     //Centering
0637     //This allows you to recenter the xplanet view
0638 
0639     args << "-center" << "+" + QString::number(Options::xplanetWidth() / 2 + center.x()) + "+" + QString::number(Options::xplanetHeight() / 2 + center.y());
0640 
0641     // Projection
0642     if (Options::xplanetProjection())
0643     {
0644         switch (Options::xplanetProjection())
0645         {
0646             case 1:
0647                 args << "-projection"
0648                      << "ancient";
0649                 break;
0650             case 2:
0651                 args << "-projection"
0652                      << "azimuthal";
0653                 break;
0654             case 3:
0655                 args << "-projection"
0656                      << "bonne";
0657                 break;
0658             case 4:
0659                 args << "-projection"
0660                      << "gnomonic";
0661                 break;
0662             case 5:
0663                 args << "-projection"
0664                      << "hemisphere";
0665                 break;
0666             case 6:
0667                 args << "-projection"
0668                      << "lambert";
0669                 break;
0670             case 7:
0671                 args << "-projection"
0672                      << "mercator";
0673                 break;
0674             case 8:
0675                 args << "-projection"
0676                      << "mollweide";
0677                 break;
0678             case 9:
0679                 args << "-projection"
0680                      << "orthographic";
0681                 break;
0682             case 10:
0683                 args << "-projection"
0684                      << "peters";
0685                 break;
0686             case 11:
0687                 args << "-projection"
0688                      << "polyconic";
0689                 break;
0690             case 12:
0691                 args << "-projection"
0692                      << "rectangular";
0693                 break;
0694             case 13:
0695                 args << "-projection"
0696                      << "tsc";
0697                 break;
0698             default:
0699                 break;
0700         }
0701         if (Options::xplanetBackground())
0702         {
0703             if (Options::xplanetBackgroundImage())
0704                 args << "-background" << Options::xplanetBackgroundImagePath();
0705             else
0706                 args << "-background"
0707                      << "0x" + Options::xplanetBackgroundColorValue().mid(1);
0708         }
0709     }
0710 
0711 #ifdef Q_OS_OSX
0712     QString searchDir = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory) + QDir::separator() + "xplanet";
0713     args << "-searchdir" << searchDir;
0714 #endif
0715 
0716 #ifdef Q_OS_WIN
0717     QString searchDir = xPlanetLocationInfo.dir().absolutePath() + QDir::separator() + "xplanet";
0718     args << "-searchdir" << searchDir;
0719 #endif
0720 
0721     //This prevents it from running forever.
0722     args << "-num_times" << "1";
0723 
0724     m_XPlanetRunning = true;
0725     m_ImageLoadSucceeded = false; //This will be set to true if it works.
0726     uint32_t timeout = Options::xplanetTimeout();
0727 
0728     //FIFO files don't work on windows
0729 #ifndef Q_OS_WIN
0730     if(Options::xplanetUseFIFO())
0731     {
0732         fifoImageLoadWatcher.setFuture(QtConcurrent::run(this, &XPlanetImageViewer::loadImage));
0733         watcherTimeout.setSingleShot(true);
0734         watcherTimeout.start(timeout);
0735     }
0736 #endif
0737 
0738     xplanetProc->start(xPlanetLocation, args);
0739 
0740     //Uncomment to print the XPlanet commands to the console
0741     // qDebug() << Q_FUNC_INFO << "Run:" << xplanetProc->program() << args.join(" ");
0742 
0743     bool XPlanetSucceeded = xplanetProc->waitForFinished(timeout);
0744     m_XPlanetRunning = false;
0745     xplanetProc->kill(); //In case it timed out
0746     xplanetProc->deleteLater();
0747     if(XPlanetSucceeded)
0748     {
0749         if(m_FOV == 0)
0750             m_Caption->setText(i18n("XPlanet View: %1 from %2 on %3", m_ObjectName, m_OriginName, m_DateText));
0751         else
0752             m_Caption->setText(i18n("XPlanet View: %1 from %2 on %3 at FOV: %4 deg", m_ObjectName, m_OriginName, m_DateText, m_FOV));
0753 #ifdef Q_OS_WIN
0754         loadImage(); //This will also set imageLoadSucceeded based on whether it worked.
0755 #else
0756         if(Options::xplanetUseFIFO())
0757             return;  //The loading of the image is handled with the watcher
0758         else
0759             loadImage(); //This will also set imageLoadSucceeded based on whether it worked.
0760 #endif
0761 
0762         if(m_ImageLoadSucceeded)
0763             showImage();
0764         else
0765         {
0766             KSNotification::error(i18n("Loading of the image of object %1 failed.", m_ObjectName));
0767         }
0768     }
0769     else
0770     {
0771         KStars::Instance()->statusBar()->showMessage(i18n("XPlanet failed to generate the image for object %1 before the timeout expired.", m_ObjectName));
0772 #ifndef Q_OS_WIN
0773         if(Options::xplanetUseFIFO())
0774             fifoImageLoadWatcher.cancel();
0775 #endif
0776     }
0777 }
0778 
0779 bool XPlanetImageViewer::setupOutputFile()
0780 {
0781 #ifndef Q_OS_WIN
0782     if(Options::xplanetUseFIFO())
0783     {
0784         if(m_File.fileName().contains("xplanetfifo") && m_File.exists())
0785             return true;
0786         QDir kstarsTempDir(KSPaths::writableLocation(QStandardPaths::TempLocation) + QDir::separator() + qAppName());
0787         kstarsTempDir.mkpath(".");
0788         m_File.setFileName(kstarsTempDir.filePath(QString("xplanetfifo%1.png").arg(QUuid::createUuid().toString().mid(1, 8)).toLatin1()));
0789         if (mkfifo(m_File.fileName().toLatin1(), S_IRUSR | S_IWUSR) < 0)
0790         {
0791             KSNotification::error(i18n("Error making FIFO file %1: %2.", m_File.fileName(), strerror(errno)));
0792             return false;
0793         }
0794         return true;
0795     }
0796 #endif
0797 
0798     //If the user is using windows or has not selected to use FIFO, it uses files in the KStars data directory.
0799     QDir xPlanetDirPath(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + QDir::separator() + "xplanet");
0800     xPlanetDirPath.mkpath(".");
0801     m_File.setFileName(xPlanetDirPath.filePath(m_ObjectName + ".png"));
0802     return true;
0803 }
0804 
0805 void XPlanetImageViewer::zoomInXPlanetFOV()
0806 {
0807     if(m_CurrentObjectIndex == m_CurrentOriginIndex)
0808     {
0809         m_Radius += 5;
0810         updatePositionDisplay();
0811         startXplanet();
0812     }
0813     else
0814     {
0815         m_FOVEdit->stepDown();
0816         startXplanet();
0817     }
0818 
0819 }
0820 
0821 void XPlanetImageViewer::zoomOutXPlanetFOV()
0822 {
0823     if(m_CurrentObjectIndex == m_CurrentOriginIndex)
0824     {
0825         if(m_Radius > 0)
0826         {
0827             m_Radius -= 5;
0828             updatePositionDisplay();
0829             startXplanet();
0830         }
0831     }
0832     else
0833     {
0834         m_FOVEdit->stepUp();
0835         startXplanet();
0836     }
0837 
0838 }
0839 
0840 void XPlanetImageViewer::updatePositionDisplay()
0841 {
0842     m_PositionDisplay->setText(i18n("%1, %2, %3", QString::number(m_lat), QString::number(m_lon), QString::number(m_Radius)));
0843 }
0844 
0845 void XPlanetImageViewer::updateXPlanetTime(int timeShift)
0846 {
0847 
0848     KStarsDateTime shiftedXPlanetTime;
0849     switch(m_CurrentTimeUnitIndex)
0850     {
0851         case YEARS:
0852             shiftedXPlanetTime = m_XPlanetTime.addDays(timeShift * 365);
0853             break;
0854 
0855         case MONTHS:
0856             shiftedXPlanetTime = m_XPlanetTime.addDays(timeShift * 30);
0857             break;
0858 
0859         case DAYS:
0860             shiftedXPlanetTime = m_XPlanetTime.addDays(timeShift);
0861             break;
0862 
0863         case HOURS:
0864             shiftedXPlanetTime = m_XPlanetTime.addSecs(timeShift * 3600);
0865             break;
0866 
0867         case MINS:
0868             shiftedXPlanetTime = m_XPlanetTime.addSecs(timeShift * 60);
0869             break;
0870 
0871         case SECS:
0872             shiftedXPlanetTime = m_XPlanetTime.addSecs(timeShift);
0873             break;
0874     }
0875 
0876     setXPlanetDate(shiftedXPlanetTime);
0877     m_DateText = i18n("%1, %2", shiftedXPlanetTime.date().toString(), shiftedXPlanetTime.time().toString());
0878     if(m_TimeEdit->value() != timeShift)
0879         m_TimeEdit->setValue(timeShift);
0880     else
0881         startXplanet();
0882 }
0883 
0884 void XPlanetImageViewer::updateXPlanetObject(int objectIndex)
0885 {
0886     center = QPoint(0, 0);
0887     m_CurrentObjectIndex = objectIndex;
0888     m_ObjectName = m_ObjectNames.at(objectIndex);
0889 
0890     setWindowTitle(i18nc("@title:window", "XPlanet Solar System Simulator: %1", m_ObjectName));
0891     if(m_FreeRotate->isChecked())
0892         m_OriginSelector->setCurrentIndex(m_CurrentObjectIndex);
0893     if(m_CurrentObjectIndex == m_CurrentOriginIndex)
0894         startXplanet();
0895     else
0896         resetXPlanetFOV();
0897 }
0898 
0899 void XPlanetImageViewer::updateXPlanetOrigin(int originIndex)
0900 {
0901     center = QPoint(0, 0);
0902     m_CurrentOriginIndex = originIndex;
0903     m_OriginName = m_ObjectNames.at(originIndex);
0904     if(m_CurrentObjectIndex == m_CurrentOriginIndex)
0905         m_FreeRotate->setChecked(true);
0906     else
0907         m_FreeRotate->setChecked(false);
0908     updateStates();//This will update the disabled/enabled states
0909     startXplanet();
0910 }
0911 
0912 void XPlanetImageViewer::changeXPlanetLocation(QPoint delta)
0913 {
0914     if(m_CurrentObjectIndex == m_CurrentOriginIndex)
0915     {
0916         double newLon = m_lon + delta.x();
0917         double newLat = m_lat + delta.y();
0918 
0919         newLat = qBound(-90.0, newLat, 90.0);
0920 
0921         m_lon = newLon;
0922         m_lat = newLat;
0923 
0924         updatePositionDisplay();
0925         startXplanet();
0926     }
0927 }
0928 
0929 void XPlanetImageViewer::changeXPlanetPosition(QPoint delta)
0930 {
0931     center.setX(center.x() + delta.x());
0932     center.setY(center.y() + delta.y());
0933     startXplanet();
0934 }
0935 
0936 void XPlanetImageViewer::reCenterXPlanet()
0937 {
0938     center = QPoint(0, 0);
0939     startXplanet();
0940 }
0941 
0942 void XPlanetImageViewer::resetLocation()
0943 {
0944     m_lat = Options::xplanetLatitude().toDouble();
0945     m_lon = Options::xplanetLongitude().toDouble();
0946     m_Radius = 45;
0947     updatePositionDisplay();
0948     startXplanet();
0949 }
0950 
0951 void XPlanetImageViewer::slotFreeRotate()
0952 {
0953     if(m_FreeRotate->isChecked())
0954         m_OriginSelector->setCurrentIndex(m_CurrentObjectIndex);
0955     else
0956         m_OriginSelector->setCurrentIndex(EARTH);
0957 }
0958 
0959 void XPlanetImageViewer::updateStates()
0960 {
0961     if(m_FreeRotate->isChecked())
0962     {
0963         m_FOVEdit->setDisabled(true);
0964         m_KStarsFOV->setDisabled(true);
0965         m_NoFOV->setDisabled(true);
0966 
0967         m_PositionDisplay->setDisabled(false);
0968     }
0969     else
0970     {
0971         m_FOVEdit->setDisabled(false);
0972         m_KStarsFOV->setDisabled(false);
0973         m_NoFOV->setDisabled(false);
0974 
0975         m_PositionDisplay->setDisabled(true);
0976     }
0977 }
0978 
0979 void XPlanetImageViewer::setXPlanetDate(KStarsDateTime time)
0980 {
0981     //Note Xplanet uses UT time for everything but we want the labels to all be LT
0982     KStarsDateTime utTime = KStarsData::Instance()->geo()->LTtoUT(time);
0983     m_Date = utTime.toString(Qt::ISODate)
0984              .replace("-", QString(""))
0985              .replace("T", ".")
0986              .replace(":", QString(""))
0987              .replace("Z", QString(""));
0988 }
0989 
0990 void XPlanetImageViewer::updateXPlanetTimeUnits(int units)
0991 {
0992     m_CurrentTimeUnitIndex = units;
0993     updateXPlanetTimeEdit();
0994 }
0995 
0996 void XPlanetImageViewer::updateXPlanetTimeEdit()
0997 {
0998     if(m_TimeSlider->isSliderDown())
0999         return;
1000     int timeShift = m_TimeEdit->value();
1001     if(m_TimeSlider->value() != timeShift)
1002     {
1003 
1004         if(timeShift > m_TimeSlider->maximum() + 100)
1005             m_TimeSlider->setMaximum(timeShift);
1006         if(timeShift < m_TimeSlider->minimum() - 100)
1007             m_TimeSlider->setMinimum(timeShift);
1008         m_TimeSlider->setValue(timeShift);
1009     }
1010     else
1011         updateXPlanetTime(timeShift);
1012 }
1013 
1014 void XPlanetImageViewer::timeSliderDisplay(int timeShift)
1015 {
1016     m_TimeEdit->setValue(timeShift);
1017 }
1018 
1019 void XPlanetImageViewer::incrementXPlanetTime()
1020 {
1021     if(!m_XPlanetRunning)
1022     {
1023         int timeShift = m_TimeEdit->value();
1024         if(m_TimeSlider->maximum() <= timeShift)
1025             m_TimeSlider->setMaximum(timeShift + 100);
1026         if(m_TimeEdit->maximum() <= timeShift)
1027             m_TimeEdit->setMaximum(timeShift + 100);
1028         m_TimeSlider->setValue(timeShift + 1);
1029     }
1030 }
1031 
1032 void XPlanetImageViewer::setXPlanetTime()
1033 {
1034     QPointer<TimeDialog> timedialog = new TimeDialog(m_XPlanetTime, KStarsData::Instance()->geo(), this);
1035     if (timedialog->exec() == QDialog::Accepted)
1036     {
1037         m_XPlanetTime = timedialog->selectedDateTime();
1038         m_XPlanetTimeDisplay->setText(i18n("%1, %2", m_XPlanetTime.date().toString(), m_XPlanetTime.time().toString()));
1039         int timeShift = 0;
1040         m_TimeSlider->setRange(-100, 100);
1041         if(m_TimeSlider->value() != timeShift)
1042             m_TimeSlider->setValue(timeShift);
1043         else
1044             updateXPlanetTime(timeShift);
1045     }
1046 }
1047 
1048 void XPlanetImageViewer::setXPlanetTimetoKStarsTime()
1049 {
1050     m_XPlanetTime = KStarsData::Instance()->lt();
1051     m_XPlanetTimeDisplay->setText(i18n("%1, %2", m_XPlanetTime.date().toString(), m_XPlanetTime.time().toString()));
1052     int timeShift = 0;
1053     m_TimeSlider->setRange(-100, 100);
1054     if(m_TimeSlider->value() != timeShift)
1055         m_TimeSlider->setValue(timeShift);
1056     else
1057         updateXPlanetTime(timeShift);
1058 }
1059 
1060 void XPlanetImageViewer::resetXPlanetTime()
1061 {
1062     int timeShift = 0;
1063     m_TimeSlider->setRange(-100, 100);
1064     if(m_TimeSlider->value() != timeShift)
1065         m_TimeSlider->setValue(timeShift);
1066     else
1067         updateXPlanetTime(timeShift);
1068 }
1069 
1070 void XPlanetImageViewer::toggleXPlanetRun()
1071 {
1072     if(m_XPlanetTimer->isActive())
1073     {
1074         m_XPlanetTimer->stop();
1075 #ifndef Q_OS_WIN
1076         if(Options::xplanetUseFIFO())
1077             fifoImageLoadWatcher.cancel();
1078 #endif
1079     }
1080     else
1081     {
1082         m_XPlanetTimer->setInterval(Options::xplanetAnimationDelay());
1083         m_XPlanetTimer->start();
1084     }
1085 }
1086 
1087 void XPlanetImageViewer::updateXPlanetFOVEdit()
1088 {
1089     m_FOV = m_FOVEdit->value();
1090     startXplanet();
1091 }
1092 
1093 void XPlanetImageViewer::resetXPlanetFOV()
1094 {
1095     m_FOV = m_objectDefaultFOVs.at(m_CurrentObjectIndex);
1096     m_FOVEdit->setValue(m_FOV);
1097     startXplanet();
1098 }
1099 
1100 void XPlanetImageViewer::setKStarsXPlanetFOV()
1101 {
1102     m_FOV = KStars::Instance()->map()->fov();
1103     m_FOVEdit->setValue(m_FOV);
1104     startXplanet();
1105 }
1106 void XPlanetImageViewer::setFOVfromList()
1107 {
1108     if (!KStarsData::Instance()->getAvailableFOVs().isEmpty())
1109     {
1110         // Ask the user to choose from a list of available FOVs.
1111         QMap<QString, const FOV *> nameToFovMap;
1112         for (const FOV *f : KStarsData::Instance()->getAvailableFOVs())
1113         {
1114             nameToFovMap.insert(f->name(), f);
1115         }
1116         bool ok = false;
1117         const FOV *fov = nullptr;
1118         fov = nameToFovMap[QInputDialog::getItem(this, i18n("Choose a field-of-view"),
1119                                                        i18n("FOV to render in XPlanet:"), nameToFovMap.keys(), 0,
1120                                                        false, &ok)];
1121         if (ok)
1122         {
1123             m_FOV = fov->sizeX() / 60 ; //Converting from arcmin to degrees
1124             m_FOVEdit->setValue(m_FOV);
1125             startXplanet();
1126         }
1127     }
1128 }
1129 
1130 void XPlanetImageViewer::updateXPlanetRotationEdit()
1131 {
1132     m_Rotation = m_RotateEdit->value();
1133     startXplanet();
1134 }
1135 
1136 void XPlanetImageViewer::resetXPlanetRotation()
1137 {
1138     m_RotateEdit->setValue(0);
1139 }
1140 
1141 void XPlanetImageViewer::invertXPlanetRotation()
1142 {
1143     m_RotateEdit->setValue(180);
1144 }
1145 
1146 bool XPlanetImageViewer::loadImage()
1147 {
1148 #ifndef KSTARS_LITE
1149 
1150     if (!m_Image.load(m_File.fileName()))
1151     {
1152         m_ImageLoadSucceeded = false;
1153         return false;
1154     }
1155     m_ImageLoadSucceeded = true;
1156     return true;
1157 #else
1158     imageLoadSucceeded = false;
1159     return false;
1160 #endif
1161 }
1162 
1163 bool XPlanetImageViewer::showImage()
1164 {
1165 #ifndef KSTARS_LITE
1166 
1167     //If the image is larger than screen width and/or screen height,
1168     //shrink it to fit the screen
1169     QRect deskRect = QGuiApplication::primaryScreen()->geometry();
1170     int w          = deskRect.width();  // screen width
1171     int h          = deskRect.height(); // screen height
1172 
1173     if (m_Image.width() <= w && m_Image.height() > h) //Window is taller than desktop
1174         m_Image = m_Image.scaled(int(m_Image.width() * h / m_Image.height()), h);
1175     else if (m_Image.height() <= h && m_Image.width() > w) //window is wider than desktop
1176         m_Image = m_Image.scaled(w, int(m_Image.height() * w / m_Image.width()));
1177     else if (m_Image.width() > w && m_Image.height() > h) //window is too tall and too wide
1178     {
1179         //which needs to be shrunk least, width or height?
1180         float fx = float(w) / float(m_Image.width());
1181         float fy = float(h) / float(m_Image.height());
1182         if (fx > fy) //width needs to be shrunk less, so shrink to fit in height
1183             m_Image = m_Image.scaled(int(m_Image.width() * fy), h);
1184         else //vice versa
1185             m_Image = m_Image.scaled(w, int(m_Image.height() * fx));
1186     }
1187     const bool initialLoad = !isVisible();
1188 
1189     show(); // hide is default
1190 
1191     m_View->setImage(m_Image);
1192     w = m_Image.width();
1193 
1194     //If the caption is wider than the image, set the window size
1195     //to fit the caption
1196     if (m_Caption->width() > w)
1197         w = m_Caption->width();
1198     if(initialLoad)
1199         resize(w, m_Image.height());
1200     else
1201     {
1202         m_View->refreshImage();
1203     }
1204 
1205     update();
1206     show();
1207 
1208     return true;
1209 #else
1210     return false;
1211 #endif
1212 }
1213 
1214 void XPlanetImageViewer::saveFileToDisk()
1215 {
1216 #ifndef KSTARS_LITE
1217     QFileDialog saveDialog(KStars::Instance(), i18nc("@title:window", "Save Image"), m_LastFile);
1218     saveDialog.setDefaultSuffix("png");
1219     saveDialog.setAcceptMode(QFileDialog::AcceptSave);
1220     saveDialog.exec();
1221 
1222     if(saveDialog.result() == QFileDialog::Rejected)
1223         return;
1224     if(saveDialog.selectedFiles().isEmpty())
1225         return;
1226     QString newFileName = saveDialog.selectedFiles().first();
1227     if(newFileName.isEmpty())
1228         return;
1229 
1230     m_LastFile = newFileName;
1231 
1232     saveFile(newFileName);
1233 
1234 #endif
1235 }
1236 
1237 void XPlanetImageViewer::saveFile(const QString &fileName)
1238 {
1239 #ifndef KSTARS_LITE
1240 
1241     if (! m_Image.save(fileName, "png"))
1242     {
1243         KSNotification::error(i18n("Saving of the image to %1 failed.", fileName));
1244     }
1245     else
1246         KStars::Instance()->statusBar()->showMessage(i18n("Saved image to %1", fileName));
1247 #endif
1248 }
1249 
1250 void XPlanetImageViewer::invertColors()
1251 {
1252 #ifndef KSTARS_LITE
1253     // Invert colors
1254     m_View->invertPixels();
1255     m_View->update();
1256 #endif
1257 }
1258