1 /*
2 SPDX-FileCopyrightText: Thomas Kabelmann
3 SPDX-FileCopyrightText: 2018 Robert Lancaster <rlancaste@gmail.com>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "xplanetimageviewer.h"
9 #include "Options.h"
10 #include "dialogs/timedialog.h"
11 #include "ksnotification.h"
12
13 #include <QtConcurrent>
14
15 #ifndef KSTARS_LITE
16 #include "kstars.h"
17 #endif
18
19 #ifndef KSTARS_LITE
20 #include <KMessageBox>
21 #endif
22
23 #include <QFileDialog>
24 #include <QPainter>
25 #include <QResizeEvent>
26 #include <QStatusBar>
27 #include <QTemporaryFile>
28 #include <QVBoxLayout>
29 #include <QPushButton>
30 #include <QApplication>
31 #include <QScreen>
32 #include <QSlider>
33 #include "skymap.h"
34 #include "kspaths.h"
35 #include "fov.h"
36
37 #include <QUuid>
38 #include <sys/stat.h>
39 #include <QInputDialog>
40
41 typedef enum
42 {
43 SUN, MERCURY, VENUS,
44 EARTH, MOON,
45 MARS, PHOBOS, DEIMOS,
46 JUPITER, GANYMEDE, IO, CALLISTO, EUROPA,
47 SATURN, TITAN, MIMAS, ENCELADUS, TETHYS, DIONE, RHEA, HYPERION, IAPETUS, PHOEBE,
48 URANUS, UMBRIEL, ARIEL, MIRANDA, TITANIA, OBERON,
49 NEPTUNE, TRITON
50 } objects;
51
XPlanetImageLabel(QWidget * parent)52 XPlanetImageLabel::XPlanetImageLabel(QWidget *parent) : QFrame(parent)
53 {
54 #ifndef KSTARS_LITE
55 grabGesture(Qt::PinchGesture);
56 setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
57 setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
58 setLineWidth(2);
59 #endif
60 }
61
setImage(const QImage & img)62 void XPlanetImageLabel::setImage(const QImage &img)
63 {
64 #ifndef KSTARS_LITE
65 m_Image = img;
66 m_Pix = QPixmap::fromImage(m_Image);
67 #endif
68 }
69
invertPixels()70 void XPlanetImageLabel::invertPixels()
71 {
72 #ifndef KSTARS_LITE
73 m_Image.invertPixels();
74 m_Pix = QPixmap::fromImage(m_Image.scaled(width(), height(), Qt::KeepAspectRatio));
75 #endif
76 }
77
paintEvent(QPaintEvent *)78 void XPlanetImageLabel::paintEvent(QPaintEvent *)
79 {
80 #ifndef KSTARS_LITE
81 QPainter p;
82 p.begin(this);
83 int x = 0;
84 if (m_Pix.width() < width())
85 x = (width() - m_Pix.width()) / 2;
86 p.drawPixmap(x, 0, m_Pix);
87 p.end();
88 #endif
89 }
90
resizeEvent(QResizeEvent * event)91 void XPlanetImageLabel::resizeEvent(QResizeEvent *event)
92 {
93 if (event->size() == m_Pix.size())
94 return;
95
96 m_Pix = QPixmap::fromImage(m_Image.scaled(event->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
97 }
98
refreshImage()99 void XPlanetImageLabel::refreshImage()
100 {
101 m_Pix = QPixmap::fromImage(m_Image.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
102 update();
103 }
104
wheelEvent(QWheelEvent * e)105 void XPlanetImageLabel::wheelEvent(QWheelEvent *e)
106 {
107 //This attempts to send the wheel event back to the Scroll Area if it was taken from a trackpad
108 //It should still do the zoom if it is a mouse wheel
109 if (e->source() == Qt::MouseEventSynthesizedBySystem)
110 {
111 QFrame::wheelEvent(e);
112 }
113 else
114 {
115 if (e->delta() > 0)
116 emit zoomIn();
117 else if (e->delta() < 0)
118 emit zoomOut();
119 e->accept();
120 }
121 }
122
event(QEvent * event)123 bool XPlanetImageLabel::event(QEvent *event)
124 {
125 if (event->type() == QEvent::Gesture)
126 return gestureEvent(dynamic_cast<QGestureEvent *>(event));
127 return QFrame::event(event);
128 }
129
gestureEvent(QGestureEvent * event)130 bool XPlanetImageLabel::gestureEvent(QGestureEvent *event)
131 {
132 if (QGesture *pinch = event->gesture(Qt::PinchGesture))
133 pinchTriggered(dynamic_cast<QPinchGesture *>(pinch));
134 return true;
135 }
136
137
pinchTriggered(QPinchGesture * gesture)138 void XPlanetImageLabel::pinchTriggered(QPinchGesture *gesture)
139 {
140 if (gesture->totalScaleFactor() > 1)
141 emit zoomIn();
142 else
143 emit zoomOut();
144 }
145
146
mousePressEvent(QMouseEvent * e)147 void XPlanetImageLabel::mousePressEvent(QMouseEvent *e)
148 {
149 m_MouseButtonDown = true;
150 m_LastMousePoint = e->globalPos();
151 e->accept();
152 }
153
mouseReleaseEvent(QMouseEvent * e)154 void XPlanetImageLabel::mouseReleaseEvent(QMouseEvent *e)
155 {
156 m_MouseButtonDown = false;
157 e->accept();
158 }
159
mouseMoveEvent(QMouseEvent * e)160 void XPlanetImageLabel::mouseMoveEvent(QMouseEvent *e)
161 {
162 if(m_MouseButtonDown)
163 {
164 QPoint newPoint = e->globalPos();
165 int dx = newPoint.x() - m_LastMousePoint.x();
166 int dy = newPoint.y() - m_LastMousePoint.y();
167 if(e->buttons() & Qt::RightButton)
168 emit changeLocation(QPoint(dx, dy));
169 if(e->buttons() & Qt::LeftButton)
170 emit changePosition(QPoint(dx, dy));
171 m_LastMousePoint = newPoint;
172 }
173 e->accept();
174 }
175
XPlanetImageViewer(const QString & obj,QWidget * parent)176 XPlanetImageViewer::XPlanetImageViewer(const QString &obj, QWidget *parent): QDialog(parent)
177 {
178 #ifndef KSTARS_LITE
179 m_LastFile = QDir::homePath();
180
181 #ifdef Q_OS_OSX
182 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
183 #endif
184 setAttribute(Qt::WA_DeleteOnClose, true);
185 setModal(false);
186 setWindowTitle(i18nc("@title:window", "XPlanet Solar System Simulator: %1", obj));
187
188 setXPlanetDate(KStarsData::Instance()->ut());
189
190 // Create widget
191 QFrame *page = new QFrame(this);
192
193 //setMainWidget( page );
194 QVBoxLayout *mainLayout = new QVBoxLayout(this);
195 mainLayout->addWidget(page);
196 setLayout(mainLayout);
197
198 QWidget *selectorsWidget = new QWidget(this);
199 QHBoxLayout *selectorsLayout = new QHBoxLayout(selectorsWidget);
200 selectorsLayout->setContentsMargins(0, 0, 0, 0);
201 mainLayout->addWidget(selectorsWidget);
202
203 m_ObjectNames << i18n("Sun") << i18n("Mercury") << i18n("Venus");
204 m_objectDefaultFOVs << 0.74818 << 0.004 << 0.02;
205 m_ObjectNames << i18n("Earth") << i18n("Moon");
206 m_objectDefaultFOVs << 1.0 << 0.74818;
207 m_ObjectNames << i18n("Mars") << i18n("Phobos") << i18n("Deimos");
208 m_objectDefaultFOVs << 0.00865 << 0.00002 << 0.00002;
209 m_ObjectNames << i18n("Jupiter") << i18n("Ganymede") << i18n("Io") << i18n("Callisto") << i18n("Europa");
210 m_objectDefaultFOVs << 0.02 << 0.0005 << 0.0004 << 0.0005 << 0.0003;
211 m_ObjectNames << i18n("Saturn") << i18n("Titan") << i18n("Mimas") << i18n("Enceladus") << i18n("Tethys") << i18n("Dione") << i18n("Rhea") << i18n("Hyperion") << i18n("Iapetus") << i18n("Phoebe");
212 m_objectDefaultFOVs << 0.02 << 0.0003 << 0.00002 << 0.00003 << 0.00007 << 0.00007 << 0.0001 << 0.00002 << 0.0001 << 0.00002;
213 m_ObjectNames << i18n("Uranus") << i18n("Umbriel") << i18n("Ariel") << i18n("Miranda") << i18n("Titania") << i18n("Oberon");
214 m_objectDefaultFOVs << 0.00256 << 0.00004 << 0.00004 << 0.00002 << 0.00005 << 0.00005;
215 m_ObjectNames << i18n("Neptune") << i18n("Triton");
216 m_objectDefaultFOVs << 0.00114 << 0.0001;
217
218 m_CurrentObjectIndex = m_ObjectNames.indexOf(obj);
219 if (m_CurrentObjectIndex < 0)
220 // Set to Saturn if current object is not in the list.
221 m_CurrentObjectIndex = 13;
222 m_ObjectName = m_ObjectNames.at(m_CurrentObjectIndex);
223
224 QComboBox *objectSelector = new QComboBox(this);
225 objectSelector->addItems(m_ObjectNames);
226 objectSelector->setToolTip(i18n("This allows you to select a new object/target for XPlanet to view"));
227 selectorsLayout->addWidget(objectSelector);
228 objectSelector->setCurrentIndex(m_CurrentObjectIndex);
229 connect(objectSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(updateXPlanetObject(int)));
230
231 m_CurrentOriginIndex = EARTH;
232 m_OriginName = m_ObjectNames.at(EARTH);
233
234 selectorsLayout->addWidget(new QLabel(i18n("from"), this));
235 m_OriginSelector = new QComboBox(this);
236 m_OriginSelector->addItems(m_ObjectNames);
237 m_OriginSelector->setToolTip(i18n("This allows you to select a viewing location"));
238 selectorsLayout->addWidget(m_OriginSelector);
239 m_OriginSelector->setCurrentIndex(EARTH);
240 connect(m_OriginSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(updateXPlanetOrigin(int)));
241
242 m_lat = Options::xplanetLatitude().toDouble();
243 m_lon = Options::xplanetLongitude().toDouble();
244 m_Radius = 45;
245
246 selectorsLayout->addWidget(new QLabel(i18n("Location:"), this));
247
248 m_PositionDisplay = new QLabel(this);
249 m_PositionDisplay->setToolTip(i18n("XPlanet Latitude, Longitude, and object radius in %. This is only valid when viewing the object from the same object"));
250 updatePositionDisplay();
251 m_PositionDisplay->setDisabled(true);
252 selectorsLayout->addWidget(m_PositionDisplay);
253
254 QPushButton *resetXPlanetLocation = new QPushButton(this);
255 resetXPlanetLocation->setIcon(QIcon::fromTheme("system-reboot"));
256 resetXPlanetLocation->setAttribute(Qt::WA_LayoutUsesWidgetRect);
257 resetXPlanetLocation->setMaximumSize(QSize(32, 32));
258 resetXPlanetLocation->setMinimumSize(QSize(32, 32));
259 resetXPlanetLocation->setToolTip(i18n("Reset XPlanet Location to the location specified in the XPlanet Options"));
260 selectorsLayout->addWidget(resetXPlanetLocation);
261 connect(resetXPlanetLocation, SIGNAL(clicked()), this, SLOT(resetLocation()));
262
263 m_FreeRotate = new QPushButton(this);
264 m_FreeRotate->setIcon(QIcon::fromTheme("object-rotate-left"));
265 m_FreeRotate->setAttribute(Qt::WA_LayoutUsesWidgetRect);
266 m_FreeRotate->setMaximumSize(QSize(32, 32));
267 m_FreeRotate->setMinimumSize(QSize(32, 32));
268 m_FreeRotate->setCheckable(true);
269 m_FreeRotate->setToolTip(i18n("Hover over target and freely rotate view with mouse in XPlanet Viewer"));
270 selectorsLayout->addWidget(m_FreeRotate);
271 connect(m_FreeRotate, SIGNAL(clicked()), this, SLOT(slotFreeRotate()));
272
273 QPushButton *reCenterB = new QPushButton(this);
274 reCenterB->setIcon(QIcon::fromTheme("snap-bounding-box-center"));
275 reCenterB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
276 reCenterB->setMaximumSize(QSize(32, 32));
277 reCenterB->setMinimumSize(QSize(32, 32));
278 reCenterB->setToolTip(i18n("Recenters the XPlanet image once it has been moved"));
279 selectorsLayout->addWidget(reCenterB);
280 connect(reCenterB, SIGNAL(clicked()), this, SLOT(reCenterXPlanet()));
281
282 QPushButton *saveB = new QPushButton(this);
283 saveB->setIcon(QIcon::fromTheme("document-save"));
284 saveB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
285 saveB->setMaximumSize(QSize(32, 32));
286 saveB->setMinimumSize(QSize(32, 32));
287 saveB->setToolTip(i18n("Save the image to disk"));
288 selectorsLayout->addWidget(saveB);
289 connect(saveB, SIGNAL(clicked()), this, SLOT(saveFileToDisk()));
290
291 QWidget *viewControlsWidget = new QWidget(this);
292 QHBoxLayout *viewControlsLayout = new QHBoxLayout(viewControlsWidget);
293 viewControlsLayout->setContentsMargins(0, 0, 0, 0);
294 mainLayout->addWidget(viewControlsWidget);
295
296 viewControlsLayout->addWidget(new QLabel(i18n("FOV:"), this));
297
298 m_FOVEdit = new NonLinearDoubleSpinBox();
299 m_FOVEdit->setDecimals(5);
300 QList<double> possibleValues;
301 possibleValues << 0;
302 for(double i = .0001; i < 100; i *= 1.5)
303 possibleValues << i;
304 m_FOVEdit->setRecommendedValues(possibleValues);
305 m_FOVEdit->setToolTip(i18n("Sets the FOV to the Specified value. Note: has no effect if hovering over object."));
306 viewControlsLayout->addWidget(m_FOVEdit);
307
308 if (Options::xplanetFOV())
309 m_FOV = KStars::Instance()->map()->fov();
310 else
311 m_FOV = m_objectDefaultFOVs.at( m_CurrentObjectIndex);
312 m_FOVEdit->setValue(m_FOV);
313
314 connect(m_FOVEdit, SIGNAL(valueChanged(double)), this, SLOT(updateXPlanetFOVEdit()));
315
316 m_KStarsFOV = new QPushButton(this);
317 m_KStarsFOV->setIcon(QIcon::fromTheme("zoom-fit-width"));
318 m_KStarsFOV->setAttribute(Qt::WA_LayoutUsesWidgetRect);
319 m_KStarsFOV->setMaximumSize(QSize(32, 32));
320 m_KStarsFOV->setMinimumSize(QSize(32, 32));
321 m_KStarsFOV->setToolTip(i18n("Zoom to the current KStars FOV. Note: has no effect if hovering over object."));
322 viewControlsLayout->addWidget(m_KStarsFOV);
323 connect(m_KStarsFOV, SIGNAL(clicked()), this, SLOT(setKStarsXPlanetFOV()));
324
325 m_setFOV = new QPushButton(this);
326 m_setFOV->setIcon(QIcon::fromTheme("view-list-details"));
327 m_setFOV->setAttribute(Qt::WA_LayoutUsesWidgetRect);
328 m_setFOV->setMaximumSize(QSize(32, 32));
329 m_setFOV->setMinimumSize(QSize(32, 32));
330 m_setFOV->setToolTip(i18n("Zoom to a specific FOV. This has no effect when hovering over an object"));
331 viewControlsLayout->addWidget(m_setFOV);
332 connect(m_setFOV, SIGNAL(clicked()), this, SLOT(setFOVfromList()));
333
334 m_NoFOV = new QPushButton(this);
335 m_NoFOV->setIcon(QIcon::fromTheme("system-reboot"));
336 m_NoFOV->setAttribute(Qt::WA_LayoutUsesWidgetRect);
337 m_NoFOV->setMaximumSize(QSize(32, 32));
338 m_NoFOV->setMinimumSize(QSize(32, 32));
339 m_NoFOV->setToolTip(i18n("Optimum FOV for the target, FOV parameter not specified. Note: has no effect if hovering over object."));
340 viewControlsLayout->addWidget(m_NoFOV);
341 connect(m_NoFOV, SIGNAL(clicked()), this, SLOT(resetXPlanetFOV()));
342
343 m_Rotation = 0;
344
345 viewControlsLayout->addWidget(new QLabel(i18n("Rotation:"), this));
346
347 m_RotateEdit = new QSpinBox();
348
349 m_RotateEdit->setRange(-180, 180);
350 m_RotateEdit->setValue(0);
351 m_RotateEdit->setSingleStep(10);
352 m_RotateEdit->setToolTip(i18n("Set the view rotation to the desired angle"));
353 viewControlsLayout->addWidget(m_RotateEdit);
354 connect(m_RotateEdit, SIGNAL(valueChanged(int)), this, SLOT(updateXPlanetRotationEdit()));
355
356 QPushButton *invertRotation = new QPushButton(this);
357 invertRotation->setIcon(QIcon::fromTheme("object-flip-vertical"));
358 invertRotation->setAttribute(Qt::WA_LayoutUsesWidgetRect);
359 invertRotation->setMaximumSize(QSize(32, 32));
360 invertRotation->setMinimumSize(QSize(32, 32));
361 invertRotation->setToolTip(i18n("Rotate the view 180 degrees"));
362 viewControlsLayout->addWidget(invertRotation);
363 connect(invertRotation, SIGNAL(clicked()), this, SLOT(invertXPlanetRotation()));
364
365 QPushButton *resetRotation = new QPushButton(this);
366 resetRotation->setIcon(QIcon::fromTheme("system-reboot"));
367 resetRotation->setAttribute(Qt::WA_LayoutUsesWidgetRect);
368 resetRotation->setMaximumSize(QSize(32, 32));
369 resetRotation->setMinimumSize(QSize(32, 32));
370 resetRotation->setToolTip(i18n("Reset view rotation to 0"));
371 viewControlsLayout->addWidget(resetRotation);
372 connect(resetRotation, SIGNAL(clicked()), this, SLOT(resetXPlanetRotation()));
373
374 QPushButton *optionsB = new QPushButton(this);
375 optionsB->setIcon(QIcon::fromTheme("configure"));
376 optionsB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
377 optionsB->setMaximumSize(QSize(32, 32));
378 optionsB->setMinimumSize(QSize(32, 32));
379 optionsB->setToolTip(i18n("Bring up XPlanet Options"));
380 viewControlsLayout->addWidget(optionsB);
381 connect(optionsB, SIGNAL(clicked()), KStars::Instance(), SLOT(slotViewOps()));
382
383 QPushButton *invertB = new QPushButton(this);
384 invertB->setIcon(QIcon::fromTheme("edit-select-invert"));
385 invertB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
386 invertB->setMaximumSize(QSize(32, 32));
387 invertB->setMinimumSize(QSize(32, 32));
388 invertB->setToolTip(i18n("Reverse colors of the image. This is useful to enhance contrast at times. This affects "
389 "only the display and not the saving."));
390 viewControlsLayout->addWidget(invertB);
391 connect(invertB, SIGNAL(clicked()), this, SLOT(invertColors()));
392
393 QWidget *timeWidget = new QWidget(this);
394 QHBoxLayout *timeLayout = new QHBoxLayout(timeWidget);
395 mainLayout->addWidget(timeWidget);
396 timeLayout->setContentsMargins(0, 0, 0, 0);
397
398 m_XPlanetTime = KStarsData::Instance()->lt();
399
400 QPushButton *setTime = new QPushButton(this);
401 setTime->setIcon(QIcon::fromTheme("clock"));
402 setTime->setAttribute(Qt::WA_LayoutUsesWidgetRect);
403 setTime->setMaximumSize(QSize(32, 32));
404 setTime->setMinimumSize(QSize(32, 32));
405 setTime->setToolTip(i18n("Allows you to set the XPlanet time to a different date/time from KStars"));
406 timeLayout->addWidget(setTime);
407 connect(setTime, SIGNAL(clicked()), this, SLOT(setXPlanetTime()));
408
409 QPushButton *kstarsTime = new QPushButton(this);
410 kstarsTime->setIcon(QIcon::fromTheme("system-reboot"));
411 kstarsTime->setAttribute(Qt::WA_LayoutUsesWidgetRect);
412 kstarsTime->setMaximumSize(QSize(32, 32));
413 kstarsTime->setMinimumSize(QSize(32, 32));
414 kstarsTime->setToolTip(i18n("Sets the XPlanet time to the current KStars time"));
415 timeLayout->addWidget(kstarsTime);
416 connect(kstarsTime, SIGNAL(clicked()), this, SLOT(setXPlanetTimetoKStarsTime()));
417
418 m_XPlanetTimeDisplay = new QLabel(this);
419 m_XPlanetTimeDisplay->setToolTip(i18n("Current XPlanet Time"));
420 timeLayout->addWidget(m_XPlanetTimeDisplay);
421
422 m_XPlanetTimeDisplay->setText(i18n("%1, %2", m_XPlanetTime.date().toString(), m_XPlanetTime.time().toString()));
423
424 m_TimeSlider = new QSlider(Qt::Horizontal, this);
425 m_TimeSlider->setTracking(false);
426 connect(m_TimeSlider, SIGNAL(sliderMoved(int)), this, SLOT(timeSliderDisplay(int)));
427 timeLayout->addWidget(m_TimeSlider);
428 m_TimeSlider->setRange(-100, 100);
429 m_TimeSlider->setToolTip(i18n("This sets the time step from the current XPlanet time, good for viewing events"));
430 connect(m_TimeSlider, SIGNAL(valueChanged(int)), this, SLOT(updateXPlanetTime(int)));
431
432 m_TimeEdit = new QSpinBox(this);
433 m_TimeEdit->setRange(-10000, 10000);
434 m_TimeEdit->setMaximumWidth(50);
435 m_TimeEdit->setToolTip(i18n("This sets the time step from the current XPlanet time"));
436 timeLayout->addWidget(m_TimeEdit);
437 connect(m_TimeEdit, SIGNAL(valueChanged(int)), this, SLOT(updateXPlanetTimeEdit()));
438
439 m_CurrentTimeUnitIndex = MINS;
440 m_TimeUnitsSelect = new QComboBox(this);
441 timeLayout->addWidget(m_TimeUnitsSelect);
442 m_TimeUnitsSelect->addItem(i18n("years"));
443 m_TimeUnitsSelect->addItem(i18n("months"));
444 m_TimeUnitsSelect->addItem(i18n("days"));
445 m_TimeUnitsSelect->addItem(i18n("hours"));
446 m_TimeUnitsSelect->addItem(i18n("minutes"));
447 m_TimeUnitsSelect->addItem(i18n("seconds"));
448 m_TimeUnitsSelect->setCurrentIndex(MINS);
449 m_TimeUnitsSelect->setToolTip(i18n("Lets you change the units for the timestep in the animation"));
450 connect(m_TimeUnitsSelect, SIGNAL(currentIndexChanged(int)), this, SLOT(updateXPlanetTimeUnits(int)));
451
452 m_XPlanetTimer = new QTimer(this);
453 m_XPlanetTimer->setInterval(Options::xplanetAnimationDelay());
454 connect(m_XPlanetTimer, SIGNAL(timeout()), this, SLOT(incrementXPlanetTime()));
455
456 m_RunTime = new QPushButton(this);
457 m_RunTime->setIcon(QIcon::fromTheme("media-playback-start"));
458 m_RunTime->setAttribute(Qt::WA_LayoutUsesWidgetRect);
459 m_RunTime->setCheckable(true);
460 m_RunTime->setMaximumSize(QSize(32, 32));
461 m_RunTime->setMinimumSize(QSize(32, 32));
462 m_RunTime->setToolTip(i18n("Lets you run the animation"));
463 timeLayout->addWidget(m_RunTime);
464 connect(m_RunTime, SIGNAL(clicked()), this, SLOT(toggleXPlanetRun()));
465
466 QPushButton *resetTime = new QPushButton(this);
467 resetTime->setIcon(QIcon::fromTheme("system-reboot"));
468 resetTime->setAttribute(Qt::WA_LayoutUsesWidgetRect);
469 resetTime->setMaximumSize(QSize(32, 32));
470 resetTime->setMinimumSize(QSize(32, 32));
471 resetTime->setToolTip(i18n("Resets the animation to 0 timesteps from the current XPlanet Time"));
472 timeLayout->addWidget(resetTime);
473 connect(resetTime, SIGNAL(clicked()), this, SLOT(resetXPlanetTime()));
474
475 m_View = new XPlanetImageLabel(page);
476 m_View->setAutoFillBackground(false);
477 m_Caption = new QLabel(page);
478 m_Caption->setAutoFillBackground(true);
479 m_Caption->setFrameShape(QFrame::StyledPanel);
480 m_Caption->setText(m_ObjectName);
481 // Add layout
482 QVBoxLayout *vlay = new QVBoxLayout(page);
483 vlay->setSpacing(0);
484 vlay->setContentsMargins(0, 0, 0, 0);
485 vlay->addWidget(m_View);
486 vlay->addWidget(m_Caption);
487
488
489 connect(m_View, SIGNAL(zoomIn()), this, SLOT(zoomInXPlanetFOV()));
490 connect(m_View, SIGNAL(zoomOut()), this, SLOT(zoomOutXPlanetFOV()));
491 connect(m_View, SIGNAL(changePosition(QPoint)), this, SLOT(changeXPlanetPosition(QPoint)));
492 connect(m_View, SIGNAL(changeLocation(QPoint)), this, SLOT(changeXPlanetLocation(QPoint)));
493
494 //Reverse colors
495 QPalette p = palette();
496 p.setColor(QPalette::Window, palette().color(QPalette::WindowText));
497 p.setColor(QPalette::WindowText, palette().color(QPalette::Window));
498 m_Caption->setPalette(p);
499 m_View->setPalette(p);
500
501 #ifndef Q_OS_WIN
502 if(Options::xplanetUseFIFO())
503 {
504 connect(&watcherTimeout, SIGNAL(timeout()), &fifoImageLoadWatcher, SLOT(cancel()));
505 connect(&fifoImageLoadWatcher, SIGNAL(finished()), this, SLOT(showImage()));
506 }
507 #endif
508
509
510 #ifdef Q_OS_OSX
511 QList<QPushButton *> qButtons = findChildren<QPushButton *>();
512 for (auto &button : qButtons)
513 button->setAutoDefault(false);
514 #endif
515 updateXPlanetTime(0);
516 #endif
517 }
518
~XPlanetImageViewer()519 XPlanetImageViewer::~XPlanetImageViewer()
520 {
521 QApplication::restoreOverrideCursor();
522 }
523
startXplanet()524 void XPlanetImageViewer::startXplanet()
525 {
526 if(m_XPlanetRunning)
527 return;
528
529 //This means something failed in the file output
530 if(!setupOutputFile())
531 return;
532
533 QString xPlanetLocation = Options::xplanetPath();
534 #ifdef Q_OS_OSX
535 if (Options::xplanetIsInternal())
536 xPlanetLocation = QCoreApplication::applicationDirPath() + "/xplanet/bin/xplanet";
537 #endif
538
539 // If Options::xplanetPath() is empty, return
540 if (xPlanetLocation.isEmpty())
541 {
542 KSNotification::error(i18n("Xplanet binary path is empty in config panel."));
543 return;
544 }
545
546 // If Options::xplanetPath() does not exist, return
547 const QFileInfo xPlanetLocationInfo(xPlanetLocation);
548 if (!xPlanetLocationInfo.exists() || !xPlanetLocationInfo.isExecutable())
549 {
550 KSNotification::error(i18n("The configured Xplanet binary does not exist or is not executable."));
551 return;
552 }
553
554 // Create xplanet process
555 QProcess *xplanetProc = new QProcess(this);
556
557 // Add some options
558 QStringList args;
559
560 //This specifies the object to be viewed
561 args << "-body" << m_ObjectName.toLower();
562 //This is the date and time requested
563 args << "-date" << m_Date;
564 //This is the glare from the sun
565 args << "-glare" << Options::xplanetGlare();
566 args << "-base_magnitude" << Options::xplanetMagnitude();
567 //This is the correction for light's travel time.
568 args << "-light_time";
569
570 args << "-geometry" << QString::number(Options::xplanetWidth()) + 'x' + QString::number(Options::xplanetHeight());
571
572 if(m_FOV != 0)
573 args << "-fov" << QString::number(m_FOV);
574 //Need to convert to locale for places that don't use decimals??
575 //args << "-fov" << fov.setNum(fov());//.replace('.', ',');
576
577 //This rotates the view for different object angles
578 args << "-rotate" << QString::number(m_Rotation);
579
580 if (Options::xplanetConfigFile())
581 args << "-config" << Options::xplanetConfigFilePath();
582 if (Options::xplanetStarmap())
583 args << "-starmap" << Options::xplanetStarmapPath();
584 if (Options::xplanetArcFile())
585 args << "-arc_file" << Options::xplanetArcFilePath();
586 if (!m_File.fileName().isEmpty())
587 args << "-output" << m_File.fileName() << "-quality" << Options::xplanetQuality();
588
589 // Labels
590 if (Options::xplanetLabel())
591 {
592 args << "-fontsize" << Options::xplanetFontSize() << "-color"
593 << "0x" + Options::xplanetColor().mid(1) << "-date_format" << Options::xplanetDateFormat();
594
595 if (Options::xplanetLabelGMT())
596 args << "-gmtlabel";
597 else
598 args << "-label";
599 if (!Options::xplanetLabelString().isEmpty())
600 args << "-label_string"
601 << "\"" + Options::xplanetLabelString() + "\"";
602 if (Options::xplanetLabelTL())
603 args << "-labelpos"
604 << "+15+15";
605 else if (Options::xplanetLabelTR())
606 args << "-labelpos"
607 << "-15+15";
608 else if (Options::xplanetLabelBR())
609 args << "-labelpos"
610 << "-15-15";
611 else if (Options::xplanetLabelBL())
612 args << "-labelpos"
613 << "+15-15";
614 }
615
616 // Markers
617 if (Options::xplanetMarkerFile())
618 args << "-marker_file" << Options::xplanetMarkerFilePath();
619 if (Options::xplanetMarkerBounds())
620 args << "-markerbounds" << Options::xplanetMarkerBoundsPath();
621
622 // Position
623 // This sets the position from which the planet is viewed.
624 // Note that setting Latitude and Longitude means that position above the SAME object
625
626 if(m_CurrentObjectIndex == m_CurrentOriginIndex)
627 {
628 if (Options::xplanetRandom())
629 args << "-random";
630 else
631 args << "-latitude" << QString::number(m_lat) << "-longitude" << QString::number(m_lon) << "-radius" << QString::number(m_Radius);
632 }
633 else
634 args << "-origin" << m_OriginName;
635
636 //Centering
637 //This allows you to recenter the xplanet view
638
639 args << "-center" << "+" + QString::number(Options::xplanetWidth() / 2 + center.x()) + "+" + QString::number(Options::xplanetHeight() / 2 + center.y());
640
641 // Projection
642 if (Options::xplanetProjection())
643 {
644 switch (Options::xplanetProjection())
645 {
646 case 1:
647 args << "-projection"
648 << "ancient";
649 break;
650 case 2:
651 args << "-projection"
652 << "azimuthal";
653 break;
654 case 3:
655 args << "-projection"
656 << "bonne";
657 break;
658 case 4:
659 args << "-projection"
660 << "gnomonic";
661 break;
662 case 5:
663 args << "-projection"
664 << "hemisphere";
665 break;
666 case 6:
667 args << "-projection"
668 << "lambert";
669 break;
670 case 7:
671 args << "-projection"
672 << "mercator";
673 break;
674 case 8:
675 args << "-projection"
676 << "mollweide";
677 break;
678 case 9:
679 args << "-projection"
680 << "orthographic";
681 break;
682 case 10:
683 args << "-projection"
684 << "peters";
685 break;
686 case 11:
687 args << "-projection"
688 << "polyconic";
689 break;
690 case 12:
691 args << "-projection"
692 << "rectangular";
693 break;
694 case 13:
695 args << "-projection"
696 << "tsc";
697 break;
698 default:
699 break;
700 }
701 if (Options::xplanetBackground())
702 {
703 if (Options::xplanetBackgroundImage())
704 args << "-background" << Options::xplanetBackgroundImagePath();
705 else
706 args << "-background"
707 << "0x" + Options::xplanetBackgroundColorValue().mid(1);
708 }
709 }
710
711 #ifdef Q_OS_OSX
712 if (Options::xplanetIsInternal())
713 {
714 QString searchDir = QCoreApplication::applicationDirPath() + "/xplanet/share/xplanet/";
715 args << "-searchdir" << searchDir;
716 }
717 #endif
718
719 #ifdef Q_OS_WIN
720 QString searchDir = xPlanetLocationInfo.dir().absolutePath() + QDir::separator() + "xplanet";
721 args << "-searchdir" << searchDir;
722 #endif
723
724 //This prevents it from running forever.
725 args << "-num_times" << "1";
726
727 m_XPlanetRunning = true;
728 m_ImageLoadSucceeded = false; //This will be set to true if it works.
729 uint32_t timeout = Options::xplanetTimeout();
730
731 //FIFO files don't work on windows
732 #ifndef Q_OS_WIN
733 if(Options::xplanetUseFIFO())
734 {
735 fifoImageLoadWatcher.setFuture(QtConcurrent::run(this, &XPlanetImageViewer::loadImage));
736 watcherTimeout.setSingleShot(true);
737 watcherTimeout.start(timeout);
738 }
739 #endif
740
741 xplanetProc->start(xPlanetLocation, args);
742
743 //Uncomment to print the XPlanet commands to the console
744 // qDebug() << "Run:" << xplanetProc->program() << args.join(" ");
745
746 bool XPlanetSucceeded = xplanetProc->waitForFinished(timeout);
747 m_XPlanetRunning = false;
748 xplanetProc->kill(); //In case it timed out
749 xplanetProc->deleteLater();
750 if(XPlanetSucceeded)
751 {
752 if(m_FOV == 0)
753 m_Caption->setText(i18n("XPlanet View: %1 from %2 on %3", m_ObjectName, m_OriginName, m_DateText));
754 else
755 m_Caption->setText(i18n("XPlanet View: %1 from %2 on %3 at FOV: %4 deg", m_ObjectName, m_OriginName, m_DateText, m_FOV));
756 #ifdef Q_OS_WIN
757 loadImage(); //This will also set imageLoadSucceeded based on whether it worked.
758 #else
759 if(Options::xplanetUseFIFO())
760 return; //The loading of the image is handled with the watcher
761 else
762 loadImage(); //This will also set imageLoadSucceeded based on whether it worked.
763 #endif
764
765 if(m_ImageLoadSucceeded)
766 showImage();
767 else
768 {
769 KSNotification::error(i18n("Loading of the image of object %1 failed.", m_ObjectName));
770 }
771 }
772 else
773 {
774 KStars::Instance()->statusBar()->showMessage(i18n("XPlanet failed to generate the image for object %1 before the timeout expired.", m_ObjectName));
775 #ifndef Q_OS_WIN
776 if(Options::xplanetUseFIFO())
777 fifoImageLoadWatcher.cancel();
778 #endif
779 }
780 }
781
setupOutputFile()782 bool XPlanetImageViewer::setupOutputFile()
783 {
784 #ifndef Q_OS_WIN
785 if(Options::xplanetUseFIFO())
786 {
787 if(m_File.fileName().contains("xplanetfifo") && m_File.exists())
788 return true;
789 QDir kstarsTempDir(KSPaths::writableLocation(QStandardPaths::TempLocation) + "/" + qAppName());
790 kstarsTempDir.mkpath(".");
791 m_File.setFileName(kstarsTempDir.filePath(QString("xplanetfifo%1.png").arg(QUuid::createUuid().toString().mid(1, 8)).toLatin1()));
792 int mkFifoSuccess = 0; //Note if the return value of the command is 0 it succeeded, -1 means it failed.
793 if ((mkFifoSuccess = mkfifo(m_File.fileName().toLatin1(), S_IRUSR | S_IWUSR) < 0))
794 {
795 KSNotification::error(i18n("Error making FIFO file %1: %2.", m_File.fileName(), strerror(errno)));
796 return false;
797 }
798 return true;
799 }
800 #endif
801
802 //If the user is using windows or has not selected to use FIFO, it uses files in the KStars data directory.
803 QDir xPlanetDirPath(KSPaths::writableLocation(QStandardPaths::AppDataLocation) + "/" + "xplanet");
804 xPlanetDirPath.mkpath(".");
805 m_File.setFileName(xPlanetDirPath.filePath(m_ObjectName + ".png"));
806 return true;
807 }
808
zoomInXPlanetFOV()809 void XPlanetImageViewer::zoomInXPlanetFOV()
810 {
811 if(m_CurrentObjectIndex == m_CurrentOriginIndex)
812 {
813 m_Radius += 5;
814 updatePositionDisplay();
815 startXplanet();
816 }
817 else
818 {
819 m_FOVEdit->stepDown();
820 startXplanet();
821 }
822
823 }
824
zoomOutXPlanetFOV()825 void XPlanetImageViewer::zoomOutXPlanetFOV()
826 {
827 if(m_CurrentObjectIndex == m_CurrentOriginIndex)
828 {
829 if(m_Radius > 0)
830 {
831 m_Radius -= 5;
832 updatePositionDisplay();
833 startXplanet();
834 }
835 }
836 else
837 {
838 m_FOVEdit->stepUp();
839 startXplanet();
840 }
841
842 }
843
updatePositionDisplay()844 void XPlanetImageViewer::updatePositionDisplay()
845 {
846 m_PositionDisplay->setText(i18n("%1, %2, %3", QString::number(m_lat), QString::number(m_lon), QString::number(m_Radius)));
847 }
848
updateXPlanetTime(int timeShift)849 void XPlanetImageViewer::updateXPlanetTime(int timeShift)
850 {
851
852 KStarsDateTime shiftedXPlanetTime;
853 switch(m_CurrentTimeUnitIndex)
854 {
855 case YEARS:
856 shiftedXPlanetTime = m_XPlanetTime.addDays(timeShift * 365);
857 break;
858
859 case MONTHS:
860 shiftedXPlanetTime = m_XPlanetTime.addDays(timeShift * 30);
861 break;
862
863 case DAYS:
864 shiftedXPlanetTime = m_XPlanetTime.addDays(timeShift);
865 break;
866
867 case HOURS:
868 shiftedXPlanetTime = m_XPlanetTime.addSecs(timeShift * 3600);
869 break;
870
871 case MINS:
872 shiftedXPlanetTime = m_XPlanetTime.addSecs(timeShift * 60);
873 break;
874
875 case SECS:
876 shiftedXPlanetTime = m_XPlanetTime.addSecs(timeShift);
877 break;
878 }
879
880 setXPlanetDate(shiftedXPlanetTime);
881 m_DateText = i18n("%1, %2", shiftedXPlanetTime.date().toString(), shiftedXPlanetTime.time().toString());
882 if(m_TimeEdit->value() != timeShift)
883 m_TimeEdit->setValue(timeShift);
884 else
885 startXplanet();
886 }
887
updateXPlanetObject(int objectIndex)888 void XPlanetImageViewer::updateXPlanetObject(int objectIndex)
889 {
890 center = QPoint(0, 0);
891 m_CurrentObjectIndex = objectIndex;
892 m_ObjectName = m_ObjectNames.at(objectIndex);
893
894 setWindowTitle(i18nc("@title:window", "XPlanet Solar System Simulator: %1", m_ObjectName));
895 if(m_FreeRotate->isChecked())
896 m_OriginSelector->setCurrentIndex(m_CurrentObjectIndex);
897 if(m_CurrentObjectIndex == m_CurrentOriginIndex)
898 startXplanet();
899 else
900 resetXPlanetFOV();
901 }
902
updateXPlanetOrigin(int originIndex)903 void XPlanetImageViewer::updateXPlanetOrigin(int originIndex)
904 {
905 center = QPoint(0, 0);
906 m_CurrentOriginIndex = originIndex;
907 m_OriginName = m_ObjectNames.at(originIndex);
908 if(m_CurrentObjectIndex == m_CurrentOriginIndex)
909 m_FreeRotate->setChecked(true);
910 else
911 m_FreeRotate->setChecked(false);
912 updateStates();//This will update the disabled/enabled states
913 startXplanet();
914 }
915
changeXPlanetLocation(QPoint delta)916 void XPlanetImageViewer::changeXPlanetLocation(QPoint delta)
917 {
918 if(m_CurrentObjectIndex == m_CurrentOriginIndex)
919 {
920 double newLon = m_lon + delta.x();
921 double newLat = m_lat + delta.y();
922
923 newLat = qBound(-90.0, newLat, 90.0);
924
925 m_lon = newLon;
926 m_lat = newLat;
927
928 updatePositionDisplay();
929 startXplanet();
930 }
931 }
932
changeXPlanetPosition(QPoint delta)933 void XPlanetImageViewer::changeXPlanetPosition(QPoint delta)
934 {
935 center.setX(center.x() + delta.x());
936 center.setY(center.y() + delta.y());
937 startXplanet();
938 }
939
reCenterXPlanet()940 void XPlanetImageViewer::reCenterXPlanet()
941 {
942 center = QPoint(0, 0);
943 startXplanet();
944 }
945
resetLocation()946 void XPlanetImageViewer::resetLocation()
947 {
948 m_lat = Options::xplanetLatitude().toDouble();
949 m_lon = Options::xplanetLongitude().toDouble();
950 m_Radius = 45;
951 updatePositionDisplay();
952 startXplanet();
953 }
954
slotFreeRotate()955 void XPlanetImageViewer::slotFreeRotate()
956 {
957 if(m_FreeRotate->isChecked())
958 m_OriginSelector->setCurrentIndex(m_CurrentObjectIndex);
959 else
960 m_OriginSelector->setCurrentIndex(EARTH);
961 }
962
updateStates()963 void XPlanetImageViewer::updateStates()
964 {
965 if(m_FreeRotate->isChecked())
966 {
967 m_FOVEdit->setDisabled(true);
968 m_KStarsFOV->setDisabled(true);
969 m_NoFOV->setDisabled(true);
970
971 m_PositionDisplay->setDisabled(false);
972 }
973 else
974 {
975 m_FOVEdit->setDisabled(false);
976 m_KStarsFOV->setDisabled(false);
977 m_NoFOV->setDisabled(false);
978
979 m_PositionDisplay->setDisabled(true);
980 }
981 }
982
setXPlanetDate(KStarsDateTime time)983 void XPlanetImageViewer::setXPlanetDate(KStarsDateTime time)
984 {
985 //Note Xplanet uses UT time for everything but we want the labels to all be LT
986 KStarsDateTime utTime = KStarsData::Instance()->geo()->LTtoUT(time);
987 m_Date = utTime.toString(Qt::ISODate)
988 .replace("-", QString(""))
989 .replace("T", ".")
990 .replace(":", QString(""))
991 .replace("Z", QString(""));
992 }
993
updateXPlanetTimeUnits(int units)994 void XPlanetImageViewer::updateXPlanetTimeUnits(int units)
995 {
996 m_CurrentTimeUnitIndex = units;
997 updateXPlanetTimeEdit();
998 }
999
updateXPlanetTimeEdit()1000 void XPlanetImageViewer::updateXPlanetTimeEdit()
1001 {
1002 if(m_TimeSlider->isSliderDown())
1003 return;
1004 int timeShift = m_TimeEdit->value();
1005 if(m_TimeSlider->value() != timeShift)
1006 {
1007
1008 if(timeShift > m_TimeSlider->maximum() + 100)
1009 m_TimeSlider->setMaximum(timeShift);
1010 if(timeShift < m_TimeSlider->minimum() - 100)
1011 m_TimeSlider->setMinimum(timeShift);
1012 m_TimeSlider->setValue(timeShift);
1013 }
1014 else
1015 updateXPlanetTime(timeShift);
1016 }
1017
timeSliderDisplay(int timeShift)1018 void XPlanetImageViewer::timeSliderDisplay(int timeShift)
1019 {
1020 m_TimeEdit->setValue(timeShift);
1021 }
1022
incrementXPlanetTime()1023 void XPlanetImageViewer::incrementXPlanetTime()
1024 {
1025 if(!m_XPlanetRunning)
1026 {
1027 int timeShift = m_TimeEdit->value();
1028 if(m_TimeSlider->maximum() <= timeShift)
1029 m_TimeSlider->setMaximum(timeShift + 100);
1030 if(m_TimeEdit->maximum() <= timeShift)
1031 m_TimeEdit->setMaximum(timeShift + 100);
1032 m_TimeSlider->setValue(timeShift + 1);
1033 }
1034 }
1035
setXPlanetTime()1036 void XPlanetImageViewer::setXPlanetTime()
1037 {
1038 QPointer<TimeDialog> timedialog = new TimeDialog(m_XPlanetTime, KStarsData::Instance()->geo(), this);
1039 if (timedialog->exec() == QDialog::Accepted)
1040 {
1041 m_XPlanetTime = timedialog->selectedDateTime();
1042 m_XPlanetTimeDisplay->setText(i18n("%1, %2", m_XPlanetTime.date().toString(), m_XPlanetTime.time().toString()));
1043 int timeShift = 0;
1044 m_TimeSlider->setRange(-100, 100);
1045 if(m_TimeSlider->value() != timeShift)
1046 m_TimeSlider->setValue(timeShift);
1047 else
1048 updateXPlanetTime(timeShift);
1049 }
1050 }
1051
setXPlanetTimetoKStarsTime()1052 void XPlanetImageViewer::setXPlanetTimetoKStarsTime()
1053 {
1054 m_XPlanetTime = KStarsData::Instance()->lt();
1055 m_XPlanetTimeDisplay->setText(i18n("%1, %2", m_XPlanetTime.date().toString(), m_XPlanetTime.time().toString()));
1056 int timeShift = 0;
1057 m_TimeSlider->setRange(-100, 100);
1058 if(m_TimeSlider->value() != timeShift)
1059 m_TimeSlider->setValue(timeShift);
1060 else
1061 updateXPlanetTime(timeShift);
1062 }
1063
resetXPlanetTime()1064 void XPlanetImageViewer::resetXPlanetTime()
1065 {
1066 int timeShift = 0;
1067 m_TimeSlider->setRange(-100, 100);
1068 if(m_TimeSlider->value() != timeShift)
1069 m_TimeSlider->setValue(timeShift);
1070 else
1071 updateXPlanetTime(timeShift);
1072 }
1073
toggleXPlanetRun()1074 void XPlanetImageViewer::toggleXPlanetRun()
1075 {
1076 if(m_XPlanetTimer->isActive())
1077 {
1078 m_XPlanetTimer->stop();
1079 #ifndef Q_OS_WIN
1080 if(Options::xplanetUseFIFO())
1081 fifoImageLoadWatcher.cancel();
1082 #endif
1083 }
1084 else
1085 {
1086 m_XPlanetTimer->setInterval(Options::xplanetAnimationDelay());
1087 m_XPlanetTimer->start();
1088 }
1089 }
1090
updateXPlanetFOVEdit()1091 void XPlanetImageViewer::updateXPlanetFOVEdit()
1092 {
1093 m_FOV = m_FOVEdit->value();
1094 startXplanet();
1095 }
1096
resetXPlanetFOV()1097 void XPlanetImageViewer::resetXPlanetFOV()
1098 {
1099 m_FOV = m_objectDefaultFOVs.at(m_CurrentObjectIndex);
1100 m_FOVEdit->setValue(m_FOV);
1101 startXplanet();
1102 }
1103
setKStarsXPlanetFOV()1104 void XPlanetImageViewer::setKStarsXPlanetFOV()
1105 {
1106 m_FOV = KStars::Instance()->map()->fov();
1107 m_FOVEdit->setValue(m_FOV);
1108 startXplanet();
1109 }
setFOVfromList()1110 void XPlanetImageViewer::setFOVfromList()
1111 {
1112 if (!KStarsData::Instance()->getAvailableFOVs().isEmpty())
1113 {
1114 // Ask the user to choose from a list of available FOVs.
1115 QMap<QString, const FOV *> nameToFovMap;
1116 for (const FOV *f : KStarsData::Instance()->getAvailableFOVs())
1117 {
1118 nameToFovMap.insert(f->name(), f);
1119 }
1120 bool ok = false;
1121 const FOV *fov = nullptr;
1122 fov = nameToFovMap[QInputDialog::getItem(this, i18n("Choose a field-of-view"),
1123 i18n("FOV to render in XPlanet:"), nameToFovMap.uniqueKeys(), 0,
1124 false, &ok)];
1125 if (ok)
1126 {
1127 m_FOV = fov->sizeX() / 60 ; //Converting from arcmin to degrees
1128 m_FOVEdit->setValue(m_FOV);
1129 startXplanet();
1130 }
1131 }
1132 }
1133
updateXPlanetRotationEdit()1134 void XPlanetImageViewer::updateXPlanetRotationEdit()
1135 {
1136 m_Rotation = m_RotateEdit->value();
1137 startXplanet();
1138 }
1139
resetXPlanetRotation()1140 void XPlanetImageViewer::resetXPlanetRotation()
1141 {
1142 m_RotateEdit->setValue(0);
1143 }
1144
invertXPlanetRotation()1145 void XPlanetImageViewer::invertXPlanetRotation()
1146 {
1147 m_RotateEdit->setValue(180);
1148 }
1149
loadImage()1150 bool XPlanetImageViewer::loadImage()
1151 {
1152 #ifndef KSTARS_LITE
1153
1154 if (!m_Image.load(m_File.fileName()))
1155 {
1156 m_ImageLoadSucceeded = false;
1157 return false;
1158 }
1159 m_ImageLoadSucceeded = true;
1160 return true;
1161 #else
1162 imageLoadSucceeded = false;
1163 return false;
1164 #endif
1165 }
1166
showImage()1167 bool XPlanetImageViewer::showImage()
1168 {
1169 #ifndef KSTARS_LITE
1170
1171 //If the image is larger than screen width and/or screen height,
1172 //shrink it to fit the screen
1173 QRect deskRect = QGuiApplication::primaryScreen()->geometry();
1174 int w = deskRect.width(); // screen width
1175 int h = deskRect.height(); // screen height
1176
1177 if (m_Image.width() <= w && m_Image.height() > h) //Window is taller than desktop
1178 m_Image = m_Image.scaled(int(m_Image.width() * h / m_Image.height()), h);
1179 else if (m_Image.height() <= h && m_Image.width() > w) //window is wider than desktop
1180 m_Image = m_Image.scaled(w, int(m_Image.height() * w / m_Image.width()));
1181 else if (m_Image.width() > w && m_Image.height() > h) //window is too tall and too wide
1182 {
1183 //which needs to be shrunk least, width or height?
1184 float fx = float(w) / float(m_Image.width());
1185 float fy = float(h) / float(m_Image.height());
1186 if (fx > fy) //width needs to be shrunk less, so shrink to fit in height
1187 m_Image = m_Image.scaled(int(m_Image.width() * fy), h);
1188 else //vice versa
1189 m_Image = m_Image.scaled(w, int(m_Image.height() * fx));
1190 }
1191 const bool initialLoad = !isVisible();
1192
1193 show(); // hide is default
1194
1195 m_View->setImage(m_Image);
1196 w = m_Image.width();
1197
1198 //If the caption is wider than the image, set the window size
1199 //to fit the caption
1200 if (m_Caption->width() > w)
1201 w = m_Caption->width();
1202 if(initialLoad)
1203 resize(w, m_Image.height());
1204 else
1205 {
1206 m_View->refreshImage();
1207 }
1208
1209 update();
1210 show();
1211
1212 return true;
1213 #else
1214 return false;
1215 #endif
1216 }
1217
saveFileToDisk()1218 void XPlanetImageViewer::saveFileToDisk()
1219 {
1220 #ifndef KSTARS_LITE
1221 QFileDialog saveDialog(KStars::Instance(), i18nc("@title:window", "Save Image"), m_LastFile);
1222 saveDialog.setDefaultSuffix("png");
1223 saveDialog.setAcceptMode(QFileDialog::AcceptSave);
1224 saveDialog.exec();
1225
1226 if(saveDialog.result() == QFileDialog::Rejected)
1227 return;
1228 if(saveDialog.selectedFiles().isEmpty())
1229 return;
1230 QString newFileName = saveDialog.selectedFiles().first();
1231 if(newFileName.isEmpty())
1232 return;
1233
1234 m_LastFile = newFileName;
1235
1236 saveFile(newFileName);
1237
1238 #endif
1239 }
1240
saveFile(const QString & fileName)1241 void XPlanetImageViewer::saveFile(const QString &fileName)
1242 {
1243 #ifndef KSTARS_LITE
1244
1245 if (! m_Image.save(fileName, "png"))
1246 {
1247 KSNotification::error(i18n("Saving of the image to %1 failed.", fileName));
1248 }
1249 else
1250 KStars::Instance()->statusBar()->showMessage(i18n("Saved image to %1", fileName));
1251 #endif
1252 }
1253
invertColors()1254 void XPlanetImageViewer::invertColors()
1255 {
1256 #ifndef KSTARS_LITE
1257 // Invert colors
1258 m_View->invertPixels();
1259 m_View->update();
1260 #endif
1261 }
1262
1263