1 /************************************************************************
2 * *
3 * This file is part of Kooka, a scanning/OCR application using *
4 * Qt <http://www.qt.io> and KDE Frameworks <http://www.kde.org>. *
5 * *
6 * Copyright (C) 1999-2016 Klaas Freitag <freitag@suse.de> *
7 * Jonathan Marten <jjm@keelhaul.me.uk> *
8 * *
9 * Kooka is free software; you can redistribute it and/or modify it *
10 * under the terms of the GNU Library General Public License as *
11 * published by the Free Software Foundation and appearing in the *
12 * file COPYING included in the packaging of this file; either *
13 * version 2 of the License, or (at your option) any later version. *
14 * *
15 * As a special exception, permission is given to link this program *
16 * with any version of the KADMOS OCR/ICR engine (a product of *
17 * reRecognition GmbH, Kreuzlingen), and distribute the resulting *
18 * executable without including the source code for KADMOS in the *
19 * source distribution. *
20 * *
21 * This program is distributed in the hope that it will be useful, *
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
24 * GNU General Public License for more details. *
25 * *
26 * You should have received a copy of the GNU General Public *
27 * License along with this program; see the file COPYING. If *
28 * not, see <http://www.gnu.org/licenses/>. *
29 * *
30 ************************************************************************/
31
32 #include "kscandevice.h"
33
34 #include <qimage.h>
35 #include <qfileinfo.h>
36 #include <qapplication.h>
37 #include <qsocketnotifier.h>
38 #include <qstandardpaths.h>
39 #include <qdebug.h>
40
41 #include <klocalizedstring.h>
42 #include <kconfig.h>
43 #include <kpassworddialog.h>
44
45 #include "scanglobal.h"
46 #include "scandevices.h"
47 #include "kgammatable.h"
48 #include "kscancontrols.h"
49 #include "kscanoption.h"
50 #include "kscanoptset.h"
51 #include "deviceselector.h"
52 #include "imagemetainfo.h"
53 #include "scansettings.h"
54
55 extern "C" {
56 #include <sane/saneopts.h>
57 }
58
59 #define MIN_PREVIEW_DPI 75
60 #define MAX_PROGRESS 100
61
62
63 // Debugging options
64 #define DEBUG_OPTIONS
65 #undef DEBUG_RELOAD
66 #undef DEBUG_CREATE
67 #define DEBUG_PARAMS
68
69 #ifdef DEBUG_OPTIONS
70 #include <iostream>
71 #endif // DEBUG_OPTIONS
72
73
74 // Accessing GUI options
75 // ---------------------
76
77 // Used only by ScanParams::slotVirtScanModeSelect()
guiSetEnabled(const QByteArray & name,bool state)78 void KScanDevice::guiSetEnabled(const QByteArray &name, bool state)
79 {
80 KScanOption *so = getExistingGuiElement(name);
81 if (so==nullptr) return;
82
83 QWidget *w = so->widget();
84 if (w==nullptr) return;
85
86 w->setEnabled(state && so->isSoftwareSettable());
87 }
88
89
getOption(const QByteArray & name,bool create)90 KScanOption *KScanDevice::getOption(const QByteArray &name, bool create)
91 {
92 QByteArray alias = aliasName(name);
93
94 if (mCreatedOptions.contains(alias))
95 {
96 #ifdef DEBUG_CREATE
97 qDebug() << "already exists" << alias;
98 #endif // DEBUG_CREATE
99 return (mCreatedOptions.value(alias));
100 }
101
102 if (!create)
103 {
104 #ifdef DEBUG_CREATE
105 qDebug() << "does not exist" << alias;
106 #endif // DEBUG_CREATE
107 return (nullptr);
108 }
109
110 #ifdef DEBUG_CREATE
111 qDebug() << "creating new" << alias;
112 #endif // DEBUG_CREATE
113 KScanOption *so = new KScanOption(alias, this);
114 mCreatedOptions.insert(alias, so);
115 return (so);
116 }
117
118
getExistingGuiElement(const QByteArray & name) const119 KScanOption *KScanDevice::getExistingGuiElement(const QByteArray &name) const
120 {
121 KScanOption *ret = nullptr;
122 QByteArray alias = aliasName(name);
123
124 for (OptionHash::const_iterator it = mCreatedOptions.constBegin();
125 it!=mCreatedOptions.constEnd(); ++it)
126 {
127 KScanOption *opt = it.value();
128 if (opt->isGuiElement() && opt->getName()==alias)
129 {
130 ret = opt;
131 break;
132 }
133 }
134
135 return (ret);
136 }
137
138
getGuiElement(const QByteArray & name,QWidget * parent)139 KScanOption *KScanDevice::getGuiElement(const QByteArray &name, QWidget *parent)
140 {
141 if (name.isEmpty()) return (nullptr);
142 if (!optionExists(name)) return (nullptr);
143
144 //qDebug() << "for" << name;
145
146 KScanOption *so = getExistingGuiElement(name); // see if already exists
147 if (so!=nullptr) return (so); // if so, just return that
148
149 so = getOption(name); // create a new scan option
150 if (so->isValid()) // option was created
151 {
152 QWidget *w = so->createWidget(parent); // create widget for option
153 if (w!=nullptr) w->setEnabled(so->isActive() && so->isSoftwareSettable());
154 //else qDebug() << "no widget created for" << name;
155 }
156 else // option not valid
157 { // (not known by scanner?)
158 //qDebug() << "option invalid" << name;
159 so = nullptr;
160 }
161
162 return (so);
163 }
164
165
166 // Constructor & destructor
167 // ------------------------
168
KScanDevice(QObject * parent)169 KScanDevice::KScanDevice(QObject *parent)
170 : QObject(parent)
171 {
172 //qDebug();
173
174 ScanGlobal::self()->init(); // do sane_init() first of all
175
176 mScannerHandle = nullptr;
177 mScannerInitialised = false; // is device opened yet?
178 mScannerName = "";
179
180 mScanningState = KScanDevice::ScanIdle;
181
182 mScanBuf = nullptr; // image data buffer while scanning
183 mScanImage = nullptr; // temporary image to scan into
184 mImageInfo = nullptr; // scanned image information
185
186 mSocketNotifier = nullptr; // socket notifier for async scanning
187 mSavedOptions = nullptr; // options to save during preview
188
189 mBytesRead = 0;
190 mBytesUsed = 0;
191 mPixelX = 0;
192 mPixelY = 0;
193
194 // TODO: make this just a function call, for predictable order
195 // (sigScanFinished before sigNewImage)
196 connect(this, &KScanDevice::sigScanFinished, this, &KScanDevice::slotScanFinished);
197 }
198
199
~KScanDevice()200 KScanDevice::~KScanDevice()
201 {
202 delete mSavedOptions;
203 delete mImageInfo;
204 // TODO: need to check and do closeDevice() here?
205 ScanGlobal::self()->setScanDevice(nullptr); // going away, don't call me
206
207 //qDebug();
208 }
209
210
211 // Opening/closing the scanner device
212 // ----------------------------------
213
openDevice(const QByteArray & backend)214 KScanDevice::Status KScanDevice::openDevice(const QByteArray &backend)
215 {
216 KScanDevice::Status stat = KScanDevice::Ok;
217
218 //qDebug() << "backend" << backend;
219
220 mSaneStatus = SANE_STATUS_UNSUPPORTED;
221 if (backend.isEmpty()) return (KScanDevice::ParamError);
222
223 // search for scanner
224 if (ScanDevices::self()->deviceInfo(backend)==nullptr) return (KScanDevice::NoDevice);
225
226 mScannerName = backend; // set now for authentication
227 QApplication::setOverrideCursor(Qt::WaitCursor); // potential lengthy operation
228 ScanGlobal::self()->setScanDevice(this); // for possible authentication
229 mSaneStatus = sane_open(backend.constData(), &mScannerHandle);
230
231 if (mSaneStatus==SANE_STATUS_ACCESS_DENIED) // authentication failed?
232 {
233 clearSavedAuth(); // clear any saved password
234 //qDebug() << "retrying authentication"; // try again once more
235 mSaneStatus = sane_open(backend.constData(), &mScannerHandle);
236 }
237
238 if (mSaneStatus==SANE_STATUS_GOOD)
239 {
240 stat = findOptions(); // fill dictionary with options
241 mScannerInitialised = true; // note scanner opened OK
242 }
243 else
244 {
245 stat = KScanDevice::OpenDevice;
246 mScannerName = "";
247 }
248
249 QApplication::restoreOverrideCursor();
250 return (stat);
251 }
252
253
closeDevice()254 void KScanDevice::closeDevice()
255 {
256 emit sigCloseDevice(); // tell callers we're closing
257
258 //qDebug() << "Saving default scan settings";
259 saveStartupConfig(); // save config for next startup
260
261 if (mScannerHandle!=nullptr)
262 {
263 if (mScanningState!=KScanDevice::ScanIdle)
264 {
265 //qDebug() << "Scanner is still active, calling sane_cancel()";
266 sane_cancel(mScannerHandle);
267 }
268 sane_close(mScannerHandle); // close the SANE device
269 mScannerHandle = nullptr; // scanner no longer open
270 }
271
272 // clear lists of options
273 QList<KScanOption *> opts = mCreatedOptions.values();
274 while (!opts.isEmpty()) delete opts.takeFirst();
275 mCreatedOptions.clear();
276 mKnownOptions.clear();
277
278 mScannerName = "";
279 mScannerInitialised = false;
280 }
281
282
283 // Scanner and image information
284 // -----------------------------
285
scannerDescription() const286 QString KScanDevice::scannerDescription() const
287 {
288 QString ret;
289
290 if (!mScannerName.isNull() && mScannerInitialised)
291 {
292 ret = ScanDevices::self()->deviceDescription(mScannerName);
293 }
294 else
295 {
296 ret = i18n("No scanner selected");
297 }
298
299 //qDebug() << "returning" << ret;
300 return (ret);
301 }
302
303
getMaxScanSize()304 QSize KScanDevice::getMaxScanSize()
305 {
306 QSize s;
307 double min, max;
308
309 KScanOption *so_w = getOption(SANE_NAME_SCAN_BR_X);
310 so_w->getRange(&min, &max);
311 s.setWidth(static_cast<int>(max));
312
313 KScanOption *so_h = getOption(SANE_NAME_SCAN_BR_Y);
314 so_h->getRange(&min, &max);
315 s.setHeight(static_cast<int>(max));
316
317 return (s);
318 }
319
320
getCurrentFormat(int * format,int * depth)321 void KScanDevice::getCurrentFormat(int *format, int *depth)
322 {
323 sane_get_parameters(mScannerHandle, &mSaneParameters);
324 *format = mSaneParameters.format;
325 *depth = mSaneParameters.depth;
326 }
327
328
329 // Listing the available options
330 // -----------------------------
331
findOptions()332 KScanDevice::Status KScanDevice::findOptions()
333 {
334 SANE_Int n;
335 SANE_Int opt;
336
337 if (sane_control_option(mScannerHandle, 0, SANE_ACTION_GET_VALUE,
338 &n, &opt)!=SANE_STATUS_GOOD)
339 {
340 qWarning() << "cannot read option 0 (count)";
341 return (KScanDevice::ControlError);
342 }
343
344 mKnownOptions.clear();
345 for (int i = 1; i<n; i++)
346 {
347 const SANE_Option_Descriptor *d = sane_get_option_descriptor(mScannerHandle, i);
348 if (d==nullptr) continue; // could not get descriptor
349
350 QByteArray name;
351 if (d->name!=nullptr && strlen(d->name)>0) name = d->name;
352
353 if (d->type==SANE_TYPE_GROUP) // option is a group,
354 { // give it a dummy name
355 name = "group-";
356 name += QByteArray::number(i);
357 }
358
359 if (!name.isEmpty()) // must now have a name
360 {
361 #ifdef DEBUG_OPTIONS
362 qDebug() << "Option" << i << "is" << name;
363 #endif // DEBUG_OPTIONS
364 mKnownOptions.insert(i, name);
365 }
366 else qWarning() << "Invalid option" << i << "(no name and not a group)";
367 }
368
369 return (KScanDevice::Ok);
370 }
371
372
getAllOptions() const373 QList<QByteArray> KScanDevice::getAllOptions() const
374 {
375 return (mKnownOptions.values());
376 }
377
378
getCommonOptions() const379 QList<QByteArray> KScanDevice::getCommonOptions() const
380 {
381 QList<QByteArray> opts;
382
383 for (OptionHash::const_iterator it = mCreatedOptions.constBegin();
384 it!=mCreatedOptions.constEnd(); ++it)
385 {
386 KScanOption *so = it.value();
387 if (so->isCommonOption()) opts.append(it.key());
388 }
389
390 return (opts);
391 }
392
393
getAdvancedOptions() const394 QList<QByteArray> KScanDevice::getAdvancedOptions() const
395 {
396 QList<QByteArray> opts;
397
398 for (OptionHash::const_iterator it = mCreatedOptions.constBegin();
399 it!=mCreatedOptions.constEnd(); ++it)
400 {
401 KScanOption *so = it.value();
402 if (!so->isCommonOption()) opts.append(it.key());
403 }
404
405 return (opts);
406 }
407
408
409 // Controlling options
410 // -------------------
411
getOptionIndex(const QByteArray & name) const412 int KScanDevice::getOptionIndex(const QByteArray &name) const
413 {
414 return (mKnownOptions.key(name));
415 }
416
417
optionExists(const QByteArray & name) const418 bool KScanDevice::optionExists(const QByteArray &name) const
419 {
420 if (name.isEmpty()) return (false);
421 QByteArray alias = aliasName(name);
422 return (mKnownOptions.key(alias)!=0);
423 }
424
425
426 /* This function tries to find name aliases which appear from backend to backend.
427 * Example: Custom-Gamma is for epson backends 'gamma-correction' - not a really
428 * cool thing :-|
429 * Maybe this helps us out ?
430 */
aliasName(const QByteArray & name) const431 QByteArray KScanDevice::aliasName( const QByteArray& name ) const
432 {
433 if (mCreatedOptions.contains(name)) return (name);
434
435 QByteArray ret = name;
436 if( name == SANE_NAME_CUSTOM_GAMMA )
437 {
438 if (mCreatedOptions.contains("gamma-correction"))
439 ret = "gamma-correction";
440 }
441
442 //if( ret != name )
443 //qDebug() << "Found alias for" << name << "which is" << ret;
444
445 return( ret );
446 }
447
448
applyOption(KScanOption * opt)449 void KScanDevice::applyOption(KScanOption *opt)
450 {
451 bool reload = true; // is a reload needed?
452
453 if (opt!=nullptr) // an option is specified
454 {
455 #ifdef DEBUG_RELOAD
456 qDebug() << "option" << opt->getName();
457 #endif // DEBUG_APPLY
458 reload = opt->apply(); // apply this option
459 }
460
461 if (!reload) // need to reload now?
462 {
463 #ifdef DEBUG_RELOAD
464 qDebug() << "Reload of others not needed";
465 #endif // DEBUG_RELOAD
466 return;
467 }
468 // reload of all others needed
469 for (OptionHash::const_iterator it = mCreatedOptions.constBegin();
470 it!=mCreatedOptions.constEnd(); ++it)
471 {
472 KScanOption *so = it.value();
473 if (!so->isGuiElement()) continue;
474 if (opt==nullptr || so!=opt)
475 {
476 #ifdef DEBUG_RELOAD
477 qDebug() << "Reloading" << so->getName();
478 #endif // DEBUG_RELOAD
479 so->reload();
480 so->redrawWidget();
481 }
482 }
483
484 #ifdef DEBUG_RELOAD
485 qDebug() << "Finished";
486 #endif // DEBUG_RELOAD
487 }
488
489
reloadAllOptions()490 void KScanDevice::reloadAllOptions()
491 {
492 #ifdef DEBUG_RELOAD
493 qDebug();
494 #endif // DEBUG_RELOAD
495 applyOption(nullptr);
496 }
497
498
499 // Scanning control
500 // ----------------
501
slotStopScanning()502 void KScanDevice::slotStopScanning()
503 {
504 //qDebug() << "Attempt to stop scanning";
505
506 // TODO: needed? will be done by acquireData()
507 if (mScanningState==KScanDevice::ScanInProgress) emit sigScanFinished(KScanDevice::Cancelled);
508
509 mScanningState = KScanDevice::ScanStopNow;
510 }
511
512
513 // Preview image
514 // -------------
515
previewFile() const516 const QString KScanDevice::previewFile() const
517 {
518 // TODO: this doesn't work if that directory doesn't exist,
519 // and nothing ever creates that directory!
520 //
521 // Do we want this feature to work?
522 // If so, create the directory in savePreviewImage() below
523 QString dir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)+"/previews/";
524 QString sname(scannerDescription());
525 sname.replace('/', "_");
526 return (dir+sname);
527 }
528
529
loadPreviewImage()530 QImage KScanDevice::loadPreviewImage()
531 {
532 const QString prevFile = previewFile();
533
534 //qDebug() << "Loading preview from" << prevFile;
535 return (QImage(prevFile));
536 }
537
538
savePreviewImage(const QImage & image) const539 bool KScanDevice::savePreviewImage(const QImage &image) const
540 {
541 if (image.isNull()) return (false);
542
543 const QString prevFile = previewFile();
544
545 //qDebug() << "Saving preview to" << prevFile;
546 return (image.save(prevFile, "BMP"));
547 }
548
549
550 // Displaying scan options
551 // -----------------------
552 //
553 // For debugging. Originally showOptions() was called prepareScan() and had
554 // the comment:
555 //
556 // prepareScan tries to set as much as parameters as possible.
557 //
558 // Function which applies all Options which need to be applied.
559 // See SANE-Documentation Table 4.5, description for SANE_CAP_SOFT_DETECT.
560 // The function sets the options which have SANE_CAP_AUTOMATIC set
561 // to automatic adjust.
562 //
563 // But this wasn't true - it only reports the current state of the options.
564
565 #ifdef DEBUG_OPTIONS
566
optionNotifyString(int opt)567 inline const char *optionNotifyString(int opt)
568 {
569 return (opt!=0 ? " X |" : " - |");
570 }
571
572
showOptions()573 void KScanDevice::showOptions()
574 {
575 qDebug() << "for" << mScannerName;
576 std::cerr << "----------------------------------+---+---+---+---+---+---+---+---+-------" << std::endl;
577 std::cerr << " Option |SSL|HSL|SDT|EMU|AUT|INA|ADV|PRI| Value" << std::endl;
578 std::cerr << "----------------------------------+---+---+---+---+---+---+---+---+-------" << std::endl;
579
580 for (OptionHash::const_iterator it = mCreatedOptions.constBegin();
581 it!=mCreatedOptions.constEnd(); ++it)
582 {
583 const KScanOption *so = it.value();
584 if (so->isGroup()) continue;
585
586 int cap = so->getCapabilities();
587 QString s = QString(it.key()).left(31).leftJustified(32);
588 std::cerr <<
589 " " << qPrintable(s) << " |" <<
590 optionNotifyString((cap & SANE_CAP_SOFT_SELECT)) <<
591 optionNotifyString((cap & SANE_CAP_HARD_SELECT)) <<
592 optionNotifyString((cap & SANE_CAP_SOFT_DETECT)) <<
593 optionNotifyString((cap & SANE_CAP_EMULATED)) <<
594 optionNotifyString((cap & SANE_CAP_AUTOMATIC)) <<
595 optionNotifyString((cap & SANE_CAP_INACTIVE)) <<
596 optionNotifyString((cap & SANE_CAP_ADVANCED)) <<
597 optionNotifyString(so->isPriorityOption()) <<
598 " " << qPrintable(so->get()) << std::endl;
599 }
600 std::cerr << "----------------------------------+---+---+---+---+---+---+---+---+-------" << std::endl;
601 }
602
603 #endif // DEBUG_OPTIONS
604
605
606 // Creating a new image to receive the scan/preview
607 // ------------------------------------------------
608
getImageFormat(const SANE_Parameters * p)609 static ImageMetaInfo::ImageType getImageFormat(const SANE_Parameters *p)
610 {
611 if (p==nullptr) return (ImageMetaInfo::Unknown);
612
613 if (p->depth==1) // Line art (bitmap)
614 {
615 return (ImageMetaInfo::BlackWhite);
616 }
617 else if (p->depth==8) // 8 bit RGB
618 {
619 if (p->format==SANE_FRAME_GRAY) // Grey scale
620 {
621 return (ImageMetaInfo::Greyscale);
622 }
623 else // True colour
624 {
625 return (ImageMetaInfo::HighColour);
626 }
627 }
628 else // Error, no others supported
629 {
630 //qDebug() << "Only bit depths 1 or 8 supported!";
631 return (ImageMetaInfo::Unknown);
632 }
633 }
634
635
createNewImage(const SANE_Parameters * p)636 KScanDevice::Status KScanDevice::createNewImage(const SANE_Parameters *p)
637 {
638 QImage::Format fmt;
639 ImageMetaInfo::ImageType itype = getImageFormat(p); // what format should this be?
640 switch (itype) // choose QImage option for that
641 {
642 default:
643 case ImageMetaInfo::Unknown: return (KScanDevice::ParamError);
644 case ImageMetaInfo::BlackWhite: fmt = QImage::Format_Mono; break;
645 case ImageMetaInfo::Greyscale: fmt = QImage::Format_Indexed8; break;
646 case ImageMetaInfo::HighColour: fmt = QImage::Format_RGB32; break;
647 }
648
649 delete mScanImage; // recreate new image
650 mScanImage = new QImage(p->pixels_per_line,p->lines,fmt);
651 if (mScanImage==nullptr) return (KScanDevice::NoMemory);
652
653 if (itype==ImageMetaInfo::BlackWhite) // Line art (bitmap)
654 {
655 mScanImage->setColor(0,qRgb(0x00,0x00,0x00)); // set black/white palette
656 mScanImage->setColor(1,qRgb(0xFF,0xFF,0xFF));
657 }
658 else if (itype==ImageMetaInfo::Greyscale) // 8 bit grey
659 { // set grey scale palette
660 for (int i = 0; i<256; i++) mScanImage->setColor(i,qRgb(i,i,i));
661 }
662
663 return (KScanDevice::Ok);
664 }
665
666
667 // Acquiring preview/scan image
668 // ----------------------------
669
acquirePreview(bool forceGray,int dpi)670 KScanDevice::Status KScanDevice::acquirePreview( bool forceGray, int dpi )
671 {
672 if (mSavedOptions!=nullptr) mSavedOptions->clear();
673 else mSavedOptions = new KScanOptSet("TempStore");
674
675 /* set Preview = ON if exists */
676 KScanOption *prev = getOption(SANE_NAME_PREVIEW, false);
677 if (prev!=nullptr)
678 {
679 prev->set(true);
680 prev->apply();
681 prev->set(false); // Ensure that it gets restored
682 mSavedOptions->backupOption(prev); // back to 'false' after previewing
683 }
684
685 // TODO: this block doesn't make sense (set the option to true if
686 // it is already true?)
687 /* Gray-Preview only done by widget? */
688 KScanOption *so = getOption(SANE_NAME_GRAY_PREVIEW, false);
689 if (so!=nullptr)
690 {
691 if (so->get()=="true")
692 {
693 /* Gray preview on */
694 so->set(true);
695 //qDebug() << "Setting GrayPreview ON";
696 }
697 else
698 {
699 so->set(false);
700 //qDebug() << "Setting GrayPreview OFF";
701 }
702 so->apply();
703 }
704
705 KScanOption *mode = getOption(SANE_NAME_SCAN_MODE, false);
706 if (mode!=nullptr)
707 {
708 //qDebug() << "Scan mode before preview is" << mode->get();
709 mSavedOptions->backupOption(mode);
710 /* apply if it has a widget, or apply always? */
711 if (mode->isGuiElement()) mode->apply();
712 }
713
714 /* Some sort of Scan Resolution option should always exist */
715 KScanOption *xres = getOption(SANE_NAME_SCAN_X_RESOLUTION, false);
716 if (xres==nullptr) xres = getOption(SANE_NAME_SCAN_RESOLUTION, false);
717 if (xres!=nullptr)
718 {
719 //qDebug() << "Scan resolution before preview is" << xres->get();
720 mSavedOptions->backupOption(xres);
721
722 int preview_dpi = dpi;
723 if (dpi==0) // preview DPI not specified
724 {
725 double min, max;
726 if (!xres->getRange(&min, &max))
727 {
728 //qDebug() << "Could not retrieve resolution range!";
729 min = 75.0; // hope every scanner can do this
730 }
731
732 preview_dpi = (int) min;
733 if (preview_dpi<MIN_PREVIEW_DPI) preview_dpi = MIN_PREVIEW_DPI;
734 //qDebug() << "Resolution range" << min << "-" << max << "preview at" << preview_dpi;
735 }
736
737 KScanOption *yres = getOption(SANE_NAME_SCAN_Y_RESOLUTION, false);
738 if (yres!=nullptr)
739 {
740 /* if active ? */
741 mSavedOptions->backupOption(yres);
742 yres->set(preview_dpi);
743 yres->apply();
744 yres->get(&mCurrScanResolutionY);
745 }
746 else mCurrScanResolutionY = 0;
747
748 /* Resolution bind switch? */
749 KScanOption *bind = getOption(SANE_NAME_RESOLUTION_BIND, false);
750 if (bind!=nullptr)
751 {
752 /* Switch binding on if available */
753 mSavedOptions->backupOption(bind);
754 bind->set(true);
755 bind->apply();
756 }
757
758 xres->set(preview_dpi);
759 xres->apply();
760
761 /* Store the resulting preview for additional image information */
762 xres->get(&mCurrScanResolutionX);
763 if (mCurrScanResolutionY==0) mCurrScanResolutionY = mCurrScanResolutionX;
764 }
765
766 return (acquireData(true)); // perform the preview
767 }
768
769
applyAllOptions(bool prio)770 void KScanDevice::applyAllOptions(bool prio)
771 {
772 for (OptionHash::const_iterator it = mCreatedOptions.constBegin();
773 it!=mCreatedOptions.constEnd(); ++it)
774 {
775 KScanOption *so = it.value();
776 if (!so->isGuiElement()) continue;
777 if (so->isPriorityOption() ^ prio) continue;
778 if (so->isActive() && so->isSoftwareSettable()) so->apply();
779 }
780 }
781
782
783 /* Starts scanning
784 * depending on if a filename is given or not, the function tries to open
785 * the file using the Qt-Image-IO or really scans the image.
786 */
acquireScan(const QString & filename)787 KScanDevice::Status KScanDevice::acquireScan(const QString &filename)
788 {
789 if (filename.isEmpty()) // real scan
790 {
791 applyAllOptions(true); // apply priority options
792 applyAllOptions(false); // apply non-priority options
793
794 // One of the Scan Resolution parameters should always exist
795 KScanOption *xres = getOption(SANE_NAME_SCAN_X_RESOLUTION, false);
796 if (xres==nullptr) xres = getOption(SANE_NAME_SCAN_RESOLUTION, false);
797 if (xres!=nullptr)
798 {
799 xres->get(&mCurrScanResolutionX);
800
801 KScanOption *yres = getOption(SANE_NAME_SCAN_Y_RESOLUTION, false);
802 if (yres!=nullptr) yres->get(&mCurrScanResolutionY);
803 else mCurrScanResolutionY = mCurrScanResolutionX;
804 }
805
806 return (acquireData(false)); // perform the scan
807 }
808 else // virtual scan from image file
809 {
810 QFileInfo file(filename);
811 if (!file.exists())
812 {
813 //qDebug() << "virtual file" << filename << "does not exist";
814 return (KScanDevice::ParamError);
815 }
816
817 QImage img(filename);
818 if (img.isNull())
819 {
820 //qDebug() << "virtual file" << filename << "could not load";
821 return (KScanDevice::ParamError);
822 }
823
824 ImageMetaInfo info;
825 info.setXResolution(img.dotsPerMeterX()); // TODO: *2.54/100
826 info.setYResolution(img.dotsPerMeterY()); // TODO: *2.54/100
827 info.setScannerName(QFile::encodeName(filename));
828 emit sigNewImage(&img, &info);
829 return (KScanDevice::Ok);
830 }
831 }
832
833
834 #ifdef DEBUG_PARAMS
dumpParams(const QString & msg,const SANE_Parameters * p)835 static void dumpParams(const QString &msg, const SANE_Parameters *p)
836 {
837 QByteArray formatName("UNKNOWN");
838 switch (p->format)
839 {
840 case SANE_FRAME_GRAY: formatName = "GREY"; break;
841 case SANE_FRAME_RGB: formatName = "RGB"; break;
842 case SANE_FRAME_RED: formatName = "RED"; break;
843 case SANE_FRAME_GREEN: formatName = "GREEN"; break;
844 case SANE_FRAME_BLUE: formatName = "BLUE"; break;
845 }
846
847 qDebug() << msg.toLatin1().constData();
848 qDebug() << " format: " << p->format << "=" << formatName.constData();
849 qDebug() << " last_frame: " << p->last_frame;
850 qDebug() << " lines: " << p->lines;
851 qDebug() << " depth: " << p->depth;
852 qDebug() << " pixels_per_line: " << p->pixels_per_line;
853 qDebug() << " bytes_per_line: " << p->bytes_per_line;
854 }
855 #endif // DEBUG_PARAMS
856
857
acquireData(bool isPreview)858 KScanDevice::Status KScanDevice::acquireData(bool isPreview)
859 {
860 KScanDevice::Status stat = KScanDevice::Ok;
861 int frames = 0;
862
863 #ifdef DEBUG_OPTIONS
864 showOptions(); // dump the current noptions
865 #endif
866
867 mScanningPreview = isPreview;
868 mScanningState = KScanDevice::ScanStarting;
869 mBytesRead = 0;
870 mBlocksRead = 0;
871
872 ScanGlobal::self()->setScanDevice(this); // for possible authentication
873
874 if (mImageInfo!=nullptr) delete mImageInfo; // start with this clean
875 mImageInfo = nullptr;
876
877 if (!isPreview) // scanning to eventually save
878 {
879 mImageInfo = new ImageMetaInfo; // create for image information
880
881 mSaneStatus = sane_get_parameters(mScannerHandle, &mSaneParameters);
882 if (mSaneStatus==SANE_STATUS_GOOD) // get pre-scan parameters
883 {
884 #ifdef DEBUG_PARAMS
885 dumpParams("Before scan:", &mSaneParameters);
886 #endif // DEBUG_PARAMS
887
888 if (mSaneParameters.lines>=1 && mSaneParameters.pixels_per_line>0)
889 { // check for a plausible image
890 ImageMetaInfo::ImageType fmt = getImageFormat(&mSaneParameters);
891 if (fmt==ImageMetaInfo::Unknown) // find format it will have
892 { // scan format not recognised?
893 stat = KScanDevice::ParamError; // no point starting scan
894 emit sigScanFinished(stat); // scan is now finished
895 return (stat);
896 }
897
898 mImageInfo->setImageType(fmt); // save result for later
899 }
900 }
901 }
902
903 // Tell the application that scanning is about to start.
904 emit sigScanStart(mImageInfo);
905
906 // If the image information was available, the application may have
907 // prompted for a filename. If the user cancelled that, it will have
908 // called our slotStopScanning() which set mScanningState to
909 // KScanDevice::ScanStopNow. If that is the case, then finish here.
910 if (mScanningState==KScanDevice::ScanStopNow)
911 { // user cancelled save dialogue
912 //qDebug() << "user cancelled before start";
913 stat = KScanDevice::Cancelled;
914 emit sigScanFinished(stat);
915 return (stat);
916 }
917
918 while (true) // loop while frames available
919 {
920 QApplication::setOverrideCursor(Qt::WaitCursor);
921 // potential lengthy operation
922 mSaneStatus = sane_start(mScannerHandle);
923 if (mSaneStatus==SANE_STATUS_ACCESS_DENIED) // authentication failed?
924 {
925 //qDebug() << "retrying authentication";
926 clearSavedAuth(); // clear any saved password
927 mSaneStatus = sane_start(mScannerHandle); // try again once more
928 }
929
930 if (mSaneStatus==SANE_STATUS_GOOD)
931 {
932 mSaneStatus = sane_get_parameters(mScannerHandle, &mSaneParameters);
933 if (mSaneStatus==SANE_STATUS_GOOD)
934 {
935 #ifdef DEBUG_PARAMS
936 dumpParams(QString("For frame %1:").arg(frames+1), &mSaneParameters);
937 #endif // DEBUG_PARAMS
938
939 // TODO: implement "Hand Scanner" support
940 if (mSaneParameters.lines<1)
941 {
942 //qDebug() << "Hand Scanner not supported";
943 stat = KScanDevice::NotSupported;
944 }
945 else if (mSaneParameters.pixels_per_line==0)
946 {
947 //qDebug() << "Nothing to acquire!";
948 stat = KScanDevice::EmptyPic;
949 }
950 }
951 else
952 {
953 stat = KScanDevice::OpenDevice;
954 //qDebug() << "sane_get_parameters() error" << lastSaneErrorMessage();
955 }
956 }
957 else
958 {
959 stat = KScanDevice::OpenDevice;
960 //qDebug() << "sane_start() error" << lastSaneErrorMessage();
961 }
962 QApplication::restoreOverrideCursor();
963
964 if (stat==KScanDevice::Ok && mScanningState==KScanDevice::ScanStarting)
965 { // first time through loop
966 // Create image to receive scan, based on real SANE parameters
967 stat = createNewImage(&mSaneParameters);
968
969 // Create/reinitialise buffer for scanning one line
970 if (stat==KScanDevice::Ok)
971 {
972 if (mScanBuf!=nullptr) delete [] mScanBuf;
973 mScanBuf = new SANE_Byte[mSaneParameters.bytes_per_line+4];
974 if (mScanBuf==nullptr) stat = KScanDevice::NoMemory;
975 } // can this ever happen?
976
977 if (stat==KScanDevice::Ok)
978 {
979 int fd = 0;
980 // Don't assume that sane_get_select_fd() will succeed even if
981 // sane_set_io_mode() successfully sets I/O mode to noblocking -
982 // bug 159300
983 if (sane_set_io_mode(mScannerHandle, SANE_TRUE)==SANE_STATUS_GOOD)
984 {
985 if (sane_get_select_fd(mScannerHandle, &fd)==SANE_STATUS_GOOD)
986 {
987 //qDebug() << "using read socket notifier";
988 mSocketNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
989 connect(mSocketNotifier, &QSocketNotifier::activated, this, &KScanDevice::doProcessABlock);
990 }
991 //else qDebug() << "not using socket notifier (sane_get_select_fd() failed)";
992 }
993 //else qDebug() << "not using socket notifier (sane_set_io_mode() failed)";
994 }
995 }
996
997 if (stat!=KScanDevice::Ok) // some problem getting started
998 {
999 // Scanning could not start - give up now
1000 //qDebug() << "Scanning failed to start, status" << stat;
1001 emit sigScanFinished(stat);
1002 return (stat);
1003 }
1004
1005 if (mScanningState==KScanDevice::ScanStarting) // first time through loop
1006 {
1007 QApplication::setOverrideCursor(Qt::BusyCursor);
1008 emit sigAcquireStart();
1009 }
1010
1011 emit sigScanProgress(0); // signal the progress dialog
1012 qApp->processEvents(); // update the progress window
1013
1014 mPixelX = 0;
1015 mPixelY = 0;
1016 mBytesUsed = 0;
1017
1018 // Set internal status to indicate scanning in progress.
1019 // This status might be changed, e.g. by pressing Stop on a
1020 // GUI dialog displayed during scanning.
1021 mScanningState = KScanDevice::ScanInProgress;
1022
1023 // As originally coded, if using the socket notifier
1024 // sane_get_parameters() was only called once at the beginning of
1025 // the scan - just after sane_start() above. If not using the socket
1026 // notifier on the other hand, sane_get_parameters() was called after
1027 // each doProcessABlock() in the loop below.
1028 //
1029 // According to the SANE documentation text, sane_get_parameters()
1030 // needs to be called once after sane_start() to get the exact
1031 // parameters, but not necessarily in the reading loop that just
1032 // needs to call sane_read() repeatedly. The diagram above, though,
1033 // seems to imply that sane_get_parameters() should be done in the
1034 // reading loop.
1035 //
1036 // Doing the sane_get_parameters() just once seems to work with all
1037 // of the scanners that I have available to test, both using the
1038 // socket notifier and not. So that is what we now do, in this
1039 // much simpler loop.
1040
1041 while (true) // loop for one scanned frame
1042 {
1043 if (mSocketNotifier!=nullptr) // using the socket notifier
1044 {
1045 qApp->processEvents(); // let it fire away
1046 }
1047 else // not using socket notifier
1048 {
1049 doProcessABlock(); // may block while reading
1050 }
1051 // exit loop when frame done
1052 if (mScanningState==KScanDevice::ScanIdle ||
1053 mScanningState==KScanDevice::ScanStopNow ||
1054 mScanningState==KScanDevice::ScanNextFrame) break;
1055 }
1056
1057 ++frames; // count up this frame
1058 // more frames to do
1059 if (mScanningState==KScanDevice::ScanNextFrame) continue;
1060 break; // scan done, exit loop
1061 }
1062
1063 if (mScanningState==KScanDevice::ScanStopNow)
1064 {
1065 stat = KScanDevice::Cancelled;
1066 }
1067 else
1068 {
1069 if (mSaneStatus!=SANE_STATUS_GOOD && mSaneStatus!=SANE_STATUS_EOF)
1070 {
1071 stat = KScanDevice::ScanError;
1072 }
1073 }
1074
1075 //qDebug() << "Scan read" << mBytesRead << "bytes in"
1076 //<< mBlocksRead << "blocks," << frames << "frames - status" << stat;
1077
1078 emit sigScanFinished(stat); // scan is now finished
1079 return (stat);
1080 }
1081
1082
1083 /* This function calls at least sane_read and converts the read data from the scanner
1084 * to the qimage.
1085 * The function needs:
1086 * QImage img valid
1087 * the data-buffer set to a appropriate size
1088 **/
1089
1090 // TODO: probably needs to be extended for 16-bit scanner support
1091
doProcessABlock()1092 void KScanDevice::doProcessABlock()
1093 {
1094 int val,i;
1095 QRgb col, newCol;
1096
1097 SANE_Byte *rptr = nullptr;
1098 SANE_Int bytes_read = 0;
1099 int chan = 0;
1100 mSaneStatus = SANE_STATUS_GOOD;
1101 uchar eight_pix = 0;
1102
1103 if (mScanningState==KScanDevice::ScanIdle) return; // scan finished, no more to do
1104 // block notifications while working
1105 if (mSocketNotifier!=nullptr) mSocketNotifier->setEnabled(false);
1106
1107 while (true)
1108 {
1109 mSaneStatus = sane_read(mScannerHandle,
1110 (mScanBuf+mBytesUsed),
1111 mSaneParameters.bytes_per_line,
1112 &bytes_read);
1113 if (mSaneStatus!=SANE_STATUS_GOOD)
1114 {
1115 if (mSaneStatus!=SANE_STATUS_EOF) // this is OK, just stop
1116 { // any other error
1117 //qDebug() << "sane_read() error" << lastSaneErrorMessage()
1118 //<< "bytes read" << bytes_read;
1119 }
1120 break;
1121 }
1122
1123 if (bytes_read<1) break; // no data, finish loop
1124
1125 ++mBlocksRead;
1126 mBytesRead += bytes_read;
1127 // qDebug( "Bytes read: %d, bytes written: %d", bytes_read, mBytesUsed );
1128
1129 int red = 0;
1130 int green = 0;
1131 int blue = 0;
1132
1133 rptr = mScanBuf; // start of scan data
1134 switch (mSaneParameters.format)
1135 {
1136 case SANE_FRAME_RGB:
1137 if (mSaneParameters.lines<1) break;
1138 bytes_read += mBytesUsed; // die �bergebliebenen Bytes dazu
1139 mBytesUsed = bytes_read % 3;
1140
1141 for (val = 0; val<((bytes_read-mBytesUsed)/3); val++)
1142 {
1143 red = *rptr++;
1144 green = *rptr++;
1145 blue = *rptr++;
1146
1147 if (mPixelX>=mSaneParameters.pixels_per_line)
1148 { // reached end of a row
1149 mPixelX = 0;
1150 mPixelY++;
1151 }
1152 if (mPixelY<mScanImage->height()) // within image height
1153 {
1154 mScanImage->setPixel(mPixelX, mPixelY, qRgb(red, green, blue));
1155 }
1156 mPixelX++;
1157 }
1158
1159 for (val = 0; val<mBytesUsed; val++) // Copy the remaining bytes down
1160 { // to the beginning of the block
1161 *(mScanBuf+val) = *rptr++;
1162 }
1163 break;
1164
1165 case SANE_FRAME_GRAY:
1166 for (val = 0; val<bytes_read ; val++)
1167 {
1168 if (mPixelY>=mSaneParameters.lines) break;
1169 if (mSaneParameters.depth==8) // Greyscale
1170 {
1171 if (mPixelX>=mSaneParameters.pixels_per_line)
1172 { // reached end of a row
1173 mPixelX = 0;
1174 mPixelY++;
1175 }
1176 mScanImage->setPixel(mPixelX, mPixelY, *rptr++);
1177 mPixelX++;
1178 }
1179 else // Lineart (bitmap)
1180 { // needs to be converted to byte
1181 eight_pix = *rptr++;
1182 for (i = 0; i<8; i++)
1183 {
1184 if (mPixelY<mSaneParameters.lines)
1185 {
1186 chan = (eight_pix & 0x80)>0 ? 0 : 1;
1187 eight_pix = eight_pix << 1;
1188 mScanImage->setPixel(mPixelX, mPixelY, chan);
1189 mPixelX++;
1190 if( mPixelX>=mSaneParameters.pixels_per_line)
1191 {
1192 mPixelX = 0;
1193 mPixelY++;
1194 break;
1195 }
1196 }
1197 }
1198 }
1199 }
1200 break;
1201
1202 case SANE_FRAME_RED:
1203 case SANE_FRAME_GREEN:
1204 case SANE_FRAME_BLUE:
1205 for (val = 0; val<bytes_read ; val++)
1206 {
1207 if (mPixelX>=mSaneParameters.pixels_per_line)
1208 { // reached end of a row
1209 mPixelX = 0;
1210 mPixelY++;
1211 }
1212
1213 if (mPixelY<mSaneParameters.lines)
1214 {
1215 col = mScanImage->pixel(mPixelX, mPixelY);
1216
1217 red = qRed(col);
1218 green = qGreen(col);
1219 blue = qBlue(col);
1220 chan = *rptr++;
1221
1222 switch (mSaneParameters.format)
1223 {
1224 case SANE_FRAME_RED: newCol = qRgba(chan, green, blue, 0xFF);
1225 break;
1226
1227 case SANE_FRAME_GREEN: newCol = qRgba(red, chan, blue, 0xFF);
1228 break;
1229
1230 case SANE_FRAME_BLUE: newCol = qRgba(red, green, chan, 0xFF);
1231 break;
1232
1233 default: newCol = qRgba(0xFF, 0xFF, 0xFF, 0xFF);
1234 break;
1235 }
1236 mScanImage->setPixel(mPixelX, mPixelY, newCol);
1237 mPixelX++;
1238 }
1239 }
1240 break;
1241
1242 default: //qDebug() << "Undefined SANE format" << mSaneParameters.format;
1243 break;
1244 } // switch of scan format
1245
1246 if ((mSaneParameters.lines>0) && ((mSaneParameters.lines*mPixelY)>0))
1247 {
1248 int progress = (int)(((double)MAX_PROGRESS)/mSaneParameters.lines*mPixelY);
1249 if (progress<MAX_PROGRESS) emit sigScanProgress(progress);
1250 }
1251
1252 // cannot get here, bytes_read and EOF tested above
1253 //if( bytes_read == 0 || mSaneStatus == SANE_STATUS_EOF )
1254 //{
1255 // //qDebug() << "mSaneStatus not OK:" << sane_stat;
1256 // break;
1257 //}
1258
1259 if (mScanningState==KScanDevice::ScanStopNow)
1260 {
1261 /* mScanningState is set to ScanStopNow due to hitting slStopScanning */
1262 /* Mostly that one is fired by the STOP-Button in the progress dialog. */
1263
1264 /* This is also hit after the normal finish of the scan. Most probably,
1265 * the QSocketnotifier fires for a few times after the scan has been
1266 * cancelled. Does it matter ? To see it, just uncomment the qDebug msg.
1267 */
1268 //qDebug() << "Stopping the scan progress";
1269 // mScanningState = KScanDevice::ScanIdle;
1270 break;
1271 }
1272 } // end of main loop
1273
1274 // Here when scanning is finished or has had an error
1275 if (mSaneStatus==SANE_STATUS_EOF) // end of scan pass
1276 {
1277 if (mSaneParameters.last_frame) // end of scanning run
1278 {
1279 /** Everything is okay, the picture is ready **/
1280 //qDebug() << "Last frame reached, scan successful";
1281 mScanningState = KScanDevice::ScanIdle;
1282 }
1283 else
1284 {
1285 /** EOF und nicht letzter Frame -> Parameter neu belegen und neu starten **/
1286 mScanningState = KScanDevice::ScanNextFrame;
1287 //qDebug() << "EOF, but another frame to scan";
1288 }
1289 }
1290 else if (mSaneStatus!=SANE_STATUS_GOOD)
1291 {
1292 mScanningState = KScanDevice::ScanIdle;
1293 //qDebug() << "Scan error or cancelled, status" << mSaneStatus;
1294 }
1295
1296 if (mSocketNotifier!=nullptr) mSocketNotifier->setEnabled(true);
1297 }
1298
1299
slotScanFinished(KScanDevice::Status status)1300 void KScanDevice::slotScanFinished(KScanDevice::Status status)
1301 {
1302 if (mSocketNotifier!=nullptr) // clean up if in use
1303 {
1304 delete mSocketNotifier;
1305 mSocketNotifier = nullptr;
1306 }
1307
1308 emit sigScanProgress(MAX_PROGRESS);
1309 QApplication::restoreOverrideCursor();
1310
1311 //qDebug() << "status" << status;
1312
1313 if (mScanBuf!=nullptr)
1314 {
1315 delete[] mScanBuf;
1316 mScanBuf = nullptr;
1317 }
1318
1319 if (status==KScanDevice::Ok && mScanImage!=nullptr)
1320 {
1321 ImageMetaInfo info;
1322 info.setXResolution(mCurrScanResolutionX);
1323 info.setYResolution(mCurrScanResolutionY);
1324 info.setScannerName(mScannerName);
1325
1326 // put the resolution also into the image itself
1327 // TODO: use qRound()
1328 mScanImage->setDotsPerMeterX(static_cast<int>(mCurrScanResolutionX / 0.0254 + 0.5));
1329 mScanImage->setDotsPerMeterY(static_cast<int>(mCurrScanResolutionY / 0.0254 + 0.5));
1330
1331 if (mScanningPreview)
1332 {
1333 savePreviewImage(*mScanImage);
1334 emit sigNewPreview(mScanImage, &info);
1335
1336 loadOptionSet(mSavedOptions); // restore original scan settings
1337 }
1338 else
1339 {
1340 emit sigNewImage(mScanImage, &info);
1341 }
1342 }
1343
1344 sane_cancel(mScannerHandle);
1345
1346 /* This follows after sending the signal */
1347 if (mScanImage!=nullptr)
1348 {
1349 delete mScanImage;
1350 mScanImage = nullptr;
1351 }
1352
1353 mScanningState = KScanDevice::ScanIdle;
1354 }
1355
1356
1357 // Configuration
1358 // -------------
1359
saveStartupConfig()1360 void KScanDevice::saveStartupConfig()
1361 {
1362 if (mScannerName.isNull()) return; // do not save for no scanner
1363
1364 KScanOptSet optSet(KScanOptSet::startupSetName());
1365 getCurrentOptions(&optSet);
1366 optSet.saveConfig(mScannerName, i18n("Default startup configuration"));
1367 }
1368
1369
loadOptionSetInternal(const KScanOptSet * optSet,bool prio)1370 void KScanDevice::loadOptionSetInternal(const KScanOptSet *optSet, bool prio)
1371 {
1372 for (KScanOptSet::const_iterator it = optSet->constBegin();
1373 it!=optSet->constEnd(); ++it)
1374 {
1375 const QByteArray name = it.key();
1376 if (!optionExists(name)) continue; // only for options that exist
1377
1378 KScanOption *so = getOption(name, false);
1379 if (so==nullptr) continue; // we don't have this option
1380 if (so->isGroup()) continue; // nothing to do here
1381 if (so->isPriorityOption() ^ prio) continue; // check whether requested priority
1382
1383 so->set(it.value());
1384 if (so->isInitialised() && so->isSoftwareSettable() && so->isActive()) so->apply();
1385 }
1386 }
1387
1388
loadOptionSet(const KScanOptSet * optSet)1389 void KScanDevice::loadOptionSet(const KScanOptSet *optSet)
1390 {
1391 if (optSet==nullptr) return;
1392 //qDebug() << "Loading set" << optSet->getSetName() << "with" << optSet->count() << "options";
1393
1394 loadOptionSetInternal(optSet, true);
1395 loadOptionSetInternal(optSet, false);
1396 }
1397
1398
1399 // Retrieve the current options from the scanner, i.e. all of those that
1400 // have an associated GUI element and also any others (e.g. the TL_X and
1401 // other scan area settings) that have been apply()'ed but do not have
1402 // a GUI.
1403
getCurrentOptions(KScanOptSet * optSet) const1404 void KScanDevice::getCurrentOptions(KScanOptSet *optSet) const
1405 {
1406 if (optSet==nullptr) return;
1407
1408 for (OptionHash::const_iterator it = mCreatedOptions.constBegin();
1409 it!=mCreatedOptions.constEnd(); ++it)
1410 {
1411 KScanOption *so = it.value();
1412 if (!so->isReadable()) continue;
1413
1414 if (so->isGuiElement() || so->isApplied())
1415 {
1416 if (so->isActive()) optSet->backupOption(so);
1417 so->setApplied(false);
1418 }
1419 }
1420 }
1421
1422
configGroup(const QString & groupName)1423 KConfigGroup KScanDevice::configGroup(const QString &groupName)
1424 {
1425 Q_ASSERT(!groupName.isEmpty());
1426 return (ScanSettings::self()->config()->group(groupName));
1427 }
1428
1429 // SANE Authentication
1430 // -------------------
1431 //
1432 // According to the SANE documentation, this may be requested for any use of
1433 // sane_open(), sane_control_option() or sane_start() on a scanner device
1434 // that requires authentication.
1435 //
1436 // The only uses of sane_open() and sane_start() are here in this file, and
1437 // they set the current scanner using setScanDevice() before performing the
1438 // SANE operation.
1439 //
1440 // This does not happen for all uses of sane_control_option(), either here or
1441 // in KScanOption, so there is a slight possibility that if authentication is
1442 // needed for those (and has not been previously requested by sane_open() or
1443 // sane_start()) then it will use the wrong scanner device or will not prompt
1444 // at all. However, Kooka only supports one scanner open at a time, and does
1445 // sane_open() before any use of sane_control_option(). So hopefully this
1446 // will not be a problem.
1447
authenticate(QByteArray * retuser,QByteArray * retpass)1448 bool KScanDevice::authenticate(QByteArray *retuser, QByteArray *retpass)
1449 {
1450 //qDebug() << "for" << mScannerName;
1451
1452 // TODO: use KWallet for username/password?
1453 KConfigGroup grp = configGroup(mScannerName);
1454 QByteArray user = QByteArray::fromBase64(grp.readEntry("user", QString()).toLocal8Bit());
1455 QByteArray pass = QByteArray::fromBase64(grp.readEntry("pass", QString()).toLocal8Bit());
1456
1457 if (!user.isEmpty() && !pass.isEmpty())
1458 {
1459 //qDebug() << "have saved username/password";
1460 }
1461 else
1462 {
1463 //qDebug() << "asking for username/password";
1464
1465 KPasswordDialog dlg(nullptr, KPasswordDialog::ShowKeepPassword|KPasswordDialog::ShowUsernameLine);
1466 dlg.setPrompt(xi18nc("@info", "The scanner<nl/><emphasis strong=\"1\">%1</emphasis><nl/>requires authentication.", mScannerName.constData()));
1467 dlg.setWindowTitle(i18n("Scanner Authentication"));
1468
1469 if (!user.isEmpty()) dlg.setUsername(user);
1470 if (!pass.isEmpty()) dlg.setPassword(pass);
1471
1472 if (!dlg.exec()) return (false);
1473
1474 user = dlg.username().toLocal8Bit();
1475 pass = dlg.password().toLocal8Bit();
1476 if (dlg.keepPassword())
1477 {
1478 grp.writeEntry("user", user.toBase64());
1479 grp.writeEntry("pass", pass.toBase64());
1480 }
1481 }
1482
1483 *retuser = user;
1484 *retpass = pass;
1485 return (true);
1486 }
1487
1488
clearSavedAuth()1489 void KScanDevice::clearSavedAuth()
1490 {
1491 KConfigGroup grp = configGroup(mScannerName);
1492 grp.deleteEntry("user");
1493 grp.deleteEntry("pass");
1494 grp.sync();
1495 }
1496
1497
1498 // Error reporting
1499 // ---------------
1500
lastSaneErrorMessage() const1501 QString KScanDevice::lastSaneErrorMessage() const
1502 {
1503 return (sane_strstatus(mSaneStatus));
1504 }
1505
1506
statusMessage(KScanDevice::Status stat)1507 QString KScanDevice::statusMessage(KScanDevice::Status stat)
1508 {
1509 switch (stat)
1510 {
1511 case KScanDevice::Ok: return (i18n("OK")); // shouldn't be reported
1512 case KScanDevice::NoDevice: return (i18n("No device")); // never during scanning
1513 case KScanDevice::ParamError: return (i18n("Bad parameter"));
1514 case KScanDevice::OpenDevice: return (i18n("Cannot open device"));
1515 case KScanDevice::ControlError: return (i18n("sane_control_option() failed"));
1516 case KScanDevice::ScanError: return (i18n("Error while scanning"));
1517 case KScanDevice::EmptyPic: return (i18n("Empty picture"));
1518 case KScanDevice::NoMemory: return (i18n("Out of memory"));
1519 case KScanDevice::Reload: return (i18n("Needs reload")); // never during scanning
1520 case KScanDevice::Cancelled: return (i18n("Cancelled")); // shouldn't be reported
1521 case KScanDevice::OptionNotActive: return (i18n("Not active")); // never during scanning
1522 case KScanDevice::NotSupported: return (i18n("Not supported"));
1523 default: return (i18n("Unknown status %1", stat));
1524 }
1525 }
1526