Warning, file /graphics/skanlite/src/skanlite.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* ============================================================ 0002 * 0003 * SPDX-FileCopyrightText: 2007-2012 Kåre Särs <kare.sars@iki .fi> 0004 * SPDX-FileCopyrightText: 2009 Arseniy Lartsev <receive-spam at yandex dot ru> 0005 * SPDX-FileCopyrightText: 2014 Gregor Mitsch : port to KDE5 frameworks 0006 * 0007 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0008 * 0009 * ============================================================ */ 0010 0011 #include "skanlite.h" 0012 0013 #include "SaveLocation.h" 0014 #include "showimagedialog.h" 0015 #include "SkanliteImageSaver.h" 0016 0017 #include <QApplication> 0018 #include <QScrollArea> 0019 #include <QStringList> 0020 #include <QFileDialog> 0021 #include <QDialogButtonBox> 0022 #include <QComboBox> 0023 #include <QMessageBox> 0024 #include <QTemporaryFile> 0025 #include <QImageWriter> 0026 #include <QMimeType> 0027 #include <QMimeDatabase> 0028 #include <QCloseEvent> 0029 #include <QProgressBar> 0030 0031 #include <kio_version.h> 0032 #include <KAboutData> 0033 #include <KAboutApplicationDialog> 0034 #include <KLocalizedString> 0035 #include <KMessageBox> 0036 #include <KIO/StatJob> 0037 #include <KIO/Job> 0038 #include <KIO/StoredTransferJob> 0039 #include <KJobWidgets> 0040 #include <kio/global.h> 0041 #include <KSharedConfig> 0042 #include <KConfigGroup> 0043 #include <KHelpClient> 0044 #include <KHelpMenu> 0045 #include <KSaneWidget> 0046 0047 #include <skanlite_debug.h> 0048 0049 #include <errno.h> 0050 using namespace KSaneIface; 0051 Skanlite::Skanlite(const QString &device, QWidget *parent) 0052 : QDialog(parent) 0053 , m_dbusInterface(this) 0054 { 0055 QVBoxLayout *mainLayout = new QVBoxLayout(this); 0056 0057 QDialogButtonBox *dlgButtonBoxBottom = new QDialogButtonBox(this); 0058 dlgButtonBoxBottom->setStandardButtons(QDialogButtonBox::Help); 0059 dlgButtonBoxBottom->button(QDialogButtonBox::Help)->setAutoDefault(false); 0060 0061 KHelpMenu *helpMenu = new KHelpMenu(this, KAboutData::applicationData(), false); 0062 dlgButtonBoxBottom->button(QDialogButtonBox::Help)->setMenu(helpMenu->menu()); 0063 0064 QPushButton *btnConfigure = dlgButtonBoxBottom->addButton(i18n("Configure..."), QDialogButtonBox::ButtonRole::ResetRole); 0065 btnConfigure->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); 0066 btnConfigure->setAutoDefault(false); 0067 0068 m_btnReselectDevice = dlgButtonBoxBottom->addButton(i18n("Reselect scanner device"), QDialogButtonBox::ButtonRole::ActionRole); 0069 m_btnReselectDevice->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); 0070 m_btnReselectDevice->setAutoDefault(false); 0071 0072 m_firstImage = true; 0073 0074 m_ksanew = new KSaneIface::KSaneWidget(this); 0075 connect(m_ksanew, &KSaneWidget::scannedImageReady, this, &Skanlite::imageReady); 0076 connect(m_ksanew, &KSaneWidget::userMessage, this, &Skanlite::alertUser); 0077 connect(m_ksanew, &KSaneWidget::buttonPressed, this, &Skanlite::buttonPressed); 0078 connect(m_ksanew, &KSaneWidget::scanProgress, this, [this](int percent){ 0079 if (percent < 100) { 0080 m_btnReselectDevice->setEnabled(false); 0081 } 0082 }); 0083 connect(m_ksanew, &KSaneWidget::scanDone, this, [this](){ 0084 if (!m_pendingApplyScanOpts.isEmpty()) { 0085 applyScannerOptions(m_pendingApplyScanOpts); 0086 } 0087 m_btnReselectDevice->setEnabled(true); 0088 }); 0089 0090 m_saveProgressBar = new QProgressBar(this); 0091 m_saveProgressBar->setVisible(false); 0092 m_saveProgressBar->setFormat(i18n("Saving: %v kB")); 0093 m_saveProgressBar->setTextVisible(true); 0094 0095 m_saveUpdateTimer.setInterval(200); 0096 m_saveUpdateTimer.setSingleShot(false); 0097 connect(&m_saveUpdateTimer, &QTimer::timeout, this, &Skanlite::updateSaveProgress); 0098 0099 mainLayout->addWidget(m_ksanew); 0100 mainLayout->addWidget(m_saveProgressBar); 0101 mainLayout->addWidget(dlgButtonBoxBottom); 0102 0103 // read the size here... 0104 KConfigGroup window(KSharedConfig::openStateConfig(), QStringLiteral("Window")); 0105 QSize rect = window.readEntry("Geometry", QSize(740, 400)); 0106 resize(rect); 0107 0108 // open scanner device from command line, otherwise try remembered one 0109 QString deviceName; 0110 QString deviceVendor; 0111 QString deviceModel; 0112 if (device.isEmpty()) { 0113 KConfigGroup general(KSharedConfig::openStateConfig(), QStringLiteral("General")); 0114 deviceName = general.readEntry(QStringLiteral("deviceName")); 0115 deviceVendor = general.readEntry(QStringLiteral("deviceVendor")); 0116 deviceModel = general.readEntry(QStringLiteral("deviceModel")); 0117 } else { 0118 deviceName = device; 0119 } 0120 0121 connect(dlgButtonBoxBottom, &QDialogButtonBox::rejected, this, &QDialog::close); 0122 connect(this, &QDialog::finished, this, &Skanlite::saveWindowSize); 0123 connect(this, &QDialog::finished, this, &Skanlite::saveScannerDevice); 0124 connect(this, &QDialog::finished, this, &Skanlite::saveScannerOptions); 0125 connect(btnConfigure, &QPushButton::clicked, this, &Skanlite::showSettingsDialog); 0126 connect(m_btnReselectDevice, &QPushButton::clicked, this, &Skanlite::reselectScannerDevice); 0127 connect(dlgButtonBoxBottom, &QDialogButtonBox::helpRequested, this, &Skanlite::showHelp); 0128 0129 // 0130 // Create the settings dialog 0131 // 0132 { 0133 m_settingsDialog = new QDialog(this); 0134 0135 QVBoxLayout *mainLayout = new QVBoxLayout(m_settingsDialog); 0136 0137 QWidget *settingsWidget = new QWidget(m_settingsDialog); 0138 m_settingsUi.setupUi(settingsWidget); 0139 m_settingsUi.revertOptions->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo"))); 0140 m_saveLocation = new SaveLocation(this); 0141 0142 // add the supported image types 0143 const QList<QByteArray> tmpList = QImageWriter::supportedMimeTypes(); 0144 m_filterList.clear(); 0145 for (const auto &ba : tmpList) { 0146 if (ba.isEmpty()) { 0147 continue; 0148 } 0149 m_filterList.append(QString::fromLatin1(ba)); 0150 } 0151 0152 qCDebug(SKANLITE_LOG) << m_filterList; 0153 0154 // Put first class citizens at first place 0155 m_filterList.removeAll(QStringLiteral("image/jpeg")); 0156 m_filterList.removeAll(QStringLiteral("image/tiff")); 0157 m_filterList.removeAll(QStringLiteral("image/png")); 0158 m_filterList.insert(0, QStringLiteral("image/png")); 0159 m_filterList.insert(1, QStringLiteral("image/jpeg")); 0160 m_filterList.insert(2, QStringLiteral("image/tiff")); 0161 m_filterList.insert(3, QStringLiteral("application/pdf")); 0162 0163 m_filter16BitList << QStringLiteral("image/png"); 0164 m_filter16BitList << QStringLiteral("image/tiff"); 0165 0166 // fill m_filterList (...) 0167 { 0168 QStringList namedMimeTypes; 0169 for (const QString &mimeStr : qAsConst(m_filterList)) { 0170 QMimeType mimeType = QMimeDatabase().mimeTypeForName(mimeStr); 0171 namedMimeTypes.append(mimeType.name()); 0172 0173 m_settingsUi.imgFormat->addItem(mimeType.preferredSuffix(), mimeType.name()); 0174 m_saveLocation->addImageFormat(mimeType.preferredSuffix(), mimeType.name()); 0175 } 0176 m_filterList << std::move(namedMimeTypes); 0177 } 0178 0179 mainLayout->addWidget(settingsWidget); 0180 0181 QDialogButtonBox *dlgButtonBoxBottom = new QDialogButtonBox(this); 0182 dlgButtonBoxBottom->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Close); 0183 connect(dlgButtonBoxBottom, &QDialogButtonBox::accepted, m_settingsDialog, &QDialog::accept); 0184 connect(dlgButtonBoxBottom, &QDialogButtonBox::rejected, m_settingsDialog, &QDialog::reject); 0185 0186 mainLayout->addWidget(dlgButtonBoxBottom); 0187 0188 m_settingsDialog->setWindowTitle(i18n("Skanlite Settings")); 0189 0190 connect(m_settingsUi.revertOptions, &QPushButton::clicked, this, &Skanlite::defaultScannerOptions); 0191 readSettings(); 0192 0193 // default directory for the save dialog 0194 m_saveLocation->setFolderUrl(m_settingsUi.saveDirRequester->url()); 0195 m_saveLocation->setImagePrefix(m_settingsUi.imgPrefix->text()); 0196 m_saveLocation->setImageFormatIndex(m_settingsUi.imgFormat->currentIndex()); 0197 } 0198 0199 // open the scan device 0200 if (!m_ksanew->openDevice(deviceName)) { 0201 QString dev = m_ksanew->selectDevice(nullptr); 0202 if (dev.isEmpty()) { 0203 // either no scanner was found or then cancel was pressed. 0204 exit(0); 0205 } 0206 if (!m_ksanew->openDevice(dev)) { 0207 // could not open a scanner 0208 KMessageBox::error(nullptr, i18n("Opening the selected scanner failed.")); 0209 exit(1); 0210 } 0211 else { 0212 updateWindowTitle(dev, m_ksanew->deviceVendor(), m_ksanew->deviceModel()); 0213 m_deviceName = dev; 0214 } 0215 } 0216 else { 0217 if (deviceVendor.isEmpty()) { 0218 updateWindowTitle(deviceName); 0219 } else { 0220 updateWindowTitle(deviceName, deviceVendor, deviceModel); 0221 } 0222 m_deviceName = deviceName; 0223 m_deviceModel = deviceModel; 0224 m_deviceVendor = deviceVendor; 0225 } 0226 0227 // prepare the Show Image Dialog 0228 m_showImgDialog = new ShowImageDialog(this); 0229 connect(m_showImgDialog, &ShowImageDialog::saveRequested, this, &Skanlite::saveImage); 0230 connect(m_showImgDialog, &ShowImageDialog::rejected, m_ksanew, &KSaneWidget::cancelScan); 0231 0232 // save the default sane options for later use 0233 m_ksanew->getOptionValues(m_defaultScanOpts); 0234 0235 // load saved options 0236 loadScannerOptions(); 0237 0238 m_firstImage = true; 0239 m_ksanew->setFocus(); 0240 0241 if (m_dbusInterface.setupDBusInterface()) { 0242 // D-Bus related slots 0243 connect(&m_dbusInterface, &DBusInterface::requestedScan, m_ksanew, &KSaneWidget::startScan); 0244 connect(&m_dbusInterface, &DBusInterface::requestedPreview, m_ksanew, &KSaneWidget::startPreviewScan); 0245 connect(&m_dbusInterface, &DBusInterface::requestedScanCancel, m_ksanew, &KSaneWidget::cancelScan); 0246 connect(&m_dbusInterface, &DBusInterface::requestedSetScannerOptions, this, &Skanlite::setScannerOptions); 0247 connect(&m_dbusInterface, &DBusInterface::requestedSetSelection, this, &Skanlite::setSelection); 0248 0249 // D-Bus related slots below must be Qt::DirectConnection to simplify return value forwarding via DBusInterface 0250 connect(&m_dbusInterface, &DBusInterface::requestedGetScannerOptions, this, &Skanlite::getScannerOptions, Qt::DirectConnection); 0251 connect(&m_dbusInterface, &DBusInterface::requestedDefaultScannerOptions, this, &Skanlite::getDefaultScannerOptions, Qt::DirectConnection); 0252 connect(&m_dbusInterface, &DBusInterface::requestedDeviceName, this, &Skanlite::getDeviceName, Qt::DirectConnection); 0253 connect(&m_dbusInterface, &DBusInterface::requestedSaveScannerOptionsToProfile, this, &Skanlite::saveScannerOptionsToProfile, Qt::DirectConnection); 0254 connect(&m_dbusInterface, &DBusInterface::requestedSwitchToProfile, this, &Skanlite::switchToProfile, Qt::DirectConnection); 0255 connect(&m_dbusInterface, &DBusInterface::requestedGetSelection, this, &Skanlite::getSelection, Qt::DirectConnection); 0256 0257 // D-Bus related signals 0258 connect(m_ksanew, &KSaneWidget::scanDone, &m_dbusInterface, &DBusInterface::scanDone); 0259 connect(m_ksanew, &KSaneWidget::userMessage, &m_dbusInterface, &DBusInterface::userMessage); 0260 connect(m_ksanew, &KSaneWidget::scanProgress, &m_dbusInterface, &DBusInterface::scanProgress); 0261 connect(m_ksanew, &KSaneWidget::buttonPressed, &m_dbusInterface, &DBusInterface::buttonPressed); 0262 } 0263 else { 0264 // keep working without dbus 0265 } 0266 } 0267 0268 void Skanlite::showHelp() 0269 { 0270 KHelpClient::invokeHelp(QStringLiteral("index"), QStringLiteral("skanlite")); 0271 } 0272 0273 void Skanlite::closeEvent(QCloseEvent *event) 0274 { 0275 saveWindowSize(); 0276 saveScannerDevice(); 0277 saveScannerOptions(); 0278 event->accept(); 0279 } 0280 0281 void Skanlite::saveWindowSize() 0282 { 0283 KConfigGroup window(KSharedConfig::openStateConfig(), QStringLiteral("Window")); 0284 window.writeEntry("Geometry", size()); 0285 window.sync(); 0286 } 0287 0288 void Skanlite::saveScannerDevice() 0289 { 0290 KConfigGroup general(KSharedConfig::openStateConfig(), QStringLiteral("General")); 0291 general.writeEntry(QStringLiteral("deviceName"), m_deviceName); 0292 general.writeEntry(QStringLiteral("deviceModel"), m_deviceModel); 0293 general.writeEntry(QStringLiteral("deviceVendor"), m_deviceVendor); 0294 general.sync(); 0295 } 0296 0297 void Skanlite::reselectScannerDevice() 0298 { 0299 m_ksanew->closeDevice(); 0300 m_deviceName.clear(); 0301 m_deviceVendor.clear(); 0302 m_deviceModel.clear(); 0303 // open the scan device dialog 0304 QString dev = m_ksanew->selectDevice(nullptr); 0305 if (m_ksanew->openDevice(dev) == false) { 0306 // could not open a scanner 0307 KMessageBox::error(nullptr, i18n("Opening the selected scanner failed.")); 0308 } 0309 else { 0310 updateWindowTitle(dev, m_ksanew->deviceVendor(), m_ksanew->deviceModel()); 0311 m_deviceName = dev; 0312 m_deviceModel = m_ksanew->deviceModel(); 0313 m_deviceVendor = m_ksanew->deviceVendor(); 0314 } 0315 } 0316 0317 // Pops up message box similar to what perror() would print 0318 //************************************************************ 0319 static void perrorMessageBox(const QString &text) 0320 { 0321 if (errno != 0) { 0322 KMessageBox::error(nullptr, i18n("%1: %2", text, QString::fromLocal8Bit(strerror(errno)))); 0323 } 0324 else { 0325 KMessageBox::error(nullptr, text); 0326 } 0327 } 0328 0329 void Skanlite::readSettings(void) 0330 { 0331 // enable the widgets to allow modifying 0332 m_settingsUi.setQuality->setChecked(true); 0333 m_settingsUi.setPreviewDPI->setChecked(true); 0334 0335 // read the saved parameters 0336 KConfigGroup saving(KSharedConfig::openConfig(), QStringLiteral("Image Saving")); 0337 m_settingsUi.saveModeCB->setCurrentIndex(saving.readEntry("SaveMode", (int)SaveModeManual)); 0338 if (m_settingsUi.saveModeCB->currentIndex() != SaveModeAskFirst) { 0339 m_firstImage = false; 0340 } 0341 m_settingsUi.saveDirRequester->setUrl(saving.readEntry("Location", QUrl(QDir::homePath()))); 0342 m_settingsUi.imgPrefix->setText(saving.readEntry("NamePrefix", i18nc("prefix for auto naming", "Image-"))); 0343 QString format = saving.readEntry("ImgFormat", "image/png"); 0344 int index = m_settingsUi.imgFormat->findData(format); 0345 if (index >= 0) { 0346 m_settingsUi.imgFormat->setCurrentIndex(index); 0347 } 0348 0349 m_settingsUi.imgQuality->setValue(saving.readEntry("ImgQuality", 90)); 0350 m_settingsUi.setQuality->setChecked(saving.readEntry("SetQuality", false)); 0351 m_settingsUi.showB4Save->setChecked(saving.readEntry("ShowBeforeSave", true)); 0352 0353 KConfigGroup general(KSharedConfig::openConfig(), QStringLiteral("General")); 0354 0355 //m_settingsUi.previewDPI->setCurrentItem(general.readEntry("PreviewDPI", "100"), true); // FIXME KF5 is the 'true' parameter still needed? 0356 m_settingsUi.previewDPI->setCurrentText(general.readEntry("PreviewDPI", "100")); 0357 0358 m_settingsUi.setPreviewDPI->setChecked(general.readEntry("SetPreviewDPI", false)); 0359 if (m_settingsUi.setPreviewDPI->isChecked()) { 0360 m_ksanew->setPreviewResolution(m_settingsUi.previewDPI->currentText().toFloat()); 0361 } 0362 else { 0363 m_ksanew->setPreviewResolution(0.0); 0364 } 0365 m_settingsUi.u_disableSelections->setChecked(general.readEntry("DisableAutoSelection", false)); 0366 m_ksanew->enableAutoSelect(!m_settingsUi.u_disableSelections->isChecked()); 0367 } 0368 0369 void Skanlite::showSettingsDialog(void) 0370 { 0371 readSettings(); 0372 0373 // show the dialog 0374 if (m_settingsDialog->exec()) { 0375 // save the settings 0376 KConfigGroup saving(KSharedConfig::openConfig(), QStringLiteral("Image Saving")); 0377 saving.writeEntry("SaveMode", m_settingsUi.saveModeCB->currentIndex()); 0378 saving.writeEntry("Location", m_settingsUi.saveDirRequester->url()); 0379 saving.writeEntry("NamePrefix", m_settingsUi.imgPrefix->text()); 0380 saving.writeEntry("ImgFormat", m_settingsUi.imgFormat->currentData().toString()); 0381 saving.writeEntry("SetQuality", m_settingsUi.setQuality->isChecked()); 0382 saving.writeEntry("ImgQuality", m_settingsUi.imgQuality->value()); 0383 saving.writeEntry("ShowBeforeSave", m_settingsUi.showB4Save->isChecked()); 0384 saving.sync(); 0385 0386 KConfigGroup general(KSharedConfig::openConfig(), QStringLiteral("General")); 0387 general.writeEntry("PreviewDPI", m_settingsUi.previewDPI->currentText()); 0388 general.writeEntry("SetPreviewDPI", m_settingsUi.setPreviewDPI->isChecked()); 0389 general.writeEntry("DisableAutoSelection", m_settingsUi.u_disableSelections->isChecked()); 0390 general.sync(); 0391 0392 // the previewDPI has to be set here 0393 if (m_settingsUi.setPreviewDPI->isChecked()) { 0394 m_ksanew->setPreviewResolution(m_settingsUi.previewDPI->currentText().toFloat()); 0395 } 0396 else { 0397 // 0.0 means default value. 0398 m_ksanew->setPreviewResolution(0.0); 0399 } 0400 m_ksanew->enableAutoSelect(!m_settingsUi.u_disableSelections->isChecked()); 0401 0402 // pressing OK in the settings dialog means use those settings. 0403 m_saveLocation->setFolderUrl(m_settingsUi.saveDirRequester->url()); 0404 m_saveLocation->setImagePrefix(m_settingsUi.imgPrefix->text()); 0405 m_saveLocation->setImageFormatIndex(m_settingsUi.imgFormat->currentIndex()); 0406 0407 m_firstImage = true; 0408 } 0409 else { 0410 //Forget Changes 0411 readSettings(); 0412 } 0413 } 0414 0415 void Skanlite::imageReady(const QImage &image) 0416 { 0417 // save the image data 0418 m_img = image; 0419 if (m_settingsUi.showB4Save->isChecked() == true) { 0420 // show the image in the preview 0421 m_showImgDialog->setQImage(&m_img); 0422 m_showImgDialog->zoom2Fit(); 0423 m_showImgDialog->exec(); 0424 // save has been done as a result of save or then we got cancel 0425 } 0426 else { 0427 saveImage(); 0428 } 0429 } 0430 0431 bool urlExists(const QUrl& url) 0432 { 0433 if (url.isLocalFile()) { 0434 if (!QFileInfo::exists(url.toLocalFile())) { 0435 return false; 0436 } 0437 } 0438 else { 0439 #if KIO_VERSION >= QT_VERSION_CHECK(5, 240, 0) 0440 KIO::StatJob *statJob = KIO::stat(url, KIO::StatJob::DestinationSide, KIO::StatNoDetails); 0441 #else 0442 KIO::StatJob *statJob = KIO::statDetails(url, KIO::StatJob::DestinationSide, KIO::StatNoDetails); 0443 #endif 0444 KJobWidgets::setWindow(statJob, QApplication::activeWindow()); 0445 if (!statJob->exec()) { 0446 return false; 0447 } 0448 } 0449 return true; 0450 } 0451 0452 void Skanlite::saveImage() 0453 { 0454 QUrl dirUrl = m_saveLocation->folderUrl(); 0455 bool dirExists = urlExists(dirUrl); 0456 0457 // Ask the first time if we are in "ask on first" mode 0458 if (m_settingsUi.saveModeCB->currentIndex() == SaveModeAskFirst) { 0459 while (m_firstImage || !dirExists) { 0460 m_saveLocation->setOpenRequesterOnShow(!dirExists); 0461 if (m_saveLocation->exec() != QFileDialog::Accepted) { 0462 m_ksanew->cancelScan(); // In case we are cancelling a document feeder scan 0463 return; 0464 } 0465 dirUrl = m_saveLocation->folderUrl(); 0466 dirExists = urlExists(dirUrl); // check that we actually got an existing folder 0467 m_firstImage = false; 0468 } 0469 } 0470 else if (!dirExists) { 0471 // The save-folder from settings does not exist! Use the users home directory. 0472 dirUrl = QUrl::fromUserInput(QDir::homePath() + QLatin1Char('/')); 0473 m_saveLocation->setFolderUrl(dirUrl); 0474 } 0475 0476 QString prefix = m_saveLocation->imagePrefix(); 0477 QString imageMimetype = m_saveLocation->imageMimetype(); 0478 int fileNumber = m_saveLocation->startNumber(); 0479 QStringList filterList; 0480 0481 if ((m_img.format() == QImage::Format_Grayscale16) || 0482 (m_img.format() == QImage::Format_RGBX64)) 0483 { 0484 filterList = m_filter16BitList; 0485 if (imageMimetype != QLatin1String("image/png") && imageMimetype != QLatin1String("image/tiff")) { 0486 imageMimetype = QStringLiteral("image/png"); 0487 KMessageBox::information(this, i18n("The image will be saved in the PNG format, as the selected image type does not support saving 16 bit color images.")); 0488 } 0489 } else { 0490 filterList = m_filterList; 0491 } 0492 0493 // find next available file name for name suggestion 0494 QUrl fileUrl; 0495 QString fname; 0496 for (int i = fileNumber; i <= m_saveLocation->startNumberMax(); ++i) { 0497 fname = QStringLiteral("%1%2.%3") 0498 .arg(prefix) 0499 .arg(i, 4, 10, QLatin1Char('0')) 0500 .arg(m_saveLocation->imageSuffix()); 0501 0502 fileUrl = dirUrl; 0503 fileUrl.setPath(fileUrl.path() + fname); 0504 fileUrl = fileUrl.adjusted(QUrl::NormalizePathSegments); 0505 if (!urlExists(fileUrl)) { 0506 break; 0507 } 0508 } 0509 0510 if (m_settingsUi.saveModeCB->currentIndex() == SaveModeManual) { 0511 // prepare the save dialog 0512 QFileDialog saveDialog(this, i18n("New Image File Name")); 0513 saveDialog.setAcceptMode(QFileDialog::AcceptSave); 0514 saveDialog.setFileMode(QFileDialog::AnyFile); 0515 0516 // ask for a filename if requested. 0517 saveDialog.setDirectoryUrl(fileUrl.adjusted(QUrl::RemoveFilename)); 0518 saveDialog.selectUrl(fileUrl); 0519 // NOTE it is probably a bug that both setDirectoryUrl and selectUrl have 0520 // to be set to get remote urls to work 0521 0522 saveDialog.setMimeTypeFilters(filterList); 0523 saveDialog.selectMimeTypeFilter(imageMimetype); 0524 0525 if (saveDialog.exec() != QFileDialog::Accepted) { 0526 return; 0527 } 0528 0529 fileUrl = saveDialog.selectedUrls().at(0); 0530 } 0531 0532 m_firstImage = false; 0533 0534 // Get the quality 0535 int quality = -1; 0536 if (m_settingsUi.setQuality->isChecked()) { 0537 quality = m_settingsUi.imgQuality->value(); 0538 } 0539 0540 QString localName; 0541 QString suffix = QFileInfo(fileUrl.fileName()).suffix(); 0542 QString fileFormat; 0543 if (suffix.isEmpty()) { 0544 fileFormat = QStringLiteral("png"); 0545 } 0546 if (suffix == QLatin1String("pdf")) { 0547 fileFormat = QStringLiteral("pdf"); 0548 } 0549 0550 if (!fileUrl.isLocalFile()) { 0551 QTemporaryFile tmp; 0552 tmp.open(); 0553 if (suffix.isEmpty()) { 0554 localName = tmp.fileName(); 0555 } 0556 else { 0557 localName = QStringLiteral("%1.%2").arg(tmp.fileName(), suffix); 0558 } 0559 tmp.close(); // we just want the filename 0560 } 0561 else { 0562 localName = fileUrl.toLocalFile(); 0563 } 0564 0565 SkanliteImageSaver *imageSaver = new SkanliteImageSaver(this); 0566 connect(imageSaver, &SkanliteImageSaver::imageSaved, this, &Skanlite::imageSaved); 0567 connect(imageSaver, &SkanliteImageSaver::finished, &SkanliteImageSaver::deleteLater); 0568 0569 imageSaver->saveQImage(fileUrl, localName, m_img, fileFormat, quality); 0570 0571 m_showImgDialog->blockSignals(true); 0572 m_showImgDialog->close(); // calling close() on a closed window does nothing. 0573 // NOTE we need to block the signals since close() will emit rejected() 0574 m_showImgDialog->blockSignals(false); 0575 0576 // Disable parts of the interface and indicate that we are saving the image 0577 m_currentSaveUrl = fileUrl; 0578 m_ksanew->setDisabled(true); 0579 m_saveProgressBar->setMaximum(0); 0580 m_saveProgressBar->setValue(0); 0581 m_saveProgressBar->setVisible(true); 0582 m_saveUpdateTimer.start(); 0583 0584 // Save the file base name without number 0585 QString baseName = QFileInfo(fileUrl.fileName()).completeBaseName(); 0586 while ((!baseName.isEmpty()) && (baseName[baseName.size() - 1].isNumber())) { 0587 baseName.remove(baseName.size() - 1, 1); 0588 } 0589 m_saveLocation->setImagePrefix(baseName); 0590 0591 // Save the number 0592 if (fileNumber) { 0593 m_saveLocation->setStartNumber(fileNumber + 1); 0594 } 0595 0596 if (m_settingsUi.saveModeCB->currentIndex() == SaveModeManual) { 0597 // Save last used dir, prefix and suffix. 0598 m_saveLocation->setFolderUrl(KIO::upUrl(fileUrl)); 0599 m_saveLocation->setImageFormat(QFileInfo(fileUrl.fileName()).suffix()); 0600 } 0601 0602 0603 } 0604 0605 void Skanlite::updateSaveProgress() 0606 { 0607 QFileInfo saveInfo(m_currentSaveUrl.toLocalFile()); 0608 quint64 size = saveInfo.size()/1024; 0609 m_saveProgressBar->setMaximum(size); 0610 m_saveProgressBar->setValue(size); 0611 } 0612 0613 void Skanlite::imageSaved(const QUrl &fileUrl, const QString &localName, bool success) 0614 { 0615 if (!success) { 0616 perrorMessageBox(i18n("Failed to save image")); 0617 return; 0618 } 0619 0620 if (!fileUrl.isLocalFile()) { 0621 QFile tmpFile(localName); 0622 tmpFile.open(QIODevice::ReadOnly); 0623 auto uploadJob = KIO::storedPut(&tmpFile, fileUrl, -1); 0624 KJobWidgets::setWindow(uploadJob, QApplication::activeWindow()); 0625 bool ok = uploadJob->exec(); 0626 tmpFile.close(); 0627 tmpFile.remove(); 0628 if (!ok) { 0629 KMessageBox::error(nullptr, i18n("Failed to upload image")); 0630 } 0631 else { 0632 Q_EMIT m_dbusInterface.imageSaved(fileUrl.toString()); 0633 } 0634 } 0635 else { 0636 Q_EMIT m_dbusInterface.imageSaved(localName); 0637 } 0638 m_ksanew->setDisabled(false); 0639 m_ksanew->setFocus(); 0640 m_saveUpdateTimer.stop(); 0641 m_saveProgressBar->setVisible(false); 0642 } 0643 0644 void writeScannerOptions(const QString &groupName, const QMap <QString, QString> &opts) 0645 { 0646 KConfigGroup options(KSharedConfig::openStateConfig(), groupName); 0647 QMap<QString, QString>::const_iterator it = opts.constBegin(); 0648 while (it != opts.constEnd()) { 0649 options.writeEntry(it.key(), it.value()); 0650 ++it; 0651 } 0652 options.sync(); 0653 } 0654 0655 void readScannerOptions(const QString &groupName, QMap <QString, QString> &opts) 0656 { 0657 KConfigGroup scannerOptions(KSharedConfig::openStateConfig(), groupName); 0658 opts = scannerOptions.entryMap(); 0659 } 0660 0661 void Skanlite::saveScannerOptions() 0662 { 0663 KConfigGroup saving(KSharedConfig::openConfig(), QStringLiteral("Image Saving")); 0664 saving.writeEntry("NumberStartsFrom", m_saveLocation->startNumber()); 0665 0666 if (!m_ksanew) { 0667 return; 0668 } 0669 0670 if (!m_deviceName.isEmpty()) { 0671 KConfigGroup options(KSharedConfig::openStateConfig(), QStringLiteral("Options For %1").arg(m_deviceName)); 0672 QMap <QString, QString> opts; 0673 m_ksanew->getOptionValues(opts); 0674 writeScannerOptions(QStringLiteral("Options For %1").arg(m_deviceName), opts); 0675 } 0676 } 0677 0678 void Skanlite::defaultScannerOptions() 0679 { 0680 if (!m_ksanew) { 0681 return; 0682 } 0683 0684 applyScannerOptions(m_defaultScanOpts); 0685 } 0686 0687 void Skanlite::applyScannerOptions(const QMap <QString, QString> &opts) 0688 { 0689 if (m_ksanew->setOptionValues(opts) == -1) { 0690 m_pendingApplyScanOpts = opts; 0691 } else { 0692 m_pendingApplyScanOpts.clear(); 0693 } 0694 } 0695 0696 void Skanlite::loadScannerOptions() 0697 { 0698 if (!m_deviceName.isEmpty()) { 0699 KConfigGroup saving(KSharedConfig::openConfig(), QStringLiteral("Image Saving")); 0700 m_saveLocation->setStartNumber(saving.readEntry("NumberStartsFrom", 1)); 0701 0702 if (!m_ksanew) { 0703 return; 0704 } 0705 0706 QMap <QString, QString> opts; 0707 readScannerOptions(QStringLiteral("Options For %1").arg(m_deviceName), opts); 0708 applyScannerOptions(opts); 0709 } 0710 } 0711 0712 void Skanlite::alertUser(int type, const QString &strStatus) 0713 { 0714 switch (type) { 0715 case KSaneWidget::ErrorGeneral: 0716 KMessageBox::error(nullptr, strStatus, QStringLiteral("Skanlite Test")); 0717 break; 0718 default: 0719 KMessageBox::information(nullptr, strStatus, QStringLiteral("Skanlite Test")); 0720 } 0721 } 0722 0723 void Skanlite::buttonPressed(const QString &optionName, const QString &optionLabel, bool pressed) 0724 { 0725 qCDebug(SKANLITE_LOG) << "Button" << optionName << optionLabel << ((pressed) ? "pressed" : "released"); 0726 } 0727 0728 // D-Bus interface related helper functions 0729 0730 QStringList serializeScannerOptions(const QMap<QString, QString> &opts) 0731 { 0732 QStringList sl; 0733 QMap<QString, QString>::const_iterator it = opts.constBegin(); 0734 while (it != opts.constEnd()) { 0735 sl.append(it.key() + QLatin1Char('=') + it.value()); 0736 ++it; 0737 } 0738 return sl; 0739 } 0740 0741 void deserializeScannerOptions(const QStringList &settings, QMap<QString, QString> &opts) 0742 { 0743 for (const QString &s : settings) { 0744 int i = s.lastIndexOf(QLatin1Char('=')); 0745 opts[s.left(i)] = s.right(s.length()-i-1); 0746 } 0747 } 0748 0749 static const auto selectionSettings = { QLatin1String("tl-x"), QLatin1String("tl-y"), 0750 QLatin1String("br-x"), QLatin1String("br-y") }; 0751 0752 void filterSelectionSettings(QMap<QString, QString> &opts) 0753 { 0754 for (const auto &s : selectionSettings) { 0755 opts.remove(s); 0756 } 0757 } 0758 0759 bool containsSelectionSettings(const QMap<QString, QString> &opts) 0760 { 0761 for (const auto &s : selectionSettings) { 0762 if (opts.contains(s)) { 0763 return true; 0764 } 0765 } 0766 return false; 0767 } 0768 0769 void Skanlite::processSelectionOptions(QMap<QString, QString> &opts, bool ignoreSelection) 0770 { 0771 if (ignoreSelection) { 0772 filterSelectionSettings(opts); 0773 } 0774 else { 0775 if (containsSelectionSettings(opts)) { // make sure we really have selection to apply 0776 m_ksanew->setSelection(QPointF(0,0), QPointF(1,1)); // bcs settings have no effect if nothing was selected beforehand (Bug 377009) 0777 } 0778 } 0779 } 0780 0781 void Skanlite::updateWindowTitle(const QString &deviceName, const QString &deviceVendor, const QString &deviceModel) 0782 { 0783 if (!deviceVendor.isEmpty() && !deviceModel.isEmpty()) { 0784 setWindowTitle(i18nc("@title:window %1 = scanner maker, %2 = scanner model", "%1 %2 - Skanlite", deviceVendor, deviceModel)); 0785 } else if (!deviceName.isEmpty()) { 0786 setWindowTitle(i18nc("@title:window %1 = scanner device", "%1 - Skanlite", deviceName)); 0787 } else { 0788 setWindowTitle(i18n("Skanlite")); 0789 } 0790 } 0791 0792 // D-Bus interface related slots 0793 0794 void Skanlite::getScannerOptions() 0795 { 0796 if (!m_deviceName.isEmpty()) { 0797 QMap <QString, QString> opts; 0798 m_ksanew->getOptionValues(opts); 0799 m_dbusInterface.setReply(serializeScannerOptions(opts)); 0800 } 0801 } 0802 0803 void Skanlite::setScannerOptions(const QStringList &options, bool ignoreSelection) 0804 { 0805 if (!m_deviceName.isEmpty()) { 0806 QMap <QString, QString> opts; 0807 deserializeScannerOptions(options, opts); 0808 processSelectionOptions(opts, ignoreSelection); 0809 applyScannerOptions(opts); 0810 } 0811 } 0812 0813 0814 void Skanlite::getDefaultScannerOptions() 0815 { 0816 m_dbusInterface.setReply(serializeScannerOptions(m_defaultScanOpts)); 0817 } 0818 0819 static const QLatin1String defaultProfileGroup("Options For %1 - Profile %2"); // 1 - device, 2 - arg 0820 0821 void Skanlite::saveScannerOptionsToProfile(const QStringList &options, const QString &profile, bool ignoreSelection) 0822 { 0823 if (!m_deviceName.isEmpty()) { 0824 QMap <QString, QString> opts; 0825 deserializeScannerOptions(options, opts); 0826 processSelectionOptions(opts, ignoreSelection); 0827 writeScannerOptions(QString(defaultProfileGroup).arg(m_deviceName, profile), opts); 0828 } 0829 } 0830 0831 void Skanlite::switchToProfile(const QString &profile, bool ignoreSelection) 0832 { 0833 if (!m_deviceName.isEmpty()) { 0834 QMap <QString, QString> opts; 0835 readScannerOptions(QString(defaultProfileGroup).arg(m_deviceName, profile), opts); 0836 0837 if (opts.empty()) { 0838 opts = m_defaultScanOpts; 0839 } 0840 0841 processSelectionOptions(opts, ignoreSelection); 0842 applyScannerOptions(opts); 0843 } 0844 } 0845 0846 void Skanlite::getDeviceName() 0847 { 0848 if (!m_deviceName.isEmpty()) { 0849 m_dbusInterface.setReply(QStringList(m_deviceName)); 0850 } 0851 } 0852 0853 void Skanlite::getSelection() 0854 { 0855 if (!m_deviceName.isEmpty()) { 0856 QMap <QString, QString> opts; 0857 m_ksanew->getOptionValues(opts); 0858 0859 QStringList reply; 0860 for (const auto &key : selectionSettings ) { 0861 if (opts.contains(key)) { 0862 reply.append(key + QLatin1Char('=') + opts[key]); 0863 } 0864 } 0865 m_dbusInterface.setReply(reply); 0866 } 0867 } 0868 0869 void Skanlite::setSelection(const QStringList &options) 0870 { // here options contains selection related subset of options 0871 setScannerOptions(options, false); 0872 } 0873 0874 #include "moc_skanlite.cpp"