File indexing completed on 2024-04-28 09:49:43
0001 /** 0002 * SPDX-FileCopyrightText: 2015 by Kåre Särs <kare.sars@iki .fi> 0003 * SPDX-FileCopyrightText: 2021 by Alexander Stippich <a.stippich@gmx.net> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #include "Skanpage.h" 0009 0010 #include <QThread> 0011 #include <QtQml> 0012 0013 #include <KConfigGroup> 0014 #include <KSharedConfig> 0015 #include <KShortcutsDialog> 0016 0017 #include "DevicesModel.h" 0018 #include "OptionsModel.h" 0019 #include "FormatModel.h" 0020 #include "FilteredOptionsModel.h" 0021 #include "DocumentSaver.h" 0022 #include "DocumentPrinter.h" 0023 #include "OCREngine.h" 0024 #include "skanpage_debug.h" 0025 0026 class SkanpagePrivate { 0027 public: 0028 KSaneCore::Interface m_ksaneInterface; 0029 DocumentModel m_documentHandler; 0030 DevicesModel m_availableDevices; 0031 OptionsModel m_optionsModel; 0032 FormatModel m_formatModel; 0033 FilteredOptionsModel m_filteredOptionsModel; 0034 DocumentSaver m_documentSaver; 0035 DocumentPrinter m_documentPrinter; 0036 OCREngine m_OCREngine; 0037 QThread m_fileIOThread; 0038 SkanpageConfiguration *m_configuration; 0039 KActionCollection *m_actionCollection; 0040 SkanpageState *m_stateConfiguration; 0041 0042 int m_progress = 100; 0043 int m_remainingSeconds = 0; 0044 int m_scannedImages = 0; 0045 Skanpage::ApplicationState m_state = Skanpage::SearchingForDevices; 0046 bool m_scanInProgress = false; 0047 bool m_scanIsPreview = false; 0048 QRectF m_maximumScanArea; 0049 QRectF m_scanArea; // Rectangle from (0, 0) to (1, 1) 0050 Skanpage::ScanSplit m_scanSplit = Skanpage::ScanNotSplit; 0051 QList<QRectF> m_scanSubAreas; 0052 bool m_scanAreaConnectionsDone = false; 0053 QImage m_previewImage; 0054 QString m_deviceName; 0055 QString m_deviceVendor; 0056 QString m_deviceModel; 0057 }; 0058 0059 using namespace KSaneCore; 0060 0061 Skanpage::Skanpage(const QString &deviceName, QObject *parent) 0062 : QObject(parent) 0063 , d(std::make_unique<SkanpagePrivate>()) 0064 { 0065 d->m_stateConfiguration = SkanpageState::self(); 0066 d->m_configuration = SkanpageConfiguration::self(); 0067 if (d->m_configuration->defaultFolder().isEmpty()) { 0068 d->m_configuration->setDefaultFolder(QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation))); 0069 } 0070 0071 d->m_filteredOptionsModel.setSourceModel(&d->m_optionsModel); 0072 0073 d->m_actionCollection = new KActionCollection(this); 0074 0075 connect(&d->m_ksaneInterface, &Interface::scannedImageReady, this, &Skanpage::imageReady); 0076 connect(&d->m_ksaneInterface, &Interface::availableDevices, this, &Skanpage::availableDevices); 0077 connect(&d->m_ksaneInterface, &Interface::userMessage, this, &Skanpage::showKSaneMessage); 0078 connect(&d->m_ksaneInterface, &Interface::scanProgress, this, &Skanpage::progressUpdated); 0079 connect(&d->m_ksaneInterface, &Interface::scanFinished, this, &Skanpage::scanningFinished); 0080 connect(&d->m_ksaneInterface, &Interface::batchModeCountDown, this, &Skanpage::batchModeCountDown); 0081 connect(&d->m_documentHandler, &DocumentModel::newPageAdded, this, &Skanpage::imageTemporarilySaved); 0082 0083 d->m_fileIOThread.start(); 0084 d->m_documentSaver.moveToThread(&d->m_fileIOThread); 0085 d->m_OCREngine.moveToThread(&d->m_fileIOThread); 0086 d->m_documentSaver.setOCREngine(&d->m_OCREngine); 0087 0088 connect(&d->m_documentHandler, &DocumentModel::saveDocument, &d->m_documentSaver, &DocumentSaver::saveDocument); 0089 connect(&d->m_documentHandler, &DocumentModel::saveNewPageTemporary, &d->m_documentSaver, &DocumentSaver::saveNewPageTemporary); 0090 connect(&d->m_documentSaver, &DocumentSaver::pageTemporarilySaved, &d->m_documentHandler, &DocumentModel::updatePageInModel); 0091 connect(&d->m_documentSaver, &DocumentSaver::showUserMessage, this, &Skanpage::showUserMessage); 0092 connect(&d->m_documentSaver, &DocumentSaver::fileSaved, &d->m_documentHandler, &DocumentModel::updateFileInformation); 0093 connect(&d->m_documentSaver, &DocumentSaver::sharingFileSaved, &d->m_documentHandler, &DocumentModel::updateSharingFileInformation); 0094 connect(&d->m_documentPrinter, &DocumentPrinter::showUserMessage, this, &Skanpage::showUserMessage); 0095 0096 // try to open device from command line option first, then remembered device 0097 if (deviceName.isEmpty() || !openDevice(deviceName)) { 0098 0099 KConfigGroup options(KSharedConfig::openStateConfig(), QStringLiteral("general")); 0100 const QString savedDeviceName = options.readEntry(QStringLiteral("deviceName")); 0101 const QString savedDeviceVendor = options.readEntry(QStringLiteral("deviceVendor")); 0102 const QString savedDeviceModel = options.readEntry(QStringLiteral("deviceModel")); 0103 0104 if (!openDevice(savedDeviceName, savedDeviceVendor, savedDeviceModel)) { 0105 reloadDevicesList(); 0106 } 0107 } 0108 } 0109 0110 Skanpage::~Skanpage() 0111 { 0112 d->m_fileIOThread.quit(); 0113 d->m_configuration->save(); 0114 d->m_stateConfiguration->save(); 0115 saveScannerOptions(); 0116 d->m_fileIOThread.wait(); 0117 } 0118 0119 QString Skanpage::deviceVendor() const 0120 { 0121 return d->m_deviceVendor; 0122 } 0123 0124 QString Skanpage::deviceModel() const 0125 { 0126 return d->m_deviceModel; 0127 } 0128 0129 QString Skanpage::deviceName() const 0130 { 0131 return d->m_deviceName; 0132 } 0133 0134 void Skanpage::setupScanningBounds() 0135 { 0136 d->m_scanArea = QRectF(); 0137 0138 Option *tlx = d->m_ksaneInterface.getOption(Interface::TopLeftXOption); 0139 Option *tly = d->m_ksaneInterface.getOption(Interface::TopLeftYOption); 0140 Option *brx = d->m_ksaneInterface.getOption(Interface::BottomRightXOption); 0141 Option *bry = d->m_ksaneInterface.getOption(Interface::BottomRightYOption); 0142 0143 if (tlx && tly && brx && bry && 0144 tlx->state() == Option::StateActive && tly->state() == Option::StateActive && 0145 brx->state() == Option::StateActive && bry->state() == Option::StateActive 0146 ) { 0147 QVariant tlxMin = tlx->minimumValue(), tlyMin = tly->minimumValue(); 0148 QVariant brxMax = brx->maximumValue(), bryMax = bry->maximumValue(); 0149 if (tlxMin.isValid() && tlyMin.isValid() && brxMax.isValid() && bryMax.isValid()) { 0150 d->m_maximumScanArea.setCoords(tlxMin.toReal(), tlyMin.toReal(), brxMax.toReal(), bryMax.toReal()); 0151 d->m_scanArea.setCoords(tlx->value().toReal() / d->m_maximumScanArea.width(), 0152 tly->value().toReal() / d->m_maximumScanArea.height(), 0153 brx->value().toReal() / d->m_maximumScanArea.width(), 0154 bry->value().toReal() / d->m_maximumScanArea.height()); 0155 0156 if (!d->m_scanAreaConnectionsDone) { 0157 connect(tlx, &Option::valueChanged, this, [&](const QVariant &value){ 0158 d->m_scanArea.setLeft(value.toReal() / d->m_maximumScanArea.width()); 0159 Q_EMIT scanAreaChanged(d->m_scanArea); 0160 }); 0161 connect(tly, &Option::valueChanged, this, [&](const QVariant &value){ 0162 d->m_scanArea.setTop(value.toReal() / d->m_maximumScanArea.height()); 0163 Q_EMIT scanAreaChanged(d->m_scanArea); 0164 }); 0165 connect(brx, &Option::valueChanged, this, [&](const QVariant &value){ 0166 d->m_scanArea.setRight(value.toReal() / d->m_maximumScanArea.width()); 0167 Q_EMIT scanAreaChanged(d->m_scanArea); 0168 }); 0169 connect(bry, &Option::valueChanged, this, [&](const QVariant &value){ 0170 d->m_scanArea.setBottom(value.toReal() / d->m_maximumScanArea.height()); 0171 Q_EMIT scanAreaChanged(d->m_scanArea); 0172 }); 0173 d->m_scanAreaConnectionsDone = true; 0174 } 0175 } 0176 connect(tlx, &Option::optionReloaded, this, &Skanpage::setupScanningBounds, Qt::UniqueConnection); 0177 connect(tly, &Option::optionReloaded, this, &Skanpage::setupScanningBounds, Qt::UniqueConnection); 0178 connect(brx, &Option::optionReloaded, this, &Skanpage::setupScanningBounds, Qt::UniqueConnection); 0179 connect(bry, &Option::optionReloaded, this, &Skanpage::setupScanningBounds, Qt::UniqueConnection); 0180 } 0181 Q_EMIT scanAreaChanged(d->m_scanArea); 0182 } 0183 0184 QRectF Skanpage::scanArea() const 0185 { 0186 return d->m_scanArea; 0187 } 0188 0189 void Skanpage::setScanArea(QRectF area) 0190 { 0191 if (area == d->m_scanArea) return; 0192 d->m_ksaneInterface.getOption(Interface::TopLeftXOption)->setValue(area.left() * d->m_maximumScanArea.width()); 0193 d->m_ksaneInterface.getOption(Interface::TopLeftYOption)->setValue(area.top() * d->m_maximumScanArea.height()); 0194 d->m_ksaneInterface.getOption(Interface::BottomRightXOption)->setValue(area.right() * d->m_maximumScanArea.width()); 0195 d->m_ksaneInterface.getOption(Interface::BottomRightYOption)->setValue(area.bottom() * d->m_maximumScanArea.height()); 0196 } 0197 0198 Skanpage::ScanSplit Skanpage::scanSplit() const 0199 { 0200 return d->m_scanSplit; 0201 } 0202 0203 void Skanpage::setScanSplit(Skanpage::ScanSplit split) 0204 { 0205 if (split != d->m_scanSplit) { 0206 d->m_scanSplit = split; 0207 Q_EMIT scanSplitChanged(d->m_scanSplit); 0208 if (split != ScanNotSplit) clearSubAreas(); 0209 } 0210 } 0211 0212 const QList<QRectF> &Skanpage::scanSubAreas() 0213 { 0214 return d->m_scanSubAreas; 0215 } 0216 0217 void Skanpage::clearSubAreas() 0218 { 0219 if (!d->m_scanSubAreas.isEmpty()) { 0220 d->m_scanSubAreas.clear(); 0221 Q_EMIT scanSubAreasChanged(d->m_scanSubAreas); 0222 } 0223 } 0224 0225 void Skanpage::eraseSubArea(int index) 0226 { 0227 d->m_scanSubAreas.removeAt(index); 0228 Q_EMIT scanSubAreasChanged(d->m_scanSubAreas); 0229 } 0230 0231 bool Skanpage::appendSubArea(QRectF area) 0232 { 0233 for (int i = 0; i < d->m_scanSubAreas.length(); i++) { 0234 if (area == d->m_scanSubAreas[i]) { // If the appended area is a duplicate 0235 return false; 0236 } else if (area.contains(d->m_scanSubAreas[i])) { // This area contains a smaller one within 0237 d->m_scanSubAreas.removeAt(i); i--; // Remove redundant areas 0238 } else if (area.intersects(d->m_scanSubAreas[i])) { // Avoid very similar (overlaping too much) 0239 // return; // To not allow any overlap 0240 QRectF intersect = area.intersected(d->m_scanSubAreas[i]); 0241 float overlapProportion = intersect.width()*intersect.height() / (area.width()*area.height()); 0242 if (overlapProportion > 0.33) { d->m_scanSubAreas.removeAt(i); i--; } 0243 } 0244 } 0245 d->m_scanSubAreas.append(area); 0246 Q_EMIT scanSubAreasChanged(d->m_scanSubAreas); 0247 setScanSplit(Skanpage::ScanNotSplit); // Spliting and sub-areas... no 0248 return true; 0249 } 0250 0251 void Skanpage::selectSubArea(int index) 0252 { 0253 QRectF tmp = scanArea(); 0254 setScanArea(scanSubAreas().at(index)); 0255 eraseSubArea(index); 0256 appendSubArea(tmp); 0257 } 0258 0259 QImage Skanpage::previewImage() const 0260 { 0261 return d->m_previewImage; 0262 } 0263 0264 void Skanpage::preview() 0265 { 0266 if (Option *opt = d->m_ksaneInterface.getOption(Interface::TopLeftXOption)) opt->storeCurrentData(); 0267 if (Option *opt = d->m_ksaneInterface.getOption(Interface::TopLeftYOption)) opt->storeCurrentData(); 0268 if (Option *opt = d->m_ksaneInterface.getOption(Interface::BottomRightXOption)) opt->storeCurrentData(); 0269 if (Option *opt = d->m_ksaneInterface.getOption(Interface::BottomRightYOption)) opt->storeCurrentData(); 0270 if (d->m_maximumScanArea.isValid()) setScanArea(QRectF(0.0, 0.0, 1.0, 1.0)); 0271 0272 if (Option *opt = d->m_ksaneInterface.getOption(Interface::ResolutionOption)) { 0273 opt->storeCurrentData(); 0274 if (QVariant minRes = opt->minimumValue(); minRes.isValid()) { 0275 if (opt->type() == Option::TypeValueList) opt->setValue(minRes); 0276 else opt->setValue(minRes.toInt() < 25 ? 25 : minRes); 0277 } 0278 } 0279 if (Option* opt = d->m_ksaneInterface.getOption(Interface::PreviewOption)) opt->setValue(true); 0280 0281 d->m_scanIsPreview = true; 0282 0283 startScan(); 0284 } 0285 0286 void Skanpage::finishPreview() 0287 { 0288 if (Option* opt = d->m_ksaneInterface.getOption(Interface::TopLeftXOption)) opt->restoreSavedData(); 0289 if (Option* opt = d->m_ksaneInterface.getOption(Interface::TopLeftYOption)) opt->restoreSavedData(); 0290 if (Option* opt = d->m_ksaneInterface.getOption(Interface::BottomRightXOption)) opt->restoreSavedData(); 0291 if (Option* opt = d->m_ksaneInterface.getOption(Interface::BottomRightYOption)) opt->restoreSavedData(); 0292 0293 if (Option* opt = d->m_ksaneInterface.getOption(Interface::ResolutionOption)) opt->restoreSavedData(); 0294 if (Option* opt = d->m_ksaneInterface.getOption(Interface::PreviewOption)) opt->setValue(false); 0295 0296 d->m_scanIsPreview = false; 0297 } 0298 0299 void Skanpage::startScan() 0300 { 0301 if (!d->m_scanSubAreas.isEmpty()) { 0302 QRectF totalArea = d->m_scanArea; // Include last (unadded) area 0303 // This makes a rectangle that covers all the areas 0304 for (const QRectF& area : d->m_scanSubAreas) totalArea = totalArea.united(area); 0305 appendSubArea(d->m_scanArea); // Remember last area, for later use 0306 setScanArea(totalArea); // Scan all the needed area 0307 } 0308 d->m_ksaneInterface.startScan(); 0309 d->m_scanInProgress = true; 0310 d->m_state = ApplicationState::ScanInProgress; 0311 Q_EMIT applicationStateChanged(d->m_state); 0312 } 0313 0314 Skanpage::ApplicationState Skanpage::applicationState() const 0315 { 0316 return d->m_state; 0317 } 0318 0319 void Skanpage::imageReady(const QImage &image) 0320 { 0321 if (d->m_scanIsPreview) { 0322 d->m_ksaneInterface.stopScan(); // Needed for ADF 0323 finishPreview(); 0324 d->m_previewImage = image; 0325 Q_EMIT previewImageChanged(d->m_previewImage); 0326 return; // Do not save the preview to disk 0327 } 0328 if (d->m_scanSplit == ScanNotSplit && d->m_scanSubAreas.isEmpty()) { 0329 d->m_documentHandler.addImage(image); 0330 d->m_scannedImages++; 0331 return; // Regular scan ends here 0332 } 0333 auto applySubAreasToImage = [&]() { 0334 auto toOrigin = QTransform::fromTranslate(-d->m_scanArea.left(), -d->m_scanArea.top()); 0335 auto toScale = QTransform::fromScale(image.width() / d->m_scanArea.width(), image.height() / d->m_scanArea.height()); 0336 for (const QRectF& area : d->m_scanSubAreas) { 0337 QImage individualImage = image.copy(toScale.mapRect(toOrigin.mapRect(area)).toRect()); 0338 d->m_documentHandler.addImage(individualImage); 0339 d->m_scannedImages++; 0340 } 0341 }; 0342 if (!d->m_scanSubAreas.isEmpty()) { // There are sub-areas 0343 applySubAreasToImage(); 0344 setScanArea(d->m_scanSubAreas.back()); // Leave scanArea as last selection 0345 } else { 0346 bool v = d->m_scanSplit == ScanIsSplitV; 0347 QRectF half = d->m_scanArea; 0348 if (v) half.setWidth(half.width()/2); else half.setHeight(half.height()/2); 0349 d->m_scanSubAreas.append(half); // Fake sub-areas, no need for notification 0350 if (v) half.moveRight(d->m_scanArea.right()); else half.moveBottom(d->m_scanArea.bottom()); 0351 d->m_scanSubAreas.append(half); 0352 applySubAreasToImage(); 0353 } 0354 clearSubAreas(); // The sub-areas last just one scan 0355 } 0356 0357 void Skanpage::saveScannerOptions() 0358 { 0359 KConfigGroup options(KSharedConfig::openStateConfig(), QString::fromLatin1("Options For %1").arg(d->m_ksaneInterface.deviceName())); 0360 0361 QMap<QString, QString> optionMap = d->m_ksaneInterface.getOptionsMap(); 0362 0363 qCDebug(SKANPAGE_LOG) << QStringLiteral("Saving scanner options") << optionMap; 0364 QMap<QString, QString>::const_iterator it = optionMap.constBegin(); 0365 while (it != optionMap.constEnd()) { 0366 options.writeEntry(it.key(), it.value()); 0367 ++it; 0368 } 0369 options.sync(); 0370 } 0371 0372 void Skanpage::loadScannerOptions() 0373 { 0374 KConfigGroup scannerOptions(KSharedConfig::openStateConfig(), QString::fromLatin1("Options For %1").arg(d->m_ksaneInterface.deviceName())); 0375 0376 qCDebug(SKANPAGE_LOG) << QStringLiteral("Loading scanner options") << scannerOptions.entryMap(); 0377 0378 d->m_ksaneInterface.setOptionsMap(scannerOptions.entryMap()); 0379 } 0380 0381 void Skanpage::availableDevices(const QList<DeviceInformation *> &deviceList) 0382 { 0383 if (d->m_state == SearchingForDevices) { 0384 d->m_availableDevices.updateDevicesList(deviceList); 0385 0386 d->m_state = DeviceSelection; 0387 Q_EMIT applicationStateChanged(d->m_state); 0388 0389 // if there is only one scanning device available, open it 0390 if (d->m_availableDevices.rowCount() == 1) { 0391 d->m_availableDevices.selectDevice(0); 0392 qCDebug(SKANPAGE_LOG) << QStringLiteral("Automatically selecting only available device: ") << d->m_availableDevices.getSelectedDeviceName(); 0393 openDevice(d->m_availableDevices.getSelectedDeviceName()); 0394 } 0395 } 0396 } 0397 0398 bool Skanpage::openDevice(const QString &deviceName, const QString &deviceVendor, const QString &deviceModel) 0399 { 0400 Interface::OpenStatus status = Interface::OpeningFailed; 0401 if (!deviceName.isEmpty()) { 0402 qCDebug(SKANPAGE_LOG) << QStringLiteral("Trying to open device: %1").arg(deviceName); 0403 status = d->m_ksaneInterface.openDevice(deviceName); 0404 if (status == Interface::OpeningSucceeded) { 0405 if (!deviceVendor.isEmpty()) { 0406 finishOpeningDevice(deviceName, deviceVendor, deviceModel); 0407 } else { 0408 finishOpeningDevice(deviceName, d->m_ksaneInterface.deviceVendor(), d->m_ksaneInterface.deviceModel()); 0409 } 0410 } else if (status == Interface::OpeningDenied) { 0411 showUserMessage(SkanpageUtils::ErrorMessage, QStringLiteral("Access to selected device has been denied")); 0412 } else { 0413 showUserMessage(SkanpageUtils::ErrorMessage, QStringLiteral("Failed to open selected device.")); 0414 } 0415 0416 } 0417 return status == Interface::OpeningSucceeded; 0418 } 0419 0420 void Skanpage::finishOpeningDevice(const QString &deviceName, const QString &deviceVendor, const QString &deviceModel) 0421 { 0422 qCDebug(SKANPAGE_LOG()) << QStringLiteral("Finishing opening of device %1 and loading options").arg(deviceName); 0423 0424 KConfigGroup options(KSharedConfig::openStateConfig(), QStringLiteral("general")); 0425 options.writeEntry(QStringLiteral("deviceName"), deviceName); 0426 options.writeEntry(QStringLiteral("deviceModel"), deviceVendor); 0427 options.writeEntry(QStringLiteral("deviceVendor"), deviceModel); 0428 0429 d->m_deviceName = deviceName; 0430 d->m_deviceVendor = deviceVendor; 0431 d->m_deviceModel = deviceModel; 0432 Q_EMIT deviceInfoUpdated(); 0433 0434 d->m_optionsModel.setOptionsList(d->m_ksaneInterface.getOptionsList()); 0435 Q_EMIT optionsChanged(); 0436 0437 // load saved options 0438 loadScannerOptions(); 0439 0440 d->m_scanAreaConnectionsDone = false; 0441 setupScanningBounds(); 0442 0443 d->m_state = ReadyForScan; 0444 Q_EMIT applicationStateChanged(d->m_state); 0445 } 0446 0447 void Skanpage::reloadDevicesList() 0448 { 0449 qCDebug(SKANPAGE_LOG()) << QStringLiteral("(Re-)loading devices list"); 0450 0451 if (d->m_ksaneInterface.closeDevice()) { 0452 d->m_deviceName.clear(); 0453 d->m_deviceVendor.clear(); 0454 d->m_deviceModel.clear(); 0455 Q_EMIT deviceInfoUpdated(); 0456 d->m_optionsModel.clearOptions(); 0457 Q_EMIT optionsChanged(); 0458 } 0459 d->m_state = SearchingForDevices; 0460 Q_EMIT applicationStateChanged(d->m_state); 0461 d->m_ksaneInterface.reloadDevicesList(d->m_configuration->showAllDevices() ? Interface::DeviceType::AllDevices : Interface::DeviceType::NoCameraAndVirtualDevices); 0462 } 0463 0464 void Skanpage::showKSaneMessage(Interface::ScanStatus status, const QString &strStatus) 0465 { 0466 switch (status) { 0467 case Interface::ErrorGeneral: 0468 showUserMessage(SkanpageUtils::ErrorMessage, strStatus); 0469 break; 0470 case Interface::Information: 0471 showUserMessage(SkanpageUtils::InformationMessage, strStatus); 0472 break; 0473 default: 0474 break; 0475 } 0476 } 0477 0478 void Skanpage::showUserMessage(SkanpageUtils::MessageLevel level, const QString &text) 0479 { 0480 Q_EMIT newUserMessage(QVariant(level), QVariant(text)); 0481 } 0482 0483 void Skanpage::progressUpdated(int progress) 0484 { 0485 d->m_progress = progress; 0486 Q_EMIT progressChanged(d->m_progress); 0487 } 0488 0489 void Skanpage::batchModeCountDown(int remainingSeconds) 0490 { 0491 d->m_remainingSeconds = remainingSeconds; 0492 Q_EMIT countDownChanged(d->m_remainingSeconds); 0493 } 0494 0495 int Skanpage::progress() const 0496 { 0497 return d->m_progress; 0498 } 0499 0500 int Skanpage::countDown() const 0501 { 0502 return d->m_remainingSeconds; 0503 } 0504 0505 Interface *Skanpage::ksaneInterface() const 0506 { 0507 return &d->m_ksaneInterface; 0508 } 0509 0510 DocumentModel *Skanpage::documentModel() const 0511 { 0512 return &d->m_documentHandler; 0513 } 0514 0515 DevicesModel *Skanpage::devicesModel() const 0516 { 0517 return &d->m_availableDevices; 0518 } 0519 0520 FormatModel *Skanpage::formatModel() const 0521 { 0522 return &d->m_formatModel; 0523 } 0524 0525 FilteredOptionsModel *Skanpage::optionsModel() const 0526 { 0527 return &d->m_filteredOptionsModel; 0528 } 0529 0530 OCRLanguageModel *Skanpage::languageModel() const 0531 { 0532 return d->m_OCREngine.languages(); 0533 } 0534 0535 SkanpageConfiguration *Skanpage::configuration() const 0536 { 0537 return d->m_configuration; 0538 } 0539 0540 SkanpageState *Skanpage::stateConfiguration() const 0541 { 0542 return d->m_stateConfiguration; 0543 } 0544 0545 bool Skanpage::OCRavailable() const 0546 { 0547 return d->m_OCREngine.available(); 0548 } 0549 0550 void Skanpage::print() { 0551 d->m_documentPrinter.printDocument(d->m_documentHandler.selectPages(QList<int>())); 0552 } 0553 0554 void Skanpage::registerAction(QObject* item, QObject* shortcuts, const QString &iconText) 0555 { 0556 auto getQKeySequence = [](const QVariant &variant) -> QKeySequence { 0557 if (variant.typeId() == QMetaType::QKeySequence) return variant.value<QKeySequence>(); 0558 else if (variant.typeId() == QMetaType::QString) return variant.value<QString>(); 0559 else return variant.value<QKeySequence::StandardKey>(); 0560 }; 0561 0562 auto getKStandardShortcuts = [](const QVariant &variant) -> QList<QKeySequence> { 0563 auto id = KStandardShortcut::findByName(variant.toString()); 0564 if (id != KStandardShortcut::AccelNone) { 0565 return KStandardShortcut::shortcut(id); 0566 } else { 0567 qCDebug(SKANPAGE_LOG) << "Invalid KStandardShortcut specified from QML" << variant.toString(); 0568 return QList<QKeySequence>(); 0569 } 0570 }; 0571 0572 QString id = QQmlEngine::contextForObject(item)->nameForObject(item); 0573 0574 QAction *act = d->m_actionCollection->addAction(id); 0575 act->setText(item->property("text").toString()); 0576 act->setIcon(QIcon::fromTheme(iconText)); 0577 act->setIconVisibleInMenu(true); 0578 0579 QList<QKeySequence> sequences; 0580 if (QVariant prop = item->property("shortcut"); prop.isValid()) { 0581 QKeySequence seq = getQKeySequence(prop); 0582 if (!seq.isEmpty()) sequences.append(seq); 0583 } 0584 if (QVariant prop = item->property("shortcutsName"); prop.isValid() && !prop.toString().isEmpty()) { 0585 sequences.append(getKStandardShortcuts(prop)); 0586 } 0587 d->m_actionCollection->setDefaultShortcuts(act, sequences); 0588 d->m_actionCollection->readSettings(); 0589 0590 auto updateKeySequences = [=]() { 0591 // Set the first, only, or empty shortcut. Passing a QKeySequence doesn't always work 0592 item->setProperty("shortcut", act->shortcut().toString(QKeySequence::PortableText)); 0593 0594 QList<QVariant> sequenceList; // To set the alternate shortcuts 0595 for (int i = 1; i < act->shortcuts().size(); i++) { 0596 sequenceList.append(act->shortcuts().at(i).toString(QKeySequence::PortableText)); 0597 } 0598 shortcuts->setProperty("sequences", sequenceList); 0599 }; 0600 updateKeySequences(); // Move the specified shortcut to the QML Shortcut object 0601 connect(act, &QAction::changed, this, updateKeySequences); 0602 } 0603 0604 void Skanpage::showShortcutsDialog() { 0605 KShortcutsDialog::showDialog(d->m_actionCollection); 0606 } 0607 0608 void Skanpage::cancelScan() 0609 { 0610 if (d->m_progress > 0 && d->m_ksaneInterface.scanImage()) { 0611 d->m_ksaneInterface.lockScanImage(); 0612 QImage image = *d->m_ksaneInterface.scanImage(); 0613 d->m_ksaneInterface.unlockScanImage(); 0614 imageReady(image); 0615 } 0616 0617 d->m_ksaneInterface.stopScan(); 0618 } 0619 0620 void Skanpage::imageTemporarilySaved() 0621 { 0622 d->m_scannedImages--; 0623 checkFinish(); 0624 } 0625 0626 void Skanpage::scanningFinished(Interface::ScanStatus status, const QString &strStatus) 0627 { 0628 //only print debug, errors are already reported by Interface::userMessage 0629 qCDebug(SKANPAGE_LOG) << QStringLiteral("Finished scanning! Status code:") << status << QStringLiteral("Status message:") << strStatus; 0630 0631 if (d->m_scanIsPreview) { // imageReady didn't execute (there was an error) 0632 finishPreview(); // Restore options anyways 0633 } 0634 0635 d->m_scanInProgress = false; 0636 checkFinish(); 0637 } 0638 0639 void Skanpage::checkFinish() 0640 { 0641 if (d->m_scannedImages == 0 && !d->m_scanInProgress) { 0642 d->m_state = ApplicationState::ReadyForScan; 0643 Q_EMIT applicationStateChanged(d->m_state); 0644 } 0645 } 0646 0647 #include "moc_Skanpage.cpp"