Kstars

imageviewer.cpp
1 /*
2  SPDX-FileCopyrightText: 2001 Thomas Kabelmann <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "imageviewer.h"
8 #include "Options.h"
9 
10 #ifndef KSTARS_LITE
11 #include "kstars.h"
12 #include <KMessageBox>
13 #include <KFormat>
14 #endif
15 
16 #include "ksnotification.h"
17 #include <QFileDialog>
18 #include <QJsonObject>
19 #include <QPainter>
20 #include <QResizeEvent>
21 #include <QScreen>
22 #include <QStatusBar>
23 #include <QTemporaryFile>
24 #include <QVBoxLayout>
25 #include <QPushButton>
26 #include <QApplication>
27 
28 QUrl ImageViewer::lastURL = QUrl::fromLocalFile(QDir::homePath());
29 
30 ImageLabel::ImageLabel(QWidget *parent) : QFrame(parent)
31 {
32 #ifndef KSTARS_LITE
34  setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
35  setLineWidth(2);
36 #endif
37 }
38 
39 void ImageLabel::setImage(const QImage &img)
40 {
41 #ifndef KSTARS_LITE
42  m_Image = img;
43  pix = QPixmap::fromImage(m_Image);
44 #endif
45 }
46 
47 void ImageLabel::invertPixels()
48 {
49 #ifndef KSTARS_LITE
50  m_Image.invertPixels();
51  pix = QPixmap::fromImage(m_Image.scaled(width(), height(), Qt::KeepAspectRatio));
52 #endif
53 }
54 
55 void ImageLabel::paintEvent(QPaintEvent *)
56 {
57 #ifndef KSTARS_LITE
58  QPainter p;
59  p.begin(this);
60  int x = 0;
61  if (pix.width() < width())
62  x = (width() - pix.width()) / 2;
63  p.drawPixmap(x, 0, pix);
64  p.end();
65 #endif
66 }
67 
68 void ImageLabel::resizeEvent(QResizeEvent *event)
69 {
70  int w = pix.width();
71  int h = pix.height();
72 
73  if (event->size().width() == w && event->size().height() == h)
74  return;
75 
77 }
78 
79 ImageViewer::ImageViewer(const QString &caption, QWidget *parent) : QDialog(parent), fileIsImage(false), downloadJob(nullptr)
80 {
81 #ifndef KSTARS_LITE
82  init(caption, QString());
83 #endif
84 }
85 
86 ImageViewer::ImageViewer(const QUrl &url, const QString &capText, QWidget *parent)
87  : QDialog(parent), m_ImageUrl(url)
88 {
89 #ifndef KSTARS_LITE
90  init(url.fileName(), capText);
91 
92  // check URL
93  if (!m_ImageUrl.isValid())
94  qDebug() << Q_FUNC_INFO << "URL is malformed: " << m_ImageUrl;
95 
96  if (m_ImageUrl.isLocalFile())
97  {
98  loadImage(m_ImageUrl.toLocalFile());
99  return;
100  }
101 
102  {
103  QTemporaryFile tempfile;
104  tempfile.open();
105  file.setFileName(tempfile.fileName());
106  } // we just need the name and delete the tempfile from disc; if we don't do it, a dialog will be show
107 
108  loadImageFromURL();
109 #endif
110 }
111 
112 void ImageViewer::init(QString caption, QString capText)
113 {
114 #ifndef KSTARS_LITE
116  setModal(false);
117  setWindowTitle(i18nc("@title:window", "KStars image viewer: %1", caption));
118 
119  // Create widget
120  QFrame *page = new QFrame(this);
121 
122  //setMainWidget( page );
123  QVBoxLayout *mainLayout = new QVBoxLayout;
124  mainLayout->addWidget(page);
125  setLayout(mainLayout);
126 
128  mainLayout->addWidget(buttonBox);
129  connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
130 
131  QPushButton *invertB = new QPushButton(i18n("Invert colors"));
132  invertB->setToolTip(i18n("Reverse colors of the image. This is useful to enhance contrast at times. This affects "
133  "only the display and not the saving."));
134  QPushButton *saveB = new QPushButton(QIcon::fromTheme("document-save"), i18n("Save"));
135  saveB->setToolTip(i18n("Save the image to disk"));
136 
137  buttonBox->addButton(invertB, QDialogButtonBox::ActionRole);
138  buttonBox->addButton(saveB, QDialogButtonBox::ActionRole);
139 
140  connect(invertB, SIGNAL(clicked()), this, SLOT(invertColors()));
141  connect(saveB, SIGNAL(clicked()), this, SLOT(saveFileToDisc()));
142 
143  m_View = new ImageLabel(page);
144  m_View->setAutoFillBackground(true);
145  m_Caption = new QLabel(page);
146  m_Caption->setAutoFillBackground(true);
148  m_Caption->setText(capText);
149  // Add layout
150  QVBoxLayout *vlay = new QVBoxLayout(page);
151  vlay->setSpacing(0);
152  vlay->setContentsMargins(0, 0, 0, 0);
153  vlay->addWidget(m_View);
154  vlay->addWidget(m_Caption);
155 
156  //Reverse colors
157  QPalette p = palette();
160  m_Caption->setPalette(p);
161  m_View->setPalette(p);
162 
163  //If the caption is wider than the image, try to shrink the font a bit
164  QFont capFont = m_Caption->font();
165  capFont.setPointSize(capFont.pointSize() - 2);
166  m_Caption->setFont(capFont);
167 #endif
168 }
169 
171 {
172  QString filename = file.fileName();
173  if (filename.startsWith(QLatin1String("/tmp/")) || filename.contains("/Temp"))
174  {
175  if (m_ImageUrl.isEmpty() == false ||
176  KMessageBox::questionYesNo(nullptr, i18n("Remove temporary file %1 from disk?", filename),
177  i18n("Confirm Removal"), KStandardGuiItem::yes(), KStandardGuiItem::no(),
178  "imageviewer_temporary_file_removal") == KMessageBox::Yes)
179  QFile::remove(filename);
180  }
181 
183 }
184 
185 void ImageViewer::loadImageFromURL()
186 {
187 #ifndef KSTARS_LITE
188  QUrl saveURL = QUrl::fromLocalFile(file.fileName());
189 
190  if (!saveURL.isValid())
191  qWarning() << "tempfile-URL is malformed";
192 
194 
195  downloadJob.setProgressDialogEnabled(true, i18n("Download"),
196  i18n("Please wait while image is being downloaded..."));
197  connect(&downloadJob, SIGNAL(downloaded()), this, SLOT(downloadReady()));
198  connect(&downloadJob, SIGNAL(canceled()), this, SLOT(close()));
199  connect(&downloadJob, SIGNAL(error(QString)), this, SLOT(downloadError(QString)));
200 
201  downloadJob.get(m_ImageUrl);
202 #endif
203 }
204 
205 void ImageViewer::downloadReady()
206 {
207 #ifndef KSTARS_LITE
209 
210  if (file.open(QFile::WriteOnly))
211  {
212  file.write(downloadJob.downloadedData());
213  file.close(); // to get the newest information from the file and not any information from opening of the file
214 
215  if (file.exists())
216  {
217  showImage();
218  return;
219  }
220 
221  close();
222  }
223  else
224  KSNotification::error(file.errorString(), i18n("Image Viewer"));
225 #endif
226 }
227 
228 void ImageViewer::downloadError(const QString &errorString)
229 {
230 #ifndef KSTARS_LITE
232  KSNotification::error(errorString);
233 #endif
234 }
235 
236 bool ImageViewer::loadImage(const QString &filename)
237 {
238 #ifndef KSTARS_LITE
239  // If current file is temporary, remove from disk.
240  if (file.fileName().startsWith(QLatin1String("/tmp/")) || file.fileName().contains("/Temp"))
241  QFile::remove(file.fileName());
242 
243  file.setFileName(filename);
244  return showImage();
245 #else
246  return false;
247 #endif
248 }
249 
250 bool ImageViewer::showImage()
251 {
252 #ifndef KSTARS_LITE
253  QImage image;
254 
255  if (!image.load(file.fileName()))
256  {
257  KSNotification::error(i18n("Loading of the image %1 failed.", m_ImageUrl.url()));
258  close();
259  return false;
260  }
261 
262  fileIsImage = true; // we loaded the file and know now, that it is an image
263 
264  //If the image is larger than screen width and/or screen height,
265  //shrink it to fit the screen
266  QRect deskRect = QGuiApplication::primaryScreen()->geometry();
267  int w = deskRect.width(); // screen width
268  int h = deskRect.height(); // screen height
269 
270  if (image.width() <= w && image.height() > h) //Window is taller than desktop
271  image = image.scaled(int(image.width() * h / image.height()), h);
272  else if (image.height() <= h && image.width() > w) //window is wider than desktop
273  image = image.scaled(w, int(image.height() * w / image.width()));
274  else if (image.width() > w && image.height() > h) //window is too tall and too wide
275  {
276  //which needs to be shrunk least, width or height?
277  float fx = float(w) / float(image.width());
278  float fy = float(h) / float(image.height());
279  if (fx > fy) //width needs to be shrunk less, so shrink to fit in height
280  image = image.scaled(int(image.width() * fy), h);
281  else //vice versa
282  image = image.scaled(w, int(image.height() * fx));
283  }
284 
285  show(); // hide is default
286 
287  m_View->setImage(image);
288  w = image.width();
289 
290  //If the caption is wider than the image, set the window size
291  //to fit the caption
292  if (m_Caption->width() > w)
293  w = m_Caption->width();
294  //setFixedSize( w, image.height() + m_Caption->height() );
295 
296  resize(w, image.height());
297  update();
298  show();
299 
300  return true;
301 #else
302  return false;
303 #endif
304 }
305 
306 void ImageViewer::saveFileToDisc()
307 {
308 #ifndef KSTARS_LITE
309  QFileDialog dialog;
310 
311  QUrl newURL =
312  dialog.getSaveFileUrl(KStars::Instance(), i18nc("@title:window", "Save Image"), lastURL); // save-dialog with default filename
313  if (!newURL.isEmpty())
314  {
315  //QFile f (newURL.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).toLocalFile() + '/' + newURL.fileName());
316  QFile f(newURL.toLocalFile());
317  if (f.exists())
318  {
319  if ((KMessageBox::warningContinueCancel(static_cast<QWidget *>(parent()),
320  i18n("A file named \"%1\" already exists. "
321  "Overwrite it?",
322  newURL.fileName()),
323  i18n("Overwrite File?"), KStandardGuiItem::overwrite()) == KMessageBox::Cancel))
324  return;
325 
326  f.remove();
327  }
328 
329  lastURL = QUrl(newURL.toString(QUrl::RemoveFilename));
330 
331  saveFile(newURL);
332  }
333 #endif
334 }
335 
336 void ImageViewer::saveFile(QUrl &url)
337 {
338  // synchronous access to prevent segfaults
339 
340  //if (!KIO::NetAccess::file_copy (QUrl (file.fileName()), url, (QWidget*) 0))
341  //QUrl tmpURL((file.fileName()));
342  //tmpURL.setScheme("file");
343 
344  if (file.copy(url.toLocalFile()) == false)
345  //if (KIO::file_copy(tmpURL, url)->exec() == false)
346  {
347  QString text = i18n("Saving of the image %1 failed.", url.toString());
348 #ifndef KSTARS_LITE
349  KSNotification::error(text);
350 #else
351  qDebug() << Q_FUNC_INFO << text;
352 #endif
353  }
354 #ifndef KSTARS_LITE
355  else
356  KStars::Instance()->statusBar()->showMessage(i18n("Saved image to %1", url.toString()));
357 #endif
358 }
359 
360 void ImageViewer::invertColors()
361 {
362 #ifndef KSTARS_LITE
363  // Invert colors
364  m_View->invertPixels();
365  m_View->update();
366 #endif
367 }
368 
369 QJsonObject ImageViewer::metadata()
370 {
371 #ifndef KSTARS_LITE
372  QJsonObject md;
373  if (m_View && !m_View->m_Image.isNull())
374  {
375  md.insert("resolution", QString("%1x%2").arg(m_View->m_Image.width()).arg(m_View->m_Image.height()));
376  // sizeInBytes is only available in 5.10+
377  md.insert("size", KFormat().formatByteSize(m_View->m_Image.bytesPerLine() * m_View->m_Image.height()));
378  md.insert("bin", "1x1");
379  md.insert("bpp", QString::number(m_View->m_Image.bytesPerLine() / m_View->m_Image.width() * 8));
380  }
381  return md;
382 #endif
383 }
QString errorString() const const
QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags)
void setText(const QString &)
QString number(int n, int base)
int height() const const
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
virtual void reject()
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
bool remove()
virtual bool open(QIODevice::OpenMode mode) override
bool copy(const QString &newName)
void setPointSize(int pointSize)
void update()
QString url(QUrl::FormattingOptions options) const const
int pointSize() const const
QIcon fromTheme(const QString &name)
int width() const const
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
QString homePath()
void setAttribute(Qt::WidgetAttribute attribute, bool on)
void setFrameShape(QFrame::Shape)
bool close()
bool exists() const const
void setModal(bool modal)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool load(QIODevice *device, const char *format)
virtual QString fileName() const const override
virtual QString fileName() const const override
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
static KStars * Instance()
Definition: kstars.h:125
bool begin(QPaintDevice *device)
void showMessage(const QString &message, int timeout)
bool isValid() const const
void rejected()
QString toString(QUrl::FormattingOptions options) const const
bool end()
KeepAspectRatio
QString i18n(const char *text, const TYPE &arg...)
QJsonObject::iterator insert(const QString &key, const QJsonValue &value)
bool isEmpty() const const
RemoveFilename
QUrl getSaveFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options, const QStringList &supportedSchemes)
bool loadImage(const QString &filename)
loadImage Load image from local file and display it
QUrl fromLocalFile(const QString &localFile)
QString fileName(QUrl::ComponentFormattingOptions options) const const
WaitCursor
void setWindowTitle(const QString &)
void setFileName(const QString &name)
KGuiItem yes()
QString toLocalFile() const const
void addButton(QAbstractButton *button, QDialogButtonBox::ButtonRole role)
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
virtual void close() override
void setColor(QPalette::ColorGroup group, QPalette::ColorRole role, const QColor &color)
QStatusBar * statusBar() const const
void show()
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
ImageViewer(const QString &caption, QWidget *parent=nullptr)
Creates empty viewer.
Definition: imageviewer.cpp:79
void resize(int w, int h)
int height() const const
void setToolTip(const QString &)
void setOverrideCursor(const QCursor &cursor)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
void setSpacing(int spacing)
~ImageViewer() override
Destructor.
void setContentsMargins(int left, int top, int right, int bottom)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void setAutoFillBackground(bool enabled)
void restoreOverrideCursor()
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
void setLayout(QLayout *layout)
SmoothTransformation
ButtonCode questionYesNo(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonYes=KStandardGuiItem::yes(), const KGuiItem &buttonNo=KStandardGuiItem::no(), const QString &dontAskAgainName=QString(), Options options=Notify)
KGuiItem overwrite()
QObject * parent() const const
WA_DeleteOnClose
qint64 write(const char *data, qint64 maxSize)
int width() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Fri Aug 12 2022 04:00:54 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.