File indexing completed on 2025-01-19 03:51:17
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2007-09-09 0007 * Description : scanner dialog 0008 * 0009 * SPDX-FileCopyrightText: 2007-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "scandialog.h" 0016 0017 // Qt includes 0018 0019 #include <QVBoxLayout> 0020 #include <QDateTime> 0021 #include <QPushButton> 0022 #include <QPointer> 0023 #include <QDir> 0024 #include <QUrl> 0025 #include <QMenu> 0026 #include <QApplication> 0027 #include <QMessageBox> 0028 #include <QImageWriter> 0029 0030 // KDE includes 0031 0032 #include <klocalizedstring.h> 0033 0034 // Local includes 0035 0036 #include "digikam_debug.h" 0037 #include "saveimgthread.h" 0038 #include "statusprogressbar.h" 0039 #include "dxmlguiwindow.h" 0040 #include "dfiledialog.h" 0041 0042 namespace DigikamGenericDScannerPlugin 0043 { 0044 0045 class Q_DECL_HIDDEN ScanDialog::Private 0046 { 0047 public: 0048 0049 explicit Private() 0050 : progress (nullptr), 0051 saneWidget(nullptr) 0052 { 0053 } 0054 0055 QString targetDir; 0056 StatusProgressBar* progress; 0057 KSaneWidget* saneWidget; 0058 }; 0059 0060 ScanDialog::ScanDialog(KSaneWidget* const saneWdg, QWidget* const parent) 0061 : DPluginDialog(parent, QLatin1String("Scan Tool Dialog")), 0062 d (new Private) 0063 { 0064 setWindowTitle(i18nc("@title:window", "Scan Image")); 0065 setModal(false); 0066 0067 d->saneWidget = saneWdg; 0068 d->progress = new StatusProgressBar(this); 0069 d->progress->setProgressBarMode(StatusProgressBar::ProgressBarMode); 0070 d->progress->setProgressTotalSteps(100); 0071 d->progress->setNotify(true); 0072 d->progress->setNotificationTitle(i18n("Scan Images"), QIcon::fromTheme(QLatin1String("scanner"))); 0073 0074 QVBoxLayout* const vbx = new QVBoxLayout(this); 0075 vbx->addWidget(d->saneWidget, 10); 0076 vbx->addWidget(d->progress); 0077 vbx->addWidget(m_buttons); 0078 setLayout(vbx); 0079 0080 // ------------------------------------------------------------------------ 0081 0082 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0083 0084 connect(d->saneWidget, &KSaneWidget::scannedImageReady, 0085 this, &ScanDialog::slotSaveImage); 0086 0087 #elif KSANE_VERSION < QT_VERSION_CHECK(21,8,0) 0088 0089 connect(d->saneWidget, &KSaneWidget::imageReady, 0090 this, &ScanDialog::slotSaveImage); 0091 0092 #else 0093 0094 connect(d->saneWidget, &KSaneWidget::scannedImageReady, 0095 this, &ScanDialog::slotSaveImage); 0096 0097 #endif 0098 0099 connect(this, &QDialog::finished, 0100 this, &ScanDialog::slotDialogFinished); 0101 } 0102 0103 ScanDialog::~ScanDialog() 0104 { 0105 delete d; 0106 } 0107 0108 void ScanDialog::setTargetDir(const QString& targetDir) 0109 { 0110 d->targetDir = targetDir; 0111 } 0112 0113 void ScanDialog::closeEvent(QCloseEvent* e) 0114 { 0115 if (!e) 0116 { 0117 return; 0118 } 0119 0120 slotDialogFinished(); 0121 e->accept(); 0122 } 0123 0124 void ScanDialog::slotDialogFinished() 0125 { 0126 d->saneWidget->closeDevice(); 0127 } 0128 0129 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0130 0131 void ScanDialog::slotSaveImage(const QImage& image_data) 0132 0133 #elif KSANE_VERSION < QT_VERSION_CHECK(21,8,0) 0134 0135 // cppcheck-suppress constParameter 0136 void ScanDialog::slotSaveImage(QByteArray& ksane_data, int width, int height, int bytes_per_line, int ksaneformat) 0137 0138 #else 0139 0140 void ScanDialog::slotSaveImage(const QImage& image_data) 0141 0142 #endif 0143 0144 { 0145 QStringList writableMimetypes; 0146 QList<QByteArray> supported = QImageWriter::supportedMimeTypes(); 0147 0148 Q_FOREACH (const QByteArray& mimeType, supported) 0149 { 0150 writableMimetypes.append(QLatin1String(mimeType)); 0151 } 0152 0153 // Put first class citizens at first place 0154 0155 writableMimetypes.removeAll(QLatin1String("image/jpeg")); 0156 writableMimetypes.removeAll(QLatin1String("image/tiff")); 0157 writableMimetypes.removeAll(QLatin1String("image/png")); 0158 writableMimetypes.insert(0, QLatin1String("image/png")); 0159 writableMimetypes.insert(1, QLatin1String("image/jpeg")); 0160 writableMimetypes.insert(2, QLatin1String("image/tiff")); 0161 0162 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "slotSaveImage: Offered mimetypes: " << writableMimetypes; 0163 0164 QLatin1String defaultMimeType("image/png"); 0165 QLatin1String defaultFileName("image.png"); 0166 0167 QPointer<DFileDialog> imageFileSaveDialog = new DFileDialog(nullptr, i18nc("@title:window", "New Image File Name"), d->targetDir); 0168 imageFileSaveDialog->setAcceptMode(QFileDialog::AcceptSave); 0169 imageFileSaveDialog->setMimeTypeFilters(writableMimetypes); 0170 imageFileSaveDialog->selectMimeTypeFilter(defaultMimeType); 0171 imageFileSaveDialog->selectFile(defaultFileName); 0172 0173 // Start dialog and check if canceled. 0174 0175 imageFileSaveDialog->exec(); 0176 0177 if (!imageFileSaveDialog->hasAcceptedUrls()) 0178 { 0179 delete imageFileSaveDialog; 0180 0181 return; 0182 } 0183 0184 QUrl newURL = imageFileSaveDialog->selectedUrls().first(); 0185 QFileInfo fi(newURL.toLocalFile()); 0186 0187 // Parse name filter and extract file extension 0188 0189 QString selectedFilterString = imageFileSaveDialog->selectedNameFilter(); 0190 QLatin1String triggerString("*."); 0191 int triggerPos = selectedFilterString.lastIndexOf(triggerString); 0192 QString format; 0193 0194 if (triggerPos != -1) 0195 { 0196 format = selectedFilterString.mid(triggerPos + triggerString.size()); 0197 format = format.left(format.size() - 1); 0198 format = format.toUpper(); 0199 } 0200 0201 // If name filter was selected, we guess image type using file extension. 0202 0203 if (format.isEmpty()) 0204 { 0205 format = fi.suffix().toUpper(); 0206 0207 QList<QByteArray> imgExtList = QImageWriter::supportedImageFormats(); 0208 imgExtList << "TIF"; 0209 imgExtList << "TIFF"; 0210 imgExtList << "JPG"; 0211 imgExtList << "JPE"; 0212 0213 if (!imgExtList.contains(format.toLatin1()) && !imgExtList.contains(format.toLower().toLatin1())) 0214 { 0215 QMessageBox::critical(nullptr, i18nc("@title:window", "Unsupported Format"), 0216 i18n("The target image file format \"%1\" is unsupported.", format)); 0217 0218 qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "target image file format " << format << " is unsupported!"; 0219 0220 delete imageFileSaveDialog; 0221 0222 return; 0223 } 0224 } 0225 0226 if (!newURL.isValid()) 0227 { 0228 QMessageBox::critical(nullptr, i18nc("@title:window", "Cannot Save File"), 0229 i18n("Failed to save file\n\"%1\" to\n\"%2\".", 0230 newURL.fileName(), 0231 QDir::toNativeSeparators(newURL.toLocalFile().section(QLatin1Char('/'), -2, -2)))); 0232 0233 qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "target URL is not valid !"; 0234 0235 delete imageFileSaveDialog; 0236 0237 return; 0238 } 0239 0240 // Check for overwrite ---------------------------------------------------------- 0241 0242 if (fi.exists()) 0243 { 0244 int result = QMessageBox::warning(nullptr, i18nc("@title:window", "Overwrite File?"), 0245 i18n("A file named \"%1\" already " 0246 "exists. Are you sure you want " 0247 "to overwrite it?", 0248 newURL.fileName()), 0249 QMessageBox::Yes | QMessageBox::No); 0250 0251 if (result != QMessageBox::Yes) 0252 { 0253 delete imageFileSaveDialog; 0254 0255 return; 0256 } 0257 } 0258 0259 delete imageFileSaveDialog; 0260 QApplication::setOverrideCursor(Qt::WaitCursor); 0261 setEnabled(false); 0262 0263 // Perform saving --------------------------------------------------------------- 0264 0265 SaveImgThread* const thread = new SaveImgThread(this); 0266 0267 connect(thread, &SaveImgThread::signalProgress, 0268 this, &ScanDialog::slotThreadProgress); 0269 0270 connect(thread, &SaveImgThread::signalComplete, 0271 this, &ScanDialog::slotThreadDone); 0272 0273 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0274 0275 thread->setImageData(image_data); 0276 thread->setScannerModel(d->saneWidget->deviceVendor(), d->saneWidget->deviceModel()); 0277 0278 #elif KSANE_VERSION < QT_VERSION_CHECK(21,8,0) 0279 0280 thread->setImageData(ksane_data, width, height, bytes_per_line, ksaneformat); 0281 thread->setScannerModel(d->saneWidget->make(), d->saneWidget->model()); 0282 0283 #else 0284 0285 thread->setImageData(image_data); 0286 thread->setScannerModel(d->saneWidget->deviceVendor(), d->saneWidget->deviceModel()); 0287 0288 #endif 0289 0290 thread->setTargetFile(newURL, format); 0291 thread->start(); 0292 } 0293 0294 void ScanDialog::slotThreadProgress(const QUrl& url, int percent) 0295 { 0296 d->progress->setProgressText(i18n("Saving file %1 -", url.fileName())); 0297 d->progress->setProgressValue(percent); 0298 } 0299 0300 void ScanDialog::slotThreadDone(const QUrl& url, bool success) 0301 { 0302 if (!success) 0303 { 0304 QMessageBox::critical(nullptr, i18nc("@title:window", "File Not Saved"), i18n("Cannot save \"%1\" file", url.fileName())); 0305 } 0306 0307 d->progress->setProgressText(QString()); 0308 QApplication::restoreOverrideCursor(); 0309 setEnabled(true); 0310 0311 if (success) 0312 { 0313 Q_EMIT signalImportedImage(url); 0314 } 0315 } 0316 0317 } // namespace DigikamGenericDScannerPlugin 0318 0319 #include "moc_scandialog.cpp"