File indexing completed on 2024-05-05 04:19:16
0001 // vim: set tabstop=4 shiftwidth=4 expandtab: 0002 /* 0003 Gwenview: an image viewer 0004 Copyright 2008 Aurélien Gâteau <agateau@kde.org> 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License 0008 as published by the Free Software Foundation; either version 2 0009 of the License, or (at your option) any later version. 0010 0011 This program is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 GNU General Public License for more details. 0015 0016 You should have received a copy of the GNU General Public License 0017 along with this program; if not, write to the Free Software 0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. 0019 0020 */ 0021 // Self 0022 #include "gvcore.h" 0023 0024 // Qt 0025 #include <QApplication> 0026 #include <QHBoxLayout> 0027 #include <QImageWriter> 0028 #include <QLabel> 0029 #include <QMimeDatabase> 0030 #include <QSpacerItem> 0031 #include <QSpinBox> 0032 #include <QUrl> 0033 0034 // KF 0035 #include <KColorScheme> 0036 #include <KColorUtils> 0037 #include <KFileCustomDialog> 0038 #include <KFileWidget> 0039 #include <KLocalizedString> 0040 #include <KMessageBox> 0041 0042 // Local 0043 #include "gwenview_app_debug.h" 0044 #include <lib/binder.h> 0045 #include <lib/document/documentfactory.h> 0046 #include <lib/document/documentjob.h> 0047 #include <lib/document/savejob.h> 0048 #include <lib/gwenviewconfig.h> 0049 #include <lib/historymodel.h> 0050 #include <lib/hud/hudbutton.h> 0051 #include <lib/hud/hudmessagebubble.h> 0052 #include <lib/mimetypeutils.h> 0053 #include <lib/recentfilesmodel.h> 0054 #include <lib/semanticinfo/semanticinfodirmodel.h> 0055 #include <lib/semanticinfo/sorteddirmodel.h> 0056 #include <lib/transformimageoperation.h> 0057 #include <mainwindow.h> 0058 #include <saveallhelper.h> 0059 #include <viewmainpage.h> 0060 0061 namespace Gwenview 0062 { 0063 struct GvCorePrivate { 0064 GvCore *q = nullptr; 0065 MainWindow *mMainWindow = nullptr; 0066 SortedDirModel *mDirModel = nullptr; 0067 HistoryModel *mRecentFoldersModel = nullptr; 0068 RecentFilesModel *mRecentFilesModel = nullptr; 0069 QPalette mPalettes[4]; 0070 QString mFullScreenPaletteName; 0071 int configFileJPEGQualityValue = GwenviewConfig::jPEGQuality(); 0072 0073 KFileCustomDialog *createSaveAsDialog(const QUrl &url) 0074 { 0075 // Build the JPEG quality chooser custom widget 0076 auto JPEGQualityChooserWidget = new QWidget; 0077 JPEGQualityChooserWidget->setVisible(false); // shown only for JPEGs 0078 0079 auto JPEGQualityChooserLabel = new QLabel; 0080 JPEGQualityChooserLabel->setText(i18n("Image quality:")); 0081 JPEGQualityChooserLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 0082 0083 auto JPEGQualityChooserSpinBox = new QSpinBox; 0084 JPEGQualityChooserSpinBox->setMinimum(1); 0085 JPEGQualityChooserSpinBox->setMaximum(100); 0086 JPEGQualityChooserSpinBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 0087 JPEGQualityChooserSpinBox->setSuffix(i18nc("Spinbox suffix; percentage 1 - 100", "%")); 0088 configFileJPEGQualityValue = GwenviewConfig::jPEGQuality(); 0089 JPEGQualityChooserSpinBox->setValue(configFileJPEGQualityValue); 0090 0091 // Temporarily change JPEG quality value 0092 QObject::connect(JPEGQualityChooserSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), JPEGQualityChooserSpinBox, [=](int value) { 0093 GwenviewConfig::setJPEGQuality(value); 0094 }); 0095 0096 auto horizontalSpacer = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Fixed); 0097 0098 auto JPEGQualityChooserLayout = new QHBoxLayout(JPEGQualityChooserWidget); 0099 JPEGQualityChooserLayout->setContentsMargins(0, 0, 0, 0); 0100 JPEGQualityChooserLayout->addWidget(JPEGQualityChooserLabel); 0101 JPEGQualityChooserLayout->addWidget(JPEGQualityChooserSpinBox); 0102 JPEGQualityChooserLayout->addItem(horizontalSpacer); 0103 0104 // Set up the dialog 0105 auto dialog = new KFileCustomDialog(mMainWindow); 0106 dialog->setAttribute(Qt::WA_DeleteOnClose); 0107 dialog->setModal(true); 0108 KFileWidget *fileWidget = dialog->fileWidget(); 0109 dialog->setCustomWidget(JPEGQualityChooserWidget); 0110 fileWidget->setConfirmOverwrite(true); 0111 dialog->setOperationMode(KFileWidget::Saving); 0112 dialog->setWindowTitle(i18nc("@title:window", "Save Image")); 0113 // Temporary workaround for selectUrl() not setting the 0114 // initial directory to url (removed in D4193) 0115 dialog->setUrl(url.adjusted(QUrl::RemoveFilename)); 0116 fileWidget->setSelectedUrl(url); 0117 0118 QList<KFileFilter> filters; 0119 for (const QByteArray &mimeName : QImageWriter::supportedMimeTypes()) { 0120 filters << KFileFilter::fromMimeType(mimeName); 0121 } 0122 0123 fileWidget->setFilters(filters, KFileFilter::fromMimeType(MimeTypeUtils::urlMimeType(url))); 0124 0125 // Only show the lossy image quality chooser when saving a lossy image 0126 QObject::connect(fileWidget, &KFileWidget::filterChanged, JPEGQualityChooserWidget, [=](const KFileFilter &filter) { 0127 const bool hasQuality = std::any_of(filter.mimePatterns().constBegin(), filter.mimePatterns().constEnd(), [](const QString &mime) { 0128 return mime.contains(QLatin1String("jpeg")) || mime.contains(QLatin1String("jxl")) || mime.contains(QLatin1String("webp")) 0129 || mime.contains(QLatin1String("avif")) || mime.contains(QLatin1String("heif")) || mime.contains(QLatin1String("heic")); 0130 }); 0131 0132 JPEGQualityChooserWidget->setVisible(hasQuality); 0133 }); 0134 0135 return dialog; 0136 } 0137 0138 void setupPalettes() 0139 { 0140 // Normal 0141 KSharedConfigPtr config = KSharedConfig::openConfig(); 0142 mPalettes[GvCore::NormalPalette] = KColorScheme::createApplicationPalette(config); 0143 QPalette viewPalette = mPalettes[GvCore::NormalPalette]; 0144 0145 DocumentView::BackgroundColorMode colorMode = GwenviewConfig::backgroundColorMode(); 0146 const bool usingLightTheme = qApp->palette().base().color().lightness() > qApp->palette().text().color().lightness(); 0147 0148 if ((usingLightTheme && colorMode == DocumentView::BackgroundColorMode::Dark) 0149 || (!usingLightTheme && colorMode == DocumentView::BackgroundColorMode::Light)) { 0150 viewPalette.setColor(QPalette::Base, mPalettes[GvCore::NormalPalette].color(QPalette::Text)); 0151 viewPalette.setColor(QPalette::Text, mPalettes[GvCore::NormalPalette].color(QPalette::Base)); 0152 viewPalette.setColor(QPalette::Window, mPalettes[GvCore::NormalPalette].color(QPalette::WindowText)); 0153 viewPalette.setColor(QPalette::WindowText, mPalettes[GvCore::NormalPalette].color(QPalette::Window)); 0154 viewPalette.setColor(QPalette::Button, mPalettes[GvCore::NormalPalette].color(QPalette::ButtonText)); 0155 viewPalette.setColor(QPalette::ButtonText, mPalettes[GvCore::NormalPalette].color(QPalette::Button)); 0156 viewPalette.setColor(QPalette::ToolTipBase, mPalettes[GvCore::NormalPalette].color(QPalette::ToolTipText)); 0157 viewPalette.setColor(QPalette::ToolTipText, mPalettes[GvCore::NormalPalette].color(QPalette::ToolTipBase)); 0158 } else if (colorMode == DocumentView::BackgroundColorMode::Neutral) { 0159 QColor base = KColorUtils::mix(mPalettes[GvCore::NormalPalette].color(QPalette::Base), mPalettes[GvCore::NormalPalette].color(QPalette::Text), 0.5); 0160 QColor window = 0161 KColorUtils::mix(mPalettes[GvCore::NormalPalette].color(QPalette::Window), mPalettes[GvCore::NormalPalette].color(QPalette::WindowText), 0.5); 0162 QColor button = 0163 KColorUtils::mix(mPalettes[GvCore::NormalPalette].color(QPalette::Button), mPalettes[GvCore::NormalPalette].color(QPalette::ButtonText), 0.5); 0164 QColor toolTipBase = KColorUtils::mix(mPalettes[GvCore::NormalPalette].color(QPalette::ToolTipBase), 0165 mPalettes[GvCore::NormalPalette].color(QPalette::ToolTipText), 0166 0.5); 0167 viewPalette.setColor(QPalette::Base, base); 0168 viewPalette.setColor(QPalette::Text, base.lightnessF() > 0.5 ? Qt::black : Qt::white); 0169 viewPalette.setColor(QPalette::Window, window); 0170 viewPalette.setColor(QPalette::WindowText, base.lightnessF() > 0.5 ? Qt::black : Qt::white); 0171 viewPalette.setColor(QPalette::Button, button); 0172 viewPalette.setColor(QPalette::ButtonText, base.lightnessF() > 0.5 ? Qt::black : Qt::white); 0173 viewPalette.setColor(QPalette::ToolTipBase, toolTipBase); 0174 viewPalette.setColor(QPalette::ToolTipText, base.lightnessF() > 0.5 ? Qt::black : Qt::white); 0175 } 0176 0177 mPalettes[GvCore::NormalViewPalette] = viewPalette; 0178 0179 // Fullscreen 0180 QString name = GwenviewConfig::fullScreenColorScheme(); 0181 if (name.isEmpty()) { 0182 // Default color scheme 0183 mFullScreenPaletteName = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("color-schemes/fullscreen.colors")); 0184 config = KSharedConfig::openConfig(mFullScreenPaletteName); 0185 } else if (name.contains('/')) { 0186 // Full path to a .colors file 0187 mFullScreenPaletteName = name; 0188 config = KSharedConfig::openConfig(mFullScreenPaletteName); 0189 } else { 0190 // Standard KDE color scheme 0191 mFullScreenPaletteName = QStringLiteral("color-schemes/%1.colors").arg(name); 0192 config = KSharedConfig::openConfig(mFullScreenPaletteName, KConfig::FullConfig, QStandardPaths::AppDataLocation); 0193 } 0194 mPalettes[GvCore::FullScreenPalette] = KColorScheme::createApplicationPalette(config); 0195 0196 // If we are using the default palette, adjust it to match the system color scheme 0197 if (name.isEmpty()) { 0198 adjustDefaultFullScreenPalette(); 0199 } 0200 0201 // FullScreenView has either a solid black color or a textured background 0202 viewPalette = mPalettes[GvCore::FullScreenPalette]; 0203 QPixmap bgTexture(256, 256); 0204 if (Gwenview::GwenviewConfig::fullScreenBackground() == Gwenview::FullScreenBackground::Black) { 0205 bgTexture.fill(Qt::black); 0206 } else { 0207 QString path = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("images/background.png")); 0208 bgTexture = path; 0209 } 0210 viewPalette.setBrush(QPalette::Base, bgTexture); 0211 mPalettes[GvCore::FullScreenViewPalette] = viewPalette; 0212 } 0213 0214 void adjustDefaultFullScreenPalette() 0215 { 0216 // The Fullscreen palette by default does not use the system color scheme, and therefore uses an 'accent' color 0217 // of blue. So for every color group/role combination that uses the accent color, we use a muted version of the 0218 // Normal palette. We also use the normal HighlightedText color so it properly contrasts with Highlight. 0219 const QPalette normalPal = mPalettes[GvCore::NormalPalette]; 0220 QPalette fullscreenPal = mPalettes[GvCore::FullScreenPalette]; 0221 0222 // Colors from the normal palette (source of the system theme's accent color) 0223 const QColor normalToolTipBase = normalPal.color(QPalette::Normal, QPalette::ToolTipBase); 0224 const QColor normalToolTipText = normalPal.color(QPalette::Normal, QPalette::ToolTipText); 0225 const QColor normalHighlight = normalPal.color(QPalette::Normal, QPalette::Highlight); 0226 const QColor normalHighlightedText = normalPal.color(QPalette::Normal, QPalette::HighlightedText); 0227 const QColor normalLink = normalPal.color(QPalette::Normal, QPalette::Link); 0228 const QColor normalActiveToolTipBase = normalPal.color(QPalette::Active, QPalette::ToolTipBase); 0229 const QColor normalActiveToolTipText = normalPal.color(QPalette::Active, QPalette::ToolTipText); 0230 const QColor normalActiveHighlight = normalPal.color(QPalette::Active, QPalette::Highlight); 0231 const QColor normalActiveHighlightedText = normalPal.color(QPalette::Active, QPalette::HighlightedText); 0232 const QColor normalActiveLink = normalPal.color(QPalette::Active, QPalette::Link); 0233 const QColor normalDisabledToolTipBase = normalPal.color(QPalette::Disabled, QPalette::ToolTipBase); 0234 const QColor normalDisabledToolTipText = normalPal.color(QPalette::Disabled, QPalette::ToolTipText); 0235 // Note: Disabled Highlight missing as they do not use the accent color 0236 const QColor normalDisabledLink = normalPal.color(QPalette::Disabled, QPalette::Link); 0237 const QColor normalInactiveToolTipBase = normalPal.color(QPalette::Inactive, QPalette::ToolTipBase); 0238 const QColor normalInactiveToolTipText = normalPal.color(QPalette::Inactive, QPalette::ToolTipText); 0239 const QColor normalInactiveHighlight = normalPal.color(QPalette::Inactive, QPalette::Highlight); 0240 const QColor normalInactiveHighlightedText = normalPal.color(QPalette::Inactive, QPalette::HighlightedText); 0241 const QColor normalInactiveLink = normalPal.color(QPalette::Inactive, QPalette::Link); 0242 0243 // Colors of the fullscreen palette which we will be modifying 0244 QColor fullScreenToolTipBase = fullscreenPal.color(QPalette::Normal, QPalette::ToolTipBase); 0245 QColor fullScreenToolTipText = fullscreenPal.color(QPalette::Normal, QPalette::ToolTipText); 0246 QColor fullScreenHighlight = fullscreenPal.color(QPalette::Normal, QPalette::Highlight); 0247 QColor fullScreenLink = fullscreenPal.color(QPalette::Normal, QPalette::Link); 0248 QColor fullScreenActiveToolTipBase = fullscreenPal.color(QPalette::Active, QPalette::ToolTipBase); 0249 QColor fullScreenActiveToolTipText = fullscreenPal.color(QPalette::Active, QPalette::ToolTipText); 0250 QColor fullScreenActiveHighlight = fullscreenPal.color(QPalette::Active, QPalette::Highlight); 0251 QColor fullScreenActiveLink = fullscreenPal.color(QPalette::Active, QPalette::Link); 0252 QColor fullScreenDisabledToolTipBase = fullscreenPal.color(QPalette::Disabled, QPalette::ToolTipBase); 0253 QColor fullScreenDisabledToolTipText = fullscreenPal.color(QPalette::Disabled, QPalette::ToolTipText); 0254 QColor fullScreenDisabledLink = fullscreenPal.color(QPalette::Disabled, QPalette::Link); 0255 QColor fullScreenInactiveToolTipBase = fullscreenPal.color(QPalette::Inactive, QPalette::ToolTipBase); 0256 QColor fullScreenInactiveToolTipText = fullscreenPal.color(QPalette::Inactive, QPalette::ToolTipText); 0257 QColor fullScreenInactiveHighlight = fullscreenPal.color(QPalette::Inactive, QPalette::Highlight); 0258 QColor fullScreenInactiveLink = fullscreenPal.color(QPalette::Inactive, QPalette::Link); 0259 0260 // Adjust the value of the normal color so it's not too dark/bright, and apply to the respective fullscreen color 0261 fullScreenToolTipBase.setHsv(normalToolTipBase.hue(), normalToolTipBase.saturation(), (127 + 2 * normalToolTipBase.value()) / 3); 0262 fullScreenToolTipText.setHsv(normalToolTipText.hue(), normalToolTipText.saturation(), (127 + 2 * normalToolTipText.value()) / 3); 0263 fullScreenHighlight.setHsv(normalHighlight.hue(), normalHighlight.saturation(), (127 + 2 * normalHighlight.value()) / 3); 0264 fullScreenLink.setHsv(normalLink.hue(), normalLink.saturation(), (127 + 2 * normalLink.value()) / 3); 0265 fullScreenActiveToolTipBase.setHsv(normalActiveToolTipBase.hue(), 0266 normalActiveToolTipBase.saturation(), 0267 (127 + 2 * normalActiveToolTipBase.value()) / 3); 0268 fullScreenActiveToolTipText.setHsv(normalActiveToolTipText.hue(), 0269 normalActiveToolTipText.saturation(), 0270 (127 + 2 * normalActiveToolTipText.value()) / 3); 0271 fullScreenActiveHighlight.setHsv(normalActiveHighlight.hue(), normalActiveHighlight.saturation(), (127 + 2 * normalActiveHighlight.value()) / 3); 0272 fullScreenActiveLink.setHsv(normalActiveLink.hue(), normalActiveLink.saturation(), (127 + 2 * normalActiveLink.value()) / 3); 0273 fullScreenDisabledToolTipBase.setHsv(normalDisabledToolTipBase.hue(), 0274 normalDisabledToolTipBase.saturation(), 0275 (127 + 2 * normalDisabledToolTipBase.value()) / 3); 0276 fullScreenDisabledToolTipText.setHsv(normalDisabledToolTipText.hue(), 0277 normalDisabledToolTipText.saturation(), 0278 (127 + 2 * normalDisabledToolTipText.value()) / 3); 0279 fullScreenDisabledLink.setHsv(normalDisabledLink.hue(), normalDisabledLink.saturation(), (127 + 2 * normalDisabledLink.value()) / 3); 0280 fullScreenInactiveToolTipBase.setHsv(normalInactiveToolTipBase.hue(), 0281 normalInactiveToolTipBase.saturation(), 0282 (127 + 2 * normalInactiveToolTipBase.value()) / 3); 0283 fullScreenInactiveToolTipText.setHsv(normalInactiveToolTipText.hue(), 0284 normalInactiveToolTipText.saturation(), 0285 (127 + 2 * normalInactiveToolTipText.value()) / 3); 0286 fullScreenInactiveHighlight.setHsv(normalInactiveHighlight.hue(), 0287 normalInactiveHighlight.saturation(), 0288 (127 + 2 * normalInactiveHighlight.value()) / 3); 0289 fullScreenInactiveLink.setHsv(normalInactiveLink.hue(), normalInactiveLink.saturation(), (127 + 2 * normalInactiveLink.value()) / 3); 0290 0291 // Apply the modified colors to the fullscreen palette 0292 fullscreenPal.setColor(QPalette::Normal, QPalette::ToolTipBase, fullScreenToolTipBase); 0293 fullscreenPal.setColor(QPalette::Normal, QPalette::ToolTipText, fullScreenToolTipText); 0294 fullscreenPal.setColor(QPalette::Normal, QPalette::Highlight, fullScreenHighlight); 0295 fullscreenPal.setColor(QPalette::Normal, QPalette::Link, fullScreenLink); 0296 fullscreenPal.setColor(QPalette::Active, QPalette::ToolTipBase, fullScreenActiveToolTipBase); 0297 fullscreenPal.setColor(QPalette::Active, QPalette::ToolTipText, fullScreenActiveToolTipText); 0298 fullscreenPal.setColor(QPalette::Active, QPalette::Highlight, fullScreenActiveHighlight); 0299 fullscreenPal.setColor(QPalette::Active, QPalette::Link, fullScreenActiveLink); 0300 fullscreenPal.setColor(QPalette::Disabled, QPalette::ToolTipBase, fullScreenDisabledToolTipBase); 0301 fullscreenPal.setColor(QPalette::Disabled, QPalette::ToolTipText, fullScreenDisabledToolTipText); 0302 fullscreenPal.setColor(QPalette::Disabled, QPalette::Link, fullScreenDisabledLink); 0303 fullscreenPal.setColor(QPalette::Inactive, QPalette::ToolTipBase, fullScreenInactiveToolTipBase); 0304 fullscreenPal.setColor(QPalette::Inactive, QPalette::ToolTipText, fullScreenInactiveToolTipText); 0305 fullscreenPal.setColor(QPalette::Inactive, QPalette::Highlight, fullScreenInactiveHighlight); 0306 fullscreenPal.setColor(QPalette::Inactive, QPalette::Link, fullScreenInactiveLink); 0307 0308 // Since we use an adjusted version of the normal highlight color, we need to use the normal version of the 0309 // text color so it contrasts 0310 fullscreenPal.setColor(QPalette::Normal, QPalette::HighlightedText, normalHighlightedText); 0311 fullscreenPal.setColor(QPalette::Active, QPalette::HighlightedText, normalActiveHighlightedText); 0312 fullscreenPal.setColor(QPalette::Inactive, QPalette::HighlightedText, normalInactiveHighlightedText); 0313 0314 mPalettes[GvCore::FullScreenPalette] = fullscreenPal; 0315 } 0316 }; 0317 0318 GvCore::GvCore(MainWindow *mainWindow, SortedDirModel *dirModel) 0319 : QObject(mainWindow) 0320 , d(new GvCorePrivate) 0321 { 0322 d->q = this; 0323 d->mMainWindow = mainWindow; 0324 d->mDirModel = dirModel; 0325 d->mRecentFoldersModel = nullptr; 0326 d->mRecentFilesModel = nullptr; 0327 0328 d->setupPalettes(); 0329 0330 connect(GwenviewConfig::self(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); 0331 connect(qApp, &QApplication::paletteChanged, this, [this]() { 0332 d->setupPalettes(); 0333 }); 0334 } 0335 0336 GvCore::~GvCore() 0337 { 0338 delete d; 0339 } 0340 0341 QAbstractItemModel *GvCore::recentFoldersModel() const 0342 { 0343 if (!d->mRecentFoldersModel) { 0344 d->mRecentFoldersModel = 0345 new HistoryModel(const_cast<GvCore *>(this), QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/recentfolders/"); 0346 } 0347 return d->mRecentFoldersModel; 0348 } 0349 0350 QAbstractItemModel *GvCore::recentFilesModel() const 0351 { 0352 if (!d->mRecentFilesModel) { 0353 d->mRecentFilesModel = new RecentFilesModel(const_cast<GvCore *>(this)); 0354 } 0355 return d->mRecentFilesModel; 0356 } 0357 0358 AbstractSemanticInfoBackEnd *GvCore::semanticInfoBackEnd() const 0359 { 0360 return d->mDirModel->semanticInfoBackEnd(); 0361 } 0362 0363 SortedDirModel *GvCore::sortedDirModel() const 0364 { 0365 return d->mDirModel; 0366 } 0367 0368 void GvCore::addUrlToRecentFolders(QUrl url) 0369 { 0370 if (!GwenviewConfig::historyEnabled()) { 0371 return; 0372 } 0373 if (!url.isValid()) { 0374 return; 0375 } 0376 0377 // For "sftp://localhost", "/" is a different path than "" (bug #312060) 0378 if (!url.path().isEmpty() && !url.path().endsWith('/')) { 0379 url.setPath(url.path() + '/'); 0380 } 0381 0382 recentFoldersModel(); 0383 d->mRecentFoldersModel->addUrl(url); 0384 } 0385 0386 void GvCore::addUrlToRecentFiles(const QUrl &url) 0387 { 0388 if (!GwenviewConfig::historyEnabled()) { 0389 return; 0390 } 0391 recentFilesModel(); 0392 d->mRecentFilesModel->addUrl(url); 0393 } 0394 0395 void GvCore::saveAll() 0396 { 0397 SaveAllHelper helper(d->mMainWindow); 0398 helper.save(); 0399 } 0400 0401 void GvCore::save(const QUrl &url) 0402 { 0403 Document::Ptr doc = DocumentFactory::instance()->load(url); 0404 QByteArray format = doc->format(); 0405 const QByteArrayList availableTypes = QImageWriter::supportedImageFormats(); 0406 if (availableTypes.contains(format)) { 0407 DocumentJob *job = doc->save(url, format); 0408 connect(job, SIGNAL(result(KJob *)), SLOT(slotSaveResult(KJob *))); 0409 } else { 0410 // We don't know how to save in 'format', ask the user for a format we can 0411 // write to. 0412 KGuiItem saveUsingAnotherFormat = KStandardGuiItem::saveAs(); 0413 saveUsingAnotherFormat.setText(i18n("Save using another format")); 0414 int result = KMessageBox::warningContinueCancel(d->mMainWindow, 0415 i18n("Gwenview cannot save images in '%1' format.", QString(format)), 0416 QString() /* caption */, 0417 saveUsingAnotherFormat); 0418 if (result == KMessageBox::Continue) { 0419 saveAs(url); 0420 } 0421 } 0422 } 0423 0424 void GvCore::saveAs(const QUrl &url) 0425 { 0426 KFileCustomDialog *dialog = d->createSaveAsDialog(url); 0427 0428 connect(dialog, &QDialog::accepted, this, [=]() { 0429 KFileWidget *fileWidget = dialog->fileWidget(); 0430 0431 const QList<QUrl> files = fileWidget->selectedUrls(); 0432 if (files.isEmpty()) { 0433 return; 0434 } 0435 0436 const QString filename = files.at(0).fileName(); 0437 0438 const QMimeType mimeType = QMimeDatabase().mimeTypeForFile(filename, QMimeDatabase::MatchExtension); 0439 QByteArray format; 0440 if (mimeType.isValid()) { 0441 format = mimeType.preferredSuffix().toLocal8Bit(); 0442 } else { 0443 KMessageBox::error(d->mMainWindow, i18nc("@info", "Gwenview cannot save images as %1.", QFileInfo(filename).suffix())); 0444 } 0445 0446 QUrl saveAsUrl = fileWidget->selectedUrls().constFirst(); 0447 0448 if (format == "jpg") { 0449 // Gwenview code assumes JPEG images have "jpeg" format, so if the 0450 // dialog returned the format "jpg", use "jpeg" instead 0451 // This does not affect the actual filename extension 0452 format = "jpeg"; 0453 } 0454 0455 // Start save 0456 Document::Ptr doc = DocumentFactory::instance()->load(url); 0457 KJob *job = doc->save(saveAsUrl, format); 0458 if (!job) { 0459 const QString saveName = saveAsUrl.fileName(); 0460 const QString name = !saveName.isEmpty() ? saveName : saveAsUrl.toDisplayString(); 0461 const QString msg = xi18nc("@info", "<emphasis strong='true'>Saving <filename>%1</filename> failed:</emphasis><nl />%2", name, doc->errorString()); 0462 KMessageBox::error(QApplication::activeWindow(), msg); 0463 } else { 0464 // Regardless of job result, reset JPEG config value if it was changed by 0465 // the Save As dialog 0466 connect(job, &KJob::result, this, [=]() { 0467 if (GwenviewConfig::jPEGQuality() != d->configFileJPEGQualityValue) { 0468 GwenviewConfig::setJPEGQuality(d->configFileJPEGQualityValue); 0469 } 0470 }); 0471 0472 connect(job, &KJob::result, this, &GvCore::slotSaveResult); 0473 } 0474 }); 0475 0476 dialog->show(); 0477 } 0478 0479 static void applyTransform(const QUrl &url, Orientation orientation) 0480 { 0481 auto op = new TransformImageOperation(orientation); 0482 Document::Ptr doc = DocumentFactory::instance()->load(url); 0483 op->applyToDocument(doc); 0484 } 0485 0486 void GvCore::slotSaveResult(KJob *_job) 0487 { 0488 auto job = static_cast<SaveJob *>(_job); 0489 QUrl oldUrl = job->oldUrl(); 0490 QUrl newUrl = job->newUrl(); 0491 0492 if (job->error()) { 0493 QString name = newUrl.fileName().isEmpty() ? newUrl.toDisplayString() : newUrl.fileName(); 0494 const QString msg = 0495 xi18nc("@info", "<emphasis strong='true'>Saving <filename>%1</filename> failed:</emphasis><nl />%2", name, kxi18n(qPrintable(job->errorString()))); 0496 0497 int result = KMessageBox::warningContinueCancel(d->mMainWindow, msg, QString() /* caption */, KStandardGuiItem::saveAs()); 0498 0499 if (result == KMessageBox::Continue) { 0500 saveAs(oldUrl); 0501 } 0502 return; 0503 } 0504 0505 if (oldUrl != newUrl) { 0506 d->mMainWindow->goToUrl(newUrl); 0507 0508 ViewMainPage *page = d->mMainWindow->viewMainPage(); 0509 if (page->isVisible()) { 0510 auto bubble = new HudMessageBubble(); 0511 bubble->setText(i18n("You are now viewing the new document.")); 0512 KGuiItem item = KStandardGuiItem::back(); 0513 item.setText(i18n("Go back to the original")); 0514 HudButton *button = bubble->addButton(item); 0515 0516 BinderRef<MainWindow, QUrl>::bind(button, SIGNAL(clicked()), d->mMainWindow, &MainWindow::goToUrl, oldUrl); 0517 connect(button, SIGNAL(clicked()), bubble, SLOT(deleteLater())); 0518 0519 page->showMessageWidget(bubble); 0520 } 0521 } 0522 } 0523 0524 void GvCore::rotateLeft(const QUrl &url) 0525 { 0526 applyTransform(url, ROT_270); 0527 } 0528 0529 void GvCore::rotateRight(const QUrl &url) 0530 { 0531 applyTransform(url, ROT_90); 0532 } 0533 0534 void GvCore::setRating(const QUrl &url, int rating) 0535 { 0536 QModelIndex index = d->mDirModel->indexForUrl(url); 0537 if (!index.isValid()) { 0538 qCWarning(GWENVIEW_APP_LOG) << "invalid index!"; 0539 return; 0540 } 0541 d->mDirModel->setData(index, rating, SemanticInfoDirModel::RatingRole); 0542 } 0543 0544 static void clearModel(QAbstractItemModel *model) 0545 { 0546 model->removeRows(0, model->rowCount()); 0547 } 0548 0549 void GvCore::clearRecentFilesAndFolders() 0550 { 0551 clearModel(recentFilesModel()); 0552 clearModel(recentFoldersModel()); 0553 } 0554 0555 void GvCore::slotConfigChanged() 0556 { 0557 if (!GwenviewConfig::historyEnabled()) { 0558 clearRecentFilesAndFolders(); 0559 } 0560 d->setupPalettes(); 0561 } 0562 0563 QPalette GvCore::palette(GvCore::PaletteType type) const 0564 { 0565 return d->mPalettes[type]; 0566 } 0567 0568 QString GvCore::fullScreenPaletteName() const 0569 { 0570 return d->mFullScreenPaletteName; 0571 } 0572 0573 void GvCore::setTrackFileManagerSorting(bool enable) 0574 { 0575 sortingTracksFileManager = enable; 0576 } 0577 0578 bool GvCore::trackFileManagerSorting() 0579 { 0580 return sortingTracksFileManager; 0581 } 0582 0583 } // namespace 0584 0585 #include "moc_gvcore.cpp"