File indexing completed on 2024-04-21 05:54:08
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 if (d->m_ksaneInterface.getOption(Interface::TopLeftXOption) == NULL || 0193 d->m_ksaneInterface.getOption(Interface::TopLeftYOption) == NULL || 0194 d->m_ksaneInterface.getOption(Interface::BottomRightXOption) == NULL || 0195 d->m_ksaneInterface.getOption(Interface::BottomRightYOption) == NULL) return; 0196 d->m_ksaneInterface.getOption(Interface::TopLeftXOption)->setValue(area.left() * d->m_maximumScanArea.width()); 0197 d->m_ksaneInterface.getOption(Interface::TopLeftYOption)->setValue(area.top() * d->m_maximumScanArea.height()); 0198 d->m_ksaneInterface.getOption(Interface::BottomRightXOption)->setValue(area.right() * d->m_maximumScanArea.width()); 0199 d->m_ksaneInterface.getOption(Interface::BottomRightYOption)->setValue(area.bottom() * d->m_maximumScanArea.height()); 0200 } 0201 0202 Skanpage::ScanSplit Skanpage::scanSplit() const 0203 { 0204 return d->m_scanSplit; 0205 } 0206 0207 void Skanpage::setScanSplit(Skanpage::ScanSplit split) 0208 { 0209 if (split != d->m_scanSplit) { 0210 d->m_scanSplit = split; 0211 Q_EMIT scanSplitChanged(d->m_scanSplit); 0212 if (split != ScanNotSplit) clearSubAreas(); 0213 } 0214 } 0215 0216 const QList<QRectF> &Skanpage::scanSubAreas() 0217 { 0218 return d->m_scanSubAreas; 0219 } 0220 0221 void Skanpage::clearSubAreas() 0222 { 0223 if (!d->m_scanSubAreas.isEmpty()) { 0224 d->m_scanSubAreas.clear(); 0225 Q_EMIT scanSubAreasChanged(d->m_scanSubAreas); 0226 } 0227 } 0228 0229 void Skanpage::eraseSubArea(int index) 0230 { 0231 d->m_scanSubAreas.removeAt(index); 0232 Q_EMIT scanSubAreasChanged(d->m_scanSubAreas); 0233 } 0234 0235 bool Skanpage::appendSubArea(QRectF area) 0236 { 0237 for (int i = 0; i < d->m_scanSubAreas.length(); i++) { 0238 if (area == d->m_scanSubAreas[i]) { // If the appended area is a duplicate 0239 return false; 0240 } else if (area.contains(d->m_scanSubAreas[i])) { // This area contains a smaller one within 0241 d->m_scanSubAreas.removeAt(i); i--; // Remove redundant areas 0242 } else if (area.intersects(d->m_scanSubAreas[i])) { // Avoid very similar (overlaping too much) 0243 // return; // To not allow any overlap 0244 QRectF intersect = area.intersected(d->m_scanSubAreas[i]); 0245 float overlapProportion = intersect.width()*intersect.height() / (area.width()*area.height()); 0246 if (overlapProportion > 0.33) { d->m_scanSubAreas.removeAt(i); i--; } 0247 } 0248 } 0249 d->m_scanSubAreas.append(area); 0250 Q_EMIT scanSubAreasChanged(d->m_scanSubAreas); 0251 setScanSplit(Skanpage::ScanNotSplit); // Spliting and sub-areas... no 0252 return true; 0253 } 0254 0255 void Skanpage::selectSubArea(int index) 0256 { 0257 QRectF tmp = scanArea(); 0258 setScanArea(scanSubAreas().at(index)); 0259 eraseSubArea(index); 0260 appendSubArea(tmp); 0261 } 0262 0263 QImage Skanpage::previewImage() const 0264 { 0265 return d->m_previewImage; 0266 } 0267 0268 void Skanpage::preview() 0269 { 0270 if (Option *opt = d->m_ksaneInterface.getOption(Interface::TopLeftXOption)) opt->storeCurrentData(); 0271 if (Option *opt = d->m_ksaneInterface.getOption(Interface::TopLeftYOption)) opt->storeCurrentData(); 0272 if (Option *opt = d->m_ksaneInterface.getOption(Interface::BottomRightXOption)) opt->storeCurrentData(); 0273 if (Option *opt = d->m_ksaneInterface.getOption(Interface::BottomRightYOption)) opt->storeCurrentData(); 0274 if (d->m_maximumScanArea.isValid()) setScanArea(QRectF(0.0, 0.0, 1.0, 1.0)); 0275 0276 if (Option *opt = d->m_ksaneInterface.getOption(Interface::ResolutionOption)) { 0277 opt->storeCurrentData(); 0278 if (QVariant minRes = opt->minimumValue(); minRes.isValid()) { 0279 if (opt->type() == Option::TypeValueList) opt->setValue(minRes); 0280 else opt->setValue(minRes.toInt() < 25 ? 25 : minRes); 0281 } 0282 } 0283 if (Option* opt = d->m_ksaneInterface.getOption(Interface::PreviewOption)) opt->setValue(true); 0284 0285 d->m_scanIsPreview = true; 0286 0287 startScan(); 0288 } 0289 0290 void Skanpage::finishPreview() 0291 { 0292 if (Option* opt = d->m_ksaneInterface.getOption(Interface::TopLeftXOption)) opt->restoreSavedData(); 0293 if (Option* opt = d->m_ksaneInterface.getOption(Interface::TopLeftYOption)) opt->restoreSavedData(); 0294 if (Option* opt = d->m_ksaneInterface.getOption(Interface::BottomRightXOption)) opt->restoreSavedData(); 0295 if (Option* opt = d->m_ksaneInterface.getOption(Interface::BottomRightYOption)) opt->restoreSavedData(); 0296 0297 if (Option* opt = d->m_ksaneInterface.getOption(Interface::ResolutionOption)) opt->restoreSavedData(); 0298 if (Option* opt = d->m_ksaneInterface.getOption(Interface::PreviewOption)) opt->setValue(false); 0299 0300 d->m_scanIsPreview = false; 0301 } 0302 0303 void Skanpage::startScan() 0304 { 0305 if (!d->m_scanSubAreas.isEmpty()) { 0306 QRectF totalArea = d->m_scanArea; // Include last (unadded) area 0307 // This makes a rectangle that covers all the areas 0308 for (const QRectF& area : d->m_scanSubAreas) totalArea = totalArea.united(area); 0309 appendSubArea(d->m_scanArea); // Remember last area, for later use 0310 setScanArea(totalArea); // Scan all the needed area 0311 } 0312 d->m_ksaneInterface.startScan(); 0313 d->m_scanInProgress = true; 0314 d->m_state = ApplicationState::ScanInProgress; 0315 Q_EMIT applicationStateChanged(d->m_state); 0316 } 0317 0318 Skanpage::ApplicationState Skanpage::applicationState() const 0319 { 0320 return d->m_state; 0321 } 0322 0323 void Skanpage::imageReady(const QImage &image) 0324 { 0325 if (d->m_scanIsPreview) { 0326 d->m_ksaneInterface.stopScan(); // Needed for ADF 0327 finishPreview(); 0328 d->m_previewImage = image; 0329 Q_EMIT previewImageChanged(d->m_previewImage); 0330 return; // Do not save the preview to disk 0331 } 0332 if (d->m_scanSplit == ScanNotSplit && d->m_scanSubAreas.isEmpty()) { 0333 d->m_documentHandler.addImage(image); 0334 d->m_scannedImages++; 0335 return; // Regular scan ends here 0336 } 0337 auto applySubAreasToImage = [&]() { 0338 auto toOrigin = QTransform::fromTranslate(-d->m_scanArea.left(), -d->m_scanArea.top()); 0339 auto toScale = QTransform::fromScale(image.width() / d->m_scanArea.width(), image.height() / d->m_scanArea.height()); 0340 for (const QRectF& area : d->m_scanSubAreas) { 0341 QImage individualImage = image.copy(toScale.mapRect(toOrigin.mapRect(area)).toRect()); 0342 d->m_documentHandler.addImage(individualImage); 0343 d->m_scannedImages++; 0344 } 0345 }; 0346 if (!d->m_scanSubAreas.isEmpty()) { // There are sub-areas 0347 applySubAreasToImage(); 0348 setScanArea(d->m_scanSubAreas.back()); // Leave scanArea as last selection 0349 } else { 0350 bool v = d->m_scanSplit == ScanIsSplitV; 0351 QRectF half = d->m_scanArea; 0352 if (v) half.setWidth(half.width()/2); else half.setHeight(half.height()/2); 0353 d->m_scanSubAreas.append(half); // Fake sub-areas, no need for notification 0354 if (v) half.moveRight(d->m_scanArea.right()); else half.moveBottom(d->m_scanArea.bottom()); 0355 d->m_scanSubAreas.append(half); 0356 applySubAreasToImage(); 0357 } 0358 clearSubAreas(); // The sub-areas last just one scan 0359 } 0360 0361 void Skanpage::saveScannerOptions() 0362 { 0363 KConfigGroup options(KSharedConfig::openStateConfig(), QString::fromLatin1("Options For %1").arg(d->m_ksaneInterface.deviceName())); 0364 0365 QMap<QString, QString> optionMap = d->m_ksaneInterface.getOptionsMap(); 0366 0367 qCDebug(SKANPAGE_LOG) << QStringLiteral("Saving scanner options") << optionMap; 0368 QMap<QString, QString>::const_iterator it = optionMap.constBegin(); 0369 while (it != optionMap.constEnd()) { 0370 options.writeEntry(it.key(), it.value()); 0371 ++it; 0372 } 0373 options.sync(); 0374 } 0375 0376 void Skanpage::loadScannerOptions() 0377 { 0378 KConfigGroup scannerOptions(KSharedConfig::openStateConfig(), QString::fromLatin1("Options For %1").arg(d->m_ksaneInterface.deviceName())); 0379 0380 qCDebug(SKANPAGE_LOG) << QStringLiteral("Loading scanner options") << scannerOptions.entryMap(); 0381 0382 d->m_ksaneInterface.setOptionsMap(scannerOptions.entryMap()); 0383 } 0384 0385 void Skanpage::availableDevices(const QList<DeviceInformation *> &deviceList) 0386 { 0387 if (d->m_state == SearchingForDevices) { 0388 d->m_availableDevices.updateDevicesList(deviceList); 0389 0390 d->m_state = DeviceSelection; 0391 Q_EMIT applicationStateChanged(d->m_state); 0392 0393 // if there is only one scanning device available, open it 0394 if (d->m_availableDevices.rowCount() == 1) { 0395 d->m_availableDevices.selectDevice(0); 0396 qCDebug(SKANPAGE_LOG) << QStringLiteral("Automatically selecting only available device: ") << d->m_availableDevices.getSelectedDeviceName(); 0397 openDevice(d->m_availableDevices.getSelectedDeviceName()); 0398 } 0399 } 0400 } 0401 0402 bool Skanpage::openDevice(const QString &deviceName, const QString &deviceVendor, const QString &deviceModel) 0403 { 0404 Interface::OpenStatus status = Interface::OpeningFailed; 0405 if (!deviceName.isEmpty()) { 0406 qCDebug(SKANPAGE_LOG) << QStringLiteral("Trying to open device: %1").arg(deviceName); 0407 status = d->m_ksaneInterface.openDevice(deviceName); 0408 if (status == Interface::OpeningSucceeded) { 0409 if (!deviceVendor.isEmpty()) { 0410 finishOpeningDevice(deviceName, deviceVendor, deviceModel); 0411 } else { 0412 finishOpeningDevice(deviceName, d->m_ksaneInterface.deviceVendor(), d->m_ksaneInterface.deviceModel()); 0413 } 0414 } else if (status == Interface::OpeningDenied) { 0415 showUserMessage(SkanpageUtils::ErrorMessage, QStringLiteral("Access to selected device has been denied")); 0416 } else { 0417 showUserMessage(SkanpageUtils::ErrorMessage, QStringLiteral("Failed to open selected device.")); 0418 } 0419 0420 } 0421 return status == Interface::OpeningSucceeded; 0422 } 0423 0424 void Skanpage::finishOpeningDevice(const QString &deviceName, const QString &deviceVendor, const QString &deviceModel) 0425 { 0426 qCDebug(SKANPAGE_LOG()) << QStringLiteral("Finishing opening of device %1 and loading options").arg(deviceName); 0427 0428 KConfigGroup options(KSharedConfig::openStateConfig(), QStringLiteral("general")); 0429 options.writeEntry(QStringLiteral("deviceName"), deviceName); 0430 options.writeEntry(QStringLiteral("deviceModel"), deviceVendor); 0431 options.writeEntry(QStringLiteral("deviceVendor"), deviceModel); 0432 0433 d->m_deviceName = deviceName; 0434 d->m_deviceVendor = deviceVendor; 0435 d->m_deviceModel = deviceModel; 0436 Q_EMIT deviceInfoUpdated(); 0437 0438 d->m_optionsModel.setOptionsList(d->m_ksaneInterface.getOptionsList()); 0439 Q_EMIT optionsChanged(); 0440 0441 // load saved options 0442 loadScannerOptions(); 0443 0444 d->m_scanAreaConnectionsDone = false; 0445 setupScanningBounds(); 0446 0447 d->m_state = ReadyForScan; 0448 Q_EMIT applicationStateChanged(d->m_state); 0449 } 0450 0451 void Skanpage::reloadDevicesList() 0452 { 0453 qCDebug(SKANPAGE_LOG()) << QStringLiteral("(Re-)loading devices list"); 0454 0455 if (d->m_ksaneInterface.closeDevice()) { 0456 d->m_deviceName.clear(); 0457 d->m_deviceVendor.clear(); 0458 d->m_deviceModel.clear(); 0459 Q_EMIT deviceInfoUpdated(); 0460 d->m_optionsModel.clearOptions(); 0461 Q_EMIT optionsChanged(); 0462 } 0463 d->m_state = SearchingForDevices; 0464 Q_EMIT applicationStateChanged(d->m_state); 0465 d->m_ksaneInterface.reloadDevicesList(d->m_configuration->showAllDevices() ? Interface::DeviceType::AllDevices : Interface::DeviceType::NoCameraAndVirtualDevices); 0466 } 0467 0468 void Skanpage::showKSaneMessage(Interface::ScanStatus status, const QString &strStatus) 0469 { 0470 switch (status) { 0471 case Interface::ErrorGeneral: 0472 showUserMessage(SkanpageUtils::ErrorMessage, strStatus); 0473 break; 0474 case Interface::Information: 0475 showUserMessage(SkanpageUtils::InformationMessage, strStatus); 0476 break; 0477 default: 0478 break; 0479 } 0480 } 0481 0482 void Skanpage::showUserMessage(SkanpageUtils::MessageLevel level, const QString &text) 0483 { 0484 Q_EMIT newUserMessage(QVariant(level), QVariant(text)); 0485 } 0486 0487 void Skanpage::progressUpdated(int progress) 0488 { 0489 d->m_progress = progress; 0490 Q_EMIT progressChanged(d->m_progress); 0491 } 0492 0493 void Skanpage::batchModeCountDown(int remainingSeconds) 0494 { 0495 d->m_remainingSeconds = remainingSeconds; 0496 Q_EMIT countDownChanged(d->m_remainingSeconds); 0497 } 0498 0499 int Skanpage::progress() const 0500 { 0501 return d->m_progress; 0502 } 0503 0504 int Skanpage::countDown() const 0505 { 0506 return d->m_remainingSeconds; 0507 } 0508 0509 Interface *Skanpage::ksaneInterface() const 0510 { 0511 return &d->m_ksaneInterface; 0512 } 0513 0514 DocumentModel *Skanpage::documentModel() const 0515 { 0516 return &d->m_documentHandler; 0517 } 0518 0519 DevicesModel *Skanpage::devicesModel() const 0520 { 0521 return &d->m_availableDevices; 0522 } 0523 0524 FormatModel *Skanpage::formatModel() const 0525 { 0526 return &d->m_formatModel; 0527 } 0528 0529 FilteredOptionsModel *Skanpage::optionsModel() const 0530 { 0531 return &d->m_filteredOptionsModel; 0532 } 0533 0534 OCRLanguageModel *Skanpage::languageModel() const 0535 { 0536 return d->m_OCREngine.languages(); 0537 } 0538 0539 SkanpageConfiguration *Skanpage::configuration() const 0540 { 0541 return d->m_configuration; 0542 } 0543 0544 SkanpageState *Skanpage::stateConfiguration() const 0545 { 0546 return d->m_stateConfiguration; 0547 } 0548 0549 bool Skanpage::OCRavailable() const 0550 { 0551 return d->m_OCREngine.available(); 0552 } 0553 0554 void Skanpage::print() { 0555 d->m_documentPrinter.printDocument(d->m_documentHandler.selectPages(QList<int>())); 0556 } 0557 0558 void Skanpage::registerAction(QObject* item, QObject* shortcuts, const QString &iconText) 0559 { 0560 auto getQKeySequence = [](const QVariant &variant) -> QKeySequence { 0561 if (variant.typeId() == QMetaType::QKeySequence) return variant.value<QKeySequence>(); 0562 else if (variant.typeId() == QMetaType::QString) return variant.value<QString>(); 0563 else return variant.value<QKeySequence::StandardKey>(); 0564 }; 0565 0566 auto getKStandardShortcuts = [](const QVariant &variant) -> QList<QKeySequence> { 0567 auto id = KStandardShortcut::findByName(variant.toString()); 0568 if (id != KStandardShortcut::AccelNone) { 0569 return KStandardShortcut::shortcut(id); 0570 } else { 0571 qCDebug(SKANPAGE_LOG) << "Invalid KStandardShortcut specified from QML" << variant.toString(); 0572 return QList<QKeySequence>(); 0573 } 0574 }; 0575 0576 QString id = QQmlEngine::contextForObject(item)->nameForObject(item); 0577 0578 QAction *act = d->m_actionCollection->addAction(id); 0579 act->setText(item->property("text").toString()); 0580 act->setIcon(QIcon::fromTheme(iconText)); 0581 act->setIconVisibleInMenu(true); 0582 0583 QList<QKeySequence> sequences; 0584 if (QVariant prop = item->property("shortcut"); prop.isValid()) { 0585 QKeySequence seq = getQKeySequence(prop); 0586 if (!seq.isEmpty()) sequences.append(seq); 0587 } 0588 if (QVariant prop = item->property("shortcutsName"); prop.isValid() && !prop.toString().isEmpty()) { 0589 sequences.append(getKStandardShortcuts(prop)); 0590 } 0591 d->m_actionCollection->setDefaultShortcuts(act, sequences); 0592 d->m_actionCollection->readSettings(); 0593 0594 auto updateKeySequences = [=]() { 0595 // Set the first, only, or empty shortcut. Passing a QKeySequence doesn't always work 0596 item->setProperty("shortcut", act->shortcut().toString(QKeySequence::PortableText)); 0597 0598 QList<QVariant> sequenceList; // To set the alternate shortcuts 0599 for (int i = 1; i < act->shortcuts().size(); i++) { 0600 sequenceList.append(act->shortcuts().at(i).toString(QKeySequence::PortableText)); 0601 } 0602 shortcuts->setProperty("sequences", sequenceList); 0603 }; 0604 updateKeySequences(); // Move the specified shortcut to the QML Shortcut object 0605 connect(act, &QAction::changed, this, updateKeySequences); 0606 } 0607 0608 void Skanpage::showShortcutsDialog() { 0609 KShortcutsDialog::showDialog(d->m_actionCollection); 0610 } 0611 0612 void Skanpage::cancelScan() 0613 { 0614 if (d->m_progress > 0 && d->m_ksaneInterface.scanImage()) { 0615 d->m_ksaneInterface.lockScanImage(); 0616 QImage image = *d->m_ksaneInterface.scanImage(); 0617 d->m_ksaneInterface.unlockScanImage(); 0618 imageReady(image); 0619 } 0620 0621 d->m_ksaneInterface.stopScan(); 0622 } 0623 0624 void Skanpage::imageTemporarilySaved() 0625 { 0626 d->m_scannedImages--; 0627 checkFinish(); 0628 } 0629 0630 void Skanpage::scanningFinished(Interface::ScanStatus status, const QString &strStatus) 0631 { 0632 //only print debug, errors are already reported by Interface::userMessage 0633 qCDebug(SKANPAGE_LOG) << QStringLiteral("Finished scanning! Status code:") << status << QStringLiteral("Status message:") << strStatus; 0634 0635 if (d->m_scanIsPreview) { // imageReady didn't execute (there was an error) 0636 finishPreview(); // Restore options anyways 0637 } 0638 0639 d->m_scanInProgress = false; 0640 checkFinish(); 0641 } 0642 0643 void Skanpage::checkFinish() 0644 { 0645 if (d->m_scannedImages == 0 && !d->m_scanInProgress) { 0646 d->m_state = ApplicationState::ReadyForScan; 0647 Q_EMIT applicationStateChanged(d->m_state); 0648 } 0649 } 0650 0651 #include "moc_Skanpage.cpp"