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