Kstars

imageviewer.cpp
1/*
2 SPDX-FileCopyrightText: 2001 Thomas Kabelmann <tk78@gmx.de>
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
28QUrl ImageViewer::lastURL = QUrl::fromLocalFile(QDir::homePath());
29
30ImageLabel::ImageLabel(QWidget *parent) : QFrame(parent)
31{
32#ifndef KSTARS_LITE
34 setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
35 setLineWidth(2);
36#endif
37}
38
39void ImageLabel::setImage(const QImage &img)
40{
41#ifndef KSTARS_LITE
42 m_Image = img;
43 pix = QPixmap::fromImage(m_Image);
44#endif
45}
46
47void ImageLabel::invertPixels()
48{
49#ifndef KSTARS_LITE
50 m_Image.invertPixels();
52#endif
53}
54
55void 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
68void 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
79ImageViewer::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
86ImageViewer::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
112void 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
170ImageViewer::~ImageViewer()
171{
172 QString filename = file.fileName();
173 if (filename.startsWith(QLatin1String("/tmp/")) || filename.contains("/Temp"))
174 {
175 if (m_ImageUrl.isEmpty() == false ||
176 KMessageBox::warningContinueCancel(nullptr, i18n("Remove temporary file %1 from disk?", filename),
177 i18n("Confirm Removal")) == KMessageBox::Continue)
178 QFile::remove(filename);
179 }
180
182}
183
184void ImageViewer::loadImageFromURL()
185{
186#ifndef KSTARS_LITE
187 QUrl saveURL = QUrl::fromLocalFile(file.fileName());
188
189 if (!saveURL.isValid())
190 qWarning() << "tempfile-URL is malformed";
191
193
194 downloadJob.setProgressDialogEnabled(true, i18n("Download"),
195 i18n("Please wait while image is being downloaded..."));
196 connect(&downloadJob, SIGNAL(downloaded()), this, SLOT(downloadReady()));
197 connect(&downloadJob, SIGNAL(canceled()), this, SLOT(close()));
198 connect(&downloadJob, SIGNAL(error(QString)), this, SLOT(downloadError(QString)));
199
200 downloadJob.get(m_ImageUrl);
201#endif
202}
203
204void ImageViewer::downloadReady()
205{
206#ifndef KSTARS_LITE
208
209 if (file.open(QFile::WriteOnly))
210 {
211 file.write(downloadJob.downloadedData());
212 file.close(); // to get the newest information from the file and not any information from opening of the file
213
214 if (file.exists())
215 {
216 showImage();
217 return;
218 }
219
220 close();
221 }
222 else
223 KSNotification::error(file.errorString(), i18n("Image Viewer"));
224#endif
225}
226
227void ImageViewer::downloadError(const QString &errorString)
228{
229#ifndef KSTARS_LITE
231 KSNotification::error(errorString);
232#endif
233}
234
235bool ImageViewer::loadImage(const QString &filename)
236{
237#ifndef KSTARS_LITE
238 // If current file is temporary, remove from disk.
239 if (file.fileName().startsWith(QLatin1String("/tmp/")) || file.fileName().contains("/Temp"))
240 QFile::remove(file.fileName());
241
242 file.setFileName(filename);
243 return showImage();
244#else
245 return false;
246#endif
247}
248
249bool ImageViewer::showImage()
250{
251#ifndef KSTARS_LITE
252 QImage image;
253
254 if (!image.load(file.fileName()))
255 {
256 KSNotification::error(i18n("Loading of the image %1 failed.", m_ImageUrl.url()));
257 close();
258 return false;
259 }
260
261 fileIsImage = true; // we loaded the file and know now, that it is an image
262
263 //If the image is larger than screen width and/or screen height,
264 //shrink it to fit the screen
265 QRect deskRect = QGuiApplication::primaryScreen()->geometry();
266 int w = deskRect.width(); // screen width
267 int h = deskRect.height(); // screen height
268
269 if (image.width() <= w && image.height() > h) //Window is taller than desktop
270 image = image.scaled(int(image.width() * h / image.height()), h);
271 else if (image.height() <= h && image.width() > w) //window is wider than desktop
272 image = image.scaled(w, int(image.height() * w / image.width()));
273 else if (image.width() > w && image.height() > h) //window is too tall and too wide
274 {
275 //which needs to be shrunk least, width or height?
276 float fx = float(w) / float(image.width());
277 float fy = float(h) / float(image.height());
278 if (fx > fy) //width needs to be shrunk less, so shrink to fit in height
279 image = image.scaled(int(image.width() * fy), h);
280 else //vice versa
281 image = image.scaled(w, int(image.height() * fx));
282 }
283
284 show(); // hide is default
285
286 m_View->setImage(image);
287 w = image.width();
288
289 //If the caption is wider than the image, set the window size
290 //to fit the caption
291 if (m_Caption->width() > w)
292 w = m_Caption->width();
293 //setFixedSize( w, image.height() + m_Caption->height() );
294
295 resize(w, image.height());
296 update();
297 show();
298
299 return true;
300#else
301 return false;
302#endif
303}
304
305void ImageViewer::saveFileToDisc()
306{
307#ifndef KSTARS_LITE
308 QFileDialog dialog;
309
310 QUrl newURL =
311 dialog.getSaveFileUrl(KStars::Instance(), i18nc("@title:window", "Save Image"), lastURL); // save-dialog with default filename
312 if (!newURL.isEmpty())
313 {
314 //QFile f (newURL.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).toLocalFile() + '/' + newURL.fileName());
315 QFile f(newURL.toLocalFile());
316 if (f.exists())
317 {
319 i18n("A file named \"%1\" already exists. "
320 "Overwrite it?",
321 newURL.fileName()),
322 i18n("Overwrite File?"), KStandardGuiItem::overwrite()) == KMessageBox::Cancel))
323 return;
324
325 f.remove();
326 }
327
328 lastURL = QUrl(newURL.toString(QUrl::RemoveFilename));
329
330 saveFile(newURL);
331 }
332#endif
333}
334
335void ImageViewer::saveFile(QUrl &url)
336{
337 // synchronous access to prevent segfaults
338
339 //if (!KIO::NetAccess::file_copy (QUrl (file.fileName()), url, (QWidget*) 0))
340 //QUrl tmpURL((file.fileName()));
341 //tmpURL.setScheme("file");
342
343 if (file.copy(url.toLocalFile()) == false)
344 //if (KIO::file_copy(tmpURL, url)->exec() == false)
345 {
346 QString text = i18n("Saving of the image %1 failed.", url.toString());
347#ifndef KSTARS_LITE
348 KSNotification::error(text);
349#else
350 qDebug() << Q_FUNC_INFO << text;
351#endif
352 }
353#ifndef KSTARS_LITE
354 else
355 KStars::Instance()->statusBar()->showMessage(i18n("Saved image to %1", url.toString()));
356#endif
357}
358
359void ImageViewer::invertColors()
360{
361#ifndef KSTARS_LITE
362 // Invert colors
363 m_View->invertPixels();
364 m_View->update();
365#endif
366}
367
368QJsonObject ImageViewer::metadata()
369{
370#ifndef KSTARS_LITE
371 QJsonObject md;
372 if (m_View && !m_View->m_Image.isNull())
373 {
374 md.insert("resolution", QString("%1x%2").arg(m_View->m_Image.width()).arg(m_View->m_Image.height()));
375 // sizeInBytes is only available in 5.10+
376 md.insert("size", KFormat().formatByteSize(m_View->m_Image.bytesPerLine() * m_View->m_Image.height()));
377 md.insert("bin", "1x1");
378 md.insert("bpp", QString::number(m_View->m_Image.bytesPerLine() / m_View->m_Image.width() * 8));
379 }
380 return md;
381#endif
382}
static KStars * Instance()
Definition kstars.h:123
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
void init(KXmlGuiWindow *window, KGameDifficulty *difficulty=nullptr)
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)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
KGuiItem overwrite()
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
virtual void setSpacing(int spacing) override
void setModal(bool modal)
virtual void reject()
void rejected()
QPushButton * addButton(StandardButton button)
QString homePath()
bool copy(const QString &fileName, const QString &newName)
bool exists(const QString &fileName)
virtual QString fileName() const const override
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
bool remove()
void setFileName(const QString &name)
virtual void close() override
QUrl getSaveFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, Options options, const QStringList &supportedSchemes)
int pointSize() const const
void setPointSize(int pointSize)
virtual bool event(QEvent *e) override
void setFrameShape(Shape)
void restoreOverrideCursor()
void setOverrideCursor(const QCursor &cursor)
QIcon fromTheme(const QString &name)
qsizetype bytesPerLine() const const
int height() const const
void invertPixels(InvertMode mode)
bool isNull() const const
bool load(QIODevice *device, const char *format)
QImage scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
int width() const const
QString errorString() const const
qint64 write(const QByteArray &data)
iterator insert(QLatin1StringView key, const QJsonValue &value)
void setText(const QString &)
void setContentsMargins(const QMargins &margins)
QStatusBar * statusBar() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
bool begin(QPaintDevice *device)
void drawPixmap(const QPoint &point, const QPixmap &pixmap)
bool end()
void setColor(ColorGroup group, ColorRole role, const QColor &color)
QPixmap fromImage(QImage &&image, Qt::ImageConversionFlags flags)
int height() const const
int width() const const
int height() const const
int width() const const
void showMessage(const QString &message, int timeout)
QString arg(Args &&... args) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString number(double n, char format, int precision)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
KeepAspectRatio
WaitCursor
SmoothTransformation
WA_DeleteOnClose
virtual QString fileName() const const override
RemoveFilename
QString fileName(ComponentFormattingOptions options) const const
QUrl fromLocalFile(const QString &localFile)
bool isEmpty() const const
bool isValid() const const
QString toLocalFile() const const
QString toString(FormattingOptions options) const const
void setAutoFillBackground(bool enabled)
bool close()
void setAttribute(Qt::WidgetAttribute attribute, bool on)
void setLayout(QLayout *layout)
void show()
void resize(const QSize &)
void setToolTip(const QString &)
void update()
void setWindowTitle(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:15:10 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.