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"