File indexing completed on 2024-04-14 03:40:55
0001 /* 0002 SPDX-FileCopyrightText: 2001 Thomas Kabelmann <tk78@gmx.de> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "imageviewer.h" 0008 #include "Options.h" 0009 0010 #ifndef KSTARS_LITE 0011 #include "kstars.h" 0012 #include <KMessageBox> 0013 #include <KFormat> 0014 #endif 0015 0016 #include "ksnotification.h" 0017 #include <QFileDialog> 0018 #include <QJsonObject> 0019 #include <QPainter> 0020 #include <QResizeEvent> 0021 #include <QScreen> 0022 #include <QStatusBar> 0023 #include <QTemporaryFile> 0024 #include <QVBoxLayout> 0025 #include <QPushButton> 0026 #include <QApplication> 0027 0028 QUrl ImageViewer::lastURL = QUrl::fromLocalFile(QDir::homePath()); 0029 0030 ImageLabel::ImageLabel(QWidget *parent) : QFrame(parent) 0031 { 0032 #ifndef KSTARS_LITE 0033 setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); 0034 setFrameStyle(QFrame::StyledPanel | QFrame::Plain); 0035 setLineWidth(2); 0036 #endif 0037 } 0038 0039 void ImageLabel::setImage(const QImage &img) 0040 { 0041 #ifndef KSTARS_LITE 0042 m_Image = img; 0043 pix = QPixmap::fromImage(m_Image); 0044 #endif 0045 } 0046 0047 void ImageLabel::invertPixels() 0048 { 0049 #ifndef KSTARS_LITE 0050 m_Image.invertPixels(); 0051 pix = QPixmap::fromImage(m_Image.scaled(width(), height(), Qt::KeepAspectRatio)); 0052 #endif 0053 } 0054 0055 void ImageLabel::paintEvent(QPaintEvent *) 0056 { 0057 #ifndef KSTARS_LITE 0058 QPainter p; 0059 p.begin(this); 0060 int x = 0; 0061 if (pix.width() < width()) 0062 x = (width() - pix.width()) / 2; 0063 p.drawPixmap(x, 0, pix); 0064 p.end(); 0065 #endif 0066 } 0067 0068 void ImageLabel::resizeEvent(QResizeEvent *event) 0069 { 0070 int w = pix.width(); 0071 int h = pix.height(); 0072 0073 if (event->size().width() == w && event->size().height() == h) 0074 return; 0075 0076 pix = QPixmap::fromImage(m_Image.scaled(event->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); 0077 } 0078 0079 ImageViewer::ImageViewer(const QString &caption, QWidget *parent) : QDialog(parent), fileIsImage(false), downloadJob(nullptr) 0080 { 0081 #ifndef KSTARS_LITE 0082 init(caption, QString()); 0083 #endif 0084 } 0085 0086 ImageViewer::ImageViewer(const QUrl &url, const QString &capText, QWidget *parent) 0087 : QDialog(parent), m_ImageUrl(url) 0088 { 0089 #ifndef KSTARS_LITE 0090 init(url.fileName(), capText); 0091 0092 // check URL 0093 if (!m_ImageUrl.isValid()) 0094 qDebug() << Q_FUNC_INFO << "URL is malformed: " << m_ImageUrl; 0095 0096 if (m_ImageUrl.isLocalFile()) 0097 { 0098 loadImage(m_ImageUrl.toLocalFile()); 0099 return; 0100 } 0101 0102 { 0103 QTemporaryFile tempfile; 0104 tempfile.open(); 0105 file.setFileName(tempfile.fileName()); 0106 } // we just need the name and delete the tempfile from disc; if we don't do it, a dialog will be show 0107 0108 loadImageFromURL(); 0109 #endif 0110 } 0111 0112 void ImageViewer::init(QString caption, QString capText) 0113 { 0114 #ifndef KSTARS_LITE 0115 setAttribute(Qt::WA_DeleteOnClose, true); 0116 setModal(false); 0117 setWindowTitle(i18nc("@title:window", "KStars image viewer: %1", caption)); 0118 0119 // Create widget 0120 QFrame *page = new QFrame(this); 0121 0122 //setMainWidget( page ); 0123 QVBoxLayout *mainLayout = new QVBoxLayout; 0124 mainLayout->addWidget(page); 0125 setLayout(mainLayout); 0126 0127 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); 0128 mainLayout->addWidget(buttonBox); 0129 connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); 0130 0131 QPushButton *invertB = new QPushButton(i18n("Invert colors")); 0132 invertB->setToolTip(i18n("Reverse colors of the image. This is useful to enhance contrast at times. This affects " 0133 "only the display and not the saving.")); 0134 QPushButton *saveB = new QPushButton(QIcon::fromTheme("document-save"), i18n("Save")); 0135 saveB->setToolTip(i18n("Save the image to disk")); 0136 0137 buttonBox->addButton(invertB, QDialogButtonBox::ActionRole); 0138 buttonBox->addButton(saveB, QDialogButtonBox::ActionRole); 0139 0140 connect(invertB, SIGNAL(clicked()), this, SLOT(invertColors())); 0141 connect(saveB, SIGNAL(clicked()), this, SLOT(saveFileToDisc())); 0142 0143 m_View = new ImageLabel(page); 0144 m_View->setAutoFillBackground(true); 0145 m_Caption = new QLabel(page); 0146 m_Caption->setAutoFillBackground(true); 0147 m_Caption->setFrameShape(QFrame::StyledPanel); 0148 m_Caption->setText(capText); 0149 // Add layout 0150 QVBoxLayout *vlay = new QVBoxLayout(page); 0151 vlay->setSpacing(0); 0152 vlay->setContentsMargins(0, 0, 0, 0); 0153 vlay->addWidget(m_View); 0154 vlay->addWidget(m_Caption); 0155 0156 //Reverse colors 0157 QPalette p = palette(); 0158 p.setColor(QPalette::Window, palette().color(QPalette::WindowText)); 0159 p.setColor(QPalette::WindowText, palette().color(QPalette::Window)); 0160 m_Caption->setPalette(p); 0161 m_View->setPalette(p); 0162 0163 //If the caption is wider than the image, try to shrink the font a bit 0164 QFont capFont = m_Caption->font(); 0165 capFont.setPointSize(capFont.pointSize() - 2); 0166 m_Caption->setFont(capFont); 0167 #endif 0168 } 0169 0170 ImageViewer::~ImageViewer() 0171 { 0172 QString filename = file.fileName(); 0173 if (filename.startsWith(QLatin1String("/tmp/")) || filename.contains("/Temp")) 0174 { 0175 if (m_ImageUrl.isEmpty() == false || 0176 KMessageBox::questionYesNo(nullptr, i18n("Remove temporary file %1 from disk?", filename), 0177 i18n("Confirm Removal"), KStandardGuiItem::yes(), KStandardGuiItem::no(), 0178 "imageviewer_temporary_file_removal") == KMessageBox::Yes) 0179 QFile::remove(filename); 0180 } 0181 0182 QApplication::restoreOverrideCursor(); 0183 } 0184 0185 void ImageViewer::loadImageFromURL() 0186 { 0187 #ifndef KSTARS_LITE 0188 QUrl saveURL = QUrl::fromLocalFile(file.fileName()); 0189 0190 if (!saveURL.isValid()) 0191 qWarning() << "tempfile-URL is malformed"; 0192 0193 QApplication::setOverrideCursor(Qt::WaitCursor); 0194 0195 downloadJob.setProgressDialogEnabled(true, i18n("Download"), 0196 i18n("Please wait while image is being downloaded...")); 0197 connect(&downloadJob, SIGNAL(downloaded()), this, SLOT(downloadReady())); 0198 connect(&downloadJob, SIGNAL(canceled()), this, SLOT(close())); 0199 connect(&downloadJob, SIGNAL(error(QString)), this, SLOT(downloadError(QString))); 0200 0201 downloadJob.get(m_ImageUrl); 0202 #endif 0203 } 0204 0205 void ImageViewer::downloadReady() 0206 { 0207 #ifndef KSTARS_LITE 0208 QApplication::restoreOverrideCursor(); 0209 0210 if (file.open(QFile::WriteOnly)) 0211 { 0212 file.write(downloadJob.downloadedData()); 0213 file.close(); // to get the newest information from the file and not any information from opening of the file 0214 0215 if (file.exists()) 0216 { 0217 showImage(); 0218 return; 0219 } 0220 0221 close(); 0222 } 0223 else 0224 KSNotification::error(file.errorString(), i18n("Image Viewer")); 0225 #endif 0226 } 0227 0228 void ImageViewer::downloadError(const QString &errorString) 0229 { 0230 #ifndef KSTARS_LITE 0231 QApplication::restoreOverrideCursor(); 0232 KSNotification::error(errorString); 0233 #endif 0234 } 0235 0236 bool ImageViewer::loadImage(const QString &filename) 0237 { 0238 #ifndef KSTARS_LITE 0239 // If current file is temporary, remove from disk. 0240 if (file.fileName().startsWith(QLatin1String("/tmp/")) || file.fileName().contains("/Temp")) 0241 QFile::remove(file.fileName()); 0242 0243 file.setFileName(filename); 0244 return showImage(); 0245 #else 0246 return false; 0247 #endif 0248 } 0249 0250 bool ImageViewer::showImage() 0251 { 0252 #ifndef KSTARS_LITE 0253 QImage image; 0254 0255 if (!image.load(file.fileName())) 0256 { 0257 KSNotification::error(i18n("Loading of the image %1 failed.", m_ImageUrl.url())); 0258 close(); 0259 return false; 0260 } 0261 0262 fileIsImage = true; // we loaded the file and know now, that it is an image 0263 0264 //If the image is larger than screen width and/or screen height, 0265 //shrink it to fit the screen 0266 QRect deskRect = QGuiApplication::primaryScreen()->geometry(); 0267 int w = deskRect.width(); // screen width 0268 int h = deskRect.height(); // screen height 0269 0270 if (image.width() <= w && image.height() > h) //Window is taller than desktop 0271 image = image.scaled(int(image.width() * h / image.height()), h); 0272 else if (image.height() <= h && image.width() > w) //window is wider than desktop 0273 image = image.scaled(w, int(image.height() * w / image.width())); 0274 else if (image.width() > w && image.height() > h) //window is too tall and too wide 0275 { 0276 //which needs to be shrunk least, width or height? 0277 float fx = float(w) / float(image.width()); 0278 float fy = float(h) / float(image.height()); 0279 if (fx > fy) //width needs to be shrunk less, so shrink to fit in height 0280 image = image.scaled(int(image.width() * fy), h); 0281 else //vice versa 0282 image = image.scaled(w, int(image.height() * fx)); 0283 } 0284 0285 show(); // hide is default 0286 0287 m_View->setImage(image); 0288 w = image.width(); 0289 0290 //If the caption is wider than the image, set the window size 0291 //to fit the caption 0292 if (m_Caption->width() > w) 0293 w = m_Caption->width(); 0294 //setFixedSize( w, image.height() + m_Caption->height() ); 0295 0296 resize(w, image.height()); 0297 update(); 0298 show(); 0299 0300 return true; 0301 #else 0302 return false; 0303 #endif 0304 } 0305 0306 void ImageViewer::saveFileToDisc() 0307 { 0308 #ifndef KSTARS_LITE 0309 QFileDialog dialog; 0310 0311 QUrl newURL = 0312 dialog.getSaveFileUrl(KStars::Instance(), i18nc("@title:window", "Save Image"), lastURL); // save-dialog with default filename 0313 if (!newURL.isEmpty()) 0314 { 0315 //QFile f (newURL.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).toLocalFile() + '/' + newURL.fileName()); 0316 QFile f(newURL.toLocalFile()); 0317 if (f.exists()) 0318 { 0319 if ((KMessageBox::warningContinueCancel(static_cast<QWidget *>(parent()), 0320 i18n("A file named \"%1\" already exists. " 0321 "Overwrite it?", 0322 newURL.fileName()), 0323 i18n("Overwrite File?"), KStandardGuiItem::overwrite()) == KMessageBox::Cancel)) 0324 return; 0325 0326 f.remove(); 0327 } 0328 0329 lastURL = QUrl(newURL.toString(QUrl::RemoveFilename)); 0330 0331 saveFile(newURL); 0332 } 0333 #endif 0334 } 0335 0336 void ImageViewer::saveFile(QUrl &url) 0337 { 0338 // synchronous access to prevent segfaults 0339 0340 //if (!KIO::NetAccess::file_copy (QUrl (file.fileName()), url, (QWidget*) 0)) 0341 //QUrl tmpURL((file.fileName())); 0342 //tmpURL.setScheme("file"); 0343 0344 if (file.copy(url.toLocalFile()) == false) 0345 //if (KIO::file_copy(tmpURL, url)->exec() == false) 0346 { 0347 QString text = i18n("Saving of the image %1 failed.", url.toString()); 0348 #ifndef KSTARS_LITE 0349 KSNotification::error(text); 0350 #else 0351 qDebug() << Q_FUNC_INFO << text; 0352 #endif 0353 } 0354 #ifndef KSTARS_LITE 0355 else 0356 KStars::Instance()->statusBar()->showMessage(i18n("Saved image to %1", url.toString())); 0357 #endif 0358 } 0359 0360 void ImageViewer::invertColors() 0361 { 0362 #ifndef KSTARS_LITE 0363 // Invert colors 0364 m_View->invertPixels(); 0365 m_View->update(); 0366 #endif 0367 } 0368 0369 QJsonObject ImageViewer::metadata() 0370 { 0371 #ifndef KSTARS_LITE 0372 QJsonObject md; 0373 if (m_View && !m_View->m_Image.isNull()) 0374 { 0375 md.insert("resolution", QString("%1x%2").arg(m_View->m_Image.width()).arg(m_View->m_Image.height())); 0376 // sizeInBytes is only available in 5.10+ 0377 md.insert("size", KFormat().formatByteSize(m_View->m_Image.bytesPerLine() * m_View->m_Image.height())); 0378 md.insert("bin", "1x1"); 0379 md.insert("bpp", QString::number(m_View->m_Image.bytesPerLine() / m_View->m_Image.width() * 8)); 0380 } 0381 return md; 0382 #endif 0383 }