File indexing completed on 2024-04-14 14:36:04

0001 /* This file is part of the KDE Project
0002    Copyright (C) 2000 Klaas Freitag <freitag@suse.de>  
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "kscanoption.h"
0021 
0022 #include <qwidget.h>
0023 #include <qobject.h>
0024 #include <qcheckbox.h>
0025 #include <qlabel.h>
0026 #include <qregexp.h>
0027 
0028 #include <klocalizedstring.h>
0029 #include <kacceleratormanager.h>
0030 #include <ksqueezedtextlabel.h>
0031 
0032 extern "C" {
0033 #include <sane/sane.h>
0034 #include <sane/saneopts.h>
0035 }
0036 
0037 #include "kgammatable.h"
0038 #include "kscandevice.h"
0039 #include "kscancontrols.h"
0040 #include "kscanoptset.h"
0041 #include "libkookascan_logging.h"
0042 
0043 
0044 //  Debugging options
0045 #undef DEBUG_MEM
0046 #undef DEBUG_GETSET
0047 #define DEBUG_APPLY
0048 #undef DEBUG_RELOAD
0049 
0050 
0051 //  This defines the possible resolutions that will be shown by the combo.
0052 //  Only resolutions from this list falling within the scanner's allowed range
0053 //  will be included.
0054 static const int resList[] = { 50, 75, 100, 150, 200, 300, 600, 900, 1200, 1800, 2400, 4800, 9600, 0 };
0055 
0056 
0057 KScanOption::KScanOption(const QByteArray &name, KScanDevice *scandev)
0058 {
0059     mScanDevice = scandev;
0060 
0061     if (!initOption(name))
0062     {
0063         qCWarning(LIBKOOKASCAN_LOG) << "initOption for" << name << "failed!";
0064         return;
0065     }
0066 
0067     if (!mIsReadable) return;               // no value to read
0068     if (mBuffer.isNull()) return;           // no buffer for value
0069 
0070     // read initial value from the scanner
0071     SANE_Status sanestat = sane_control_option(mScanDevice->scannerHandle(),
0072                                                 mIndex,
0073                                                 SANE_ACTION_GET_VALUE,
0074                                                 mBuffer.data(), nullptr);
0075     if (sanestat==SANE_STATUS_GOOD) mBufferClean = false;
0076 }
0077 
0078 
0079 static bool shouldBePriorityOption(const QByteArray &name)
0080 {
0081     return (name=="source");
0082 }
0083 
0084 
0085 bool KScanOption::initOption(const QByteArray &name)
0086 {
0087     mDesc = nullptr;
0088     mControl = nullptr;
0089     mIsGroup = false;
0090     mIsReadable = true;
0091     mIsPriority = shouldBePriorityOption(name);
0092     mWidgetType = KScanOption::Invalid;
0093 
0094     if (name.isEmpty()) return (false);
0095     mName = name;
0096 
0097     // Look up the option (which must already exist in the map) by name.
0098     //
0099     // The default-constructed index is 0 for an invalid option, this is OK
0100     // because although a SANE option with that index exists it is never
0101     // requested by name.
0102     mIndex = mScanDevice->getOptionIndex(mName);
0103     if (mIndex<=0)
0104     {
0105         qCWarning(LIBKOOKASCAN_LOG) << "no option descriptor for" << mName;
0106         return (false);
0107     }
0108 
0109     mDesc = sane_get_option_descriptor(mScanDevice->scannerHandle(), mIndex);
0110     if (mDesc==nullptr) return (false);
0111 
0112     mBuffer.resize(0);
0113     mBufferClean = true;
0114     mApplied = false;
0115 
0116     if (mDesc->type==SANE_TYPE_GROUP) mIsGroup = true;
0117     if (mIsGroup || mDesc->type==SANE_TYPE_BUTTON) mIsReadable = false;
0118     if (!(mDesc->cap & SANE_CAP_SOFT_DETECT)) mIsReadable = false;
0119 
0120     mGammaTable = nullptr;              // for recording gamma values
0121 
0122     mWidgetType = resolveWidgetType();          // work out the type of widget
0123     allocForDesc();                 // allocate initial buffer
0124     return (true);
0125 }
0126 
0127 
0128 KScanOption::~KScanOption()
0129 {
0130 #ifdef DEBUG_MEM
0131     if (!mBuffer.isNull()) qCDebug(LIBKOOKASCAN_LOG) << "Freeing" << mBuffer.size() << "bytes for" << mName;
0132 #endif
0133     // TODO: need to delete mControl here?
0134 }
0135 
0136 
0137 void KScanOption::slotWidgetChange(const QString &t)
0138 {
0139     set(t.toUtf8());
0140     emit guiChange(this);
0141 }
0142 
0143 
0144 void KScanOption::slotWidgetChange(int i)
0145 {
0146     set(i);
0147     emit guiChange(this);
0148 }
0149 
0150 
0151 void KScanOption::slotWidgetChange()
0152 {
0153     set(1);
0154     emit guiChange(this);
0155 }
0156 
0157 
0158 void KScanOption::updateList()
0159 {
0160     KScanCombo *combo = qobject_cast<KScanCombo *>(mControl);
0161     if (combo==nullptr) return;
0162 
0163     QList<QByteArray> list = getList();
0164     combo->setList(list);
0165 }
0166 
0167 
0168 /* called on a widget change, if a widget was created.
0169  */
0170 void KScanOption::redrawWidget()
0171 {
0172     if (!isValid() || !isReadable() || mControl==nullptr || mBuffer.isNull()) return;
0173 
0174     KScanControl::ControlType type = mControl->type();
0175     if (type==KScanControl::Number)         // numeric control
0176     {
0177         int i = 0;
0178         if (get(&i)) mControl->setValue(i);
0179     }
0180     else if (type==KScanControl::Text)          // text control
0181     {
0182         mControl->setText(get());
0183     }
0184 }
0185 
0186 
0187 /* Get and update the current setting from the scanner */
0188 void KScanOption::reload()
0189 {
0190     if (mControl!=nullptr)
0191     {
0192         if (isGroup())
0193         {
0194             mControl->setEnabled(true);         // always enabled
0195             return;                 // no more to do
0196         }
0197 
0198         if (!isActive())
0199         {
0200 #ifdef DEBUG_RELOAD
0201             qCDebug(LIBKOOKASCAN_LOG) << "not active" << mName;
0202 #endif
0203             mControl->setEnabled(false);
0204         }
0205         else if (!isSoftwareSettable())
0206         {
0207 #ifdef DEBUG_RELOAD
0208             qCDebug(LIBKOOKASCAN_LOG) << "not settable" << mName;
0209 #endif
0210             mControl->setEnabled(false);
0211         }
0212         else mControl->setEnabled(true);
0213     }
0214 
0215     if (!isReadable())
0216     {
0217 #ifdef DEBUG_RELOAD
0218         qCDebug(LIBKOOKASCAN_LOG) << "not readable" << mName;
0219 #endif
0220         return;
0221     }
0222 
0223     if (mBuffer.isNull())               // first get mem if needed
0224     {
0225         qCDebug(LIBKOOKASCAN_LOG) << "need to allocate now";
0226         allocForDesc();                 // allocate the buffer now
0227     }
0228 
0229     if (!isActive()) return;
0230 
0231     if (mDesc->size>mBuffer.size())
0232     {
0233         qCWarning(LIBKOOKASCAN_LOG) << "buffer too small for" << mName << "type" << mDesc->type
0234                                     << "size" << mBuffer.size() << "need" << mDesc->size;
0235         allocForDesc();                 // grow the buffer
0236     }
0237 
0238     // Starting with SANE 1.0.20, the 'test' device needs this dummy call of
0239     // sane_get_option_descriptor() before any use of sane_control_option(),
0240     // otherwise the latter fails with a SANE_STATUS_INVAL.  From the master
0241     // repository at http://anonscm.debian.org/gitweb/?p=sane/sane-backends.git:
0242     //
0243     //   author  m. allan noah <kitno455@gmail.com>
0244     //   Thu, 26 Jun 2008 13:14:23 +0000 (13:14 +0000)
0245     //   commit 8733651c4b07ac6ccbcee0d39eccca0c08057729
0246     //   test backend checks for options that have not been loaded before being controlled
0247     //
0248     // I'm hoping that this is not in general an expensive operation - it certainly
0249     // is not so for the 'test' device and a sample of others - so it should not be
0250     // necessary to conditionalise it for that device only.
0251     const SANE_Option_Descriptor *desc = sane_get_option_descriptor(mScanDevice->scannerHandle(), mIndex);
0252     if (desc==nullptr) return;              // should never happen
0253 
0254     SANE_Status sanestat = sane_control_option(mScanDevice->scannerHandle(),
0255                                                 mIndex,
0256                                                 SANE_ACTION_GET_VALUE,
0257                                                 mBuffer.data(), nullptr);
0258     if (sanestat!=SANE_STATUS_GOOD)
0259     {
0260         qCWarning(LIBKOOKASCAN_LOG) << "Can't get value for" << mName << "status" << sane_strstatus(sanestat);
0261         return;
0262     }
0263 
0264     updateList();                   // if range changed, update GUI
0265 
0266 #ifdef DEBUG_RELOAD
0267     qCDebug(LIBKOOKASCAN_LOG) << "reloaded" << mName;
0268 #endif
0269     mBufferClean = false;
0270 }
0271 
0272 
0273 bool KScanOption::apply()
0274 {
0275     int sane_result = 0;
0276     bool reload = false;
0277 #ifdef DEBUG_APPLY
0278     QString debug = QString("option '%1'").arg(mName.constData());
0279 #endif // DEBUG_APPLY
0280 
0281     SANE_Status sanestat = SANE_STATUS_GOOD;
0282 
0283     // See comment in reload() above
0284     const SANE_Option_Descriptor *desc = sane_get_option_descriptor(mScanDevice->scannerHandle(), mIndex);
0285     if (desc==nullptr) return (false);          // should never happen
0286 
0287     if (mName==SANE_NAME_PREVIEW || mName==SANE_NAME_SCAN_MODE)
0288     {
0289         sanestat = sane_control_option(mScanDevice->scannerHandle(), mIndex,
0290                                        SANE_ACTION_SET_AUTO, nullptr,
0291                                        &sane_result);
0292         /* No return here, please! Carsten, does it still work than for you? */
0293     }
0294 
0295     if (!isInitialised() || mBuffer.isNull())
0296     {
0297 #ifdef DEBUG_APPLY
0298         debug += " nobuffer";
0299 #endif // DEBUG_APPLY
0300 
0301         if (!isAutoSettable()) goto ret;
0302 
0303 #ifdef DEBUG_APPLY
0304         debug += " auto";
0305 #endif // DEBUG_APPLY
0306         sanestat = sane_control_option(mScanDevice->scannerHandle(), mIndex,
0307                                        SANE_ACTION_SET_AUTO, nullptr,
0308                                        &sane_result);
0309     }
0310     else
0311     {
0312         if (!isActive())
0313         {
0314 #ifdef DEBUG_APPLY
0315             debug += " notactive";
0316 #endif // DEBUG_APPLY
0317             goto ret;
0318         }
0319         else if (!isSoftwareSettable())
0320         {
0321 #ifdef DEBUG_APPLY
0322             debug += " notsettable";
0323 #endif // DEBUG_APPLY
0324             goto ret;
0325         }
0326         else
0327         {
0328             sanestat = sane_control_option(mScanDevice->scannerHandle(), mIndex,
0329                                            SANE_ACTION_SET_VALUE,
0330                                            mBuffer.data(),
0331                                            &sane_result);
0332         }
0333     }
0334 
0335     if (sanestat!=SANE_STATUS_GOOD)
0336     {
0337         qCWarning(LIBKOOKASCAN_LOG) << "apply" << mName << "failed, SANE status" << sane_strstatus(sanestat);
0338         return (false);
0339     }
0340 
0341 #ifdef DEBUG_APPLY
0342     debug += QString(" -> '%1'").arg(get().constData());
0343 #endif // DEBUG_APPLY
0344 
0345     if (sane_result & SANE_INFO_RELOAD_OPTIONS)
0346     {
0347 #ifdef DEBUG_APPLY
0348         debug += " reload";
0349 #endif // DEBUG_APPLY
0350         reload = true;
0351     }
0352 
0353 #ifdef DEBUG_APPLY
0354     if (sane_result & SANE_INFO_INEXACT) debug += " inexact";
0355 #endif // DEBUG_APPLY
0356 
0357     mApplied = true;
0358 ret:
0359 #ifdef DEBUG_APPLY
0360     qCDebug(LIBKOOKASCAN_LOG) << qPrintable(debug);         // no quotes, please
0361 #endif // DEBUG_APPLY
0362     return (reload);
0363 }
0364 
0365 
0366 // The name of the option is checked here to detect options which are
0367 // a resolution or a gamma table, and therefore are to be treated
0368 // specially.  This should hopefully be more reliable then the earlier
0369 // heuristics.
0370 KScanOption::WidgetType KScanOption::resolveWidgetType() const
0371 {
0372     if (!isValid()) return (KScanOption::Invalid);
0373 
0374     KScanOption::WidgetType ret;
0375     switch (mDesc->type)
0376     {
0377 case SANE_TYPE_BOOL:
0378         ret = KScanOption::Bool;
0379         break;
0380 
0381 case SANE_TYPE_INT:
0382 case SANE_TYPE_FIXED:
0383         if (qstrcmp(mDesc->name, SANE_NAME_SCAN_RESOLUTION)==0 ||
0384             qstrcmp(mDesc->name, SANE_NAME_SCAN_X_RESOLUTION)==0 ||
0385             qstrcmp(mDesc->name, SANE_NAME_SCAN_Y_RESOLUTION)==0)
0386         {
0387             ret = KScanOption::Resolution;
0388             if (mDesc->unit!=SANE_UNIT_DPI) qCWarning(LIBKOOKASCAN_LOG) << "expected" << mName << "unit" << mDesc->unit << "to be DPI";
0389         }
0390         else if (qstrcmp(mDesc->name, SANE_NAME_GAMMA_VECTOR)==0 ||
0391                  qstrcmp(mDesc->name, SANE_NAME_GAMMA_VECTOR_R)==0 ||
0392                  qstrcmp(mDesc->name, SANE_NAME_GAMMA_VECTOR_G)==0 ||
0393                  qstrcmp(mDesc->name, SANE_NAME_GAMMA_VECTOR_B)==0)
0394         {
0395             ret = KScanOption::GammaTable;
0396             if (mDesc->size!=sizeof(SANE_Byte)) qCDebug(LIBKOOKASCAN_LOG) << "expected" << mName << "size" << mDesc->size << "to be BYTE";
0397         }
0398         else if (mDesc->constraint_type==SANE_CONSTRAINT_RANGE) ret = KScanOption::Range;
0399         else if (mDesc->constraint_type==SANE_CONSTRAINT_WORD_LIST) ret = KScanOption::StringList;
0400         else if (mDesc->constraint_type==SANE_CONSTRAINT_NONE) ret = KScanOption::SingleValue;
0401         else ret = KScanOption::Invalid;
0402         break;
0403 
0404 case SANE_TYPE_STRING:
0405         if (qstrcmp(mDesc->name, SANE_NAME_FILE)==0) ret = KScanOption::File;
0406         else if (mDesc->constraint_type==SANE_CONSTRAINT_STRING_LIST) ret = KScanOption::StringList;
0407         else ret = KScanOption::String;
0408         break;
0409 
0410 case SANE_TYPE_BUTTON:
0411         ret = KScanOption::Button;
0412         break;
0413 
0414 case SANE_TYPE_GROUP:
0415         ret = KScanOption::Group;
0416         break;
0417 
0418 default:
0419         qCDebug(LIBKOOKASCAN_LOG) << "unsupported SANE type" << mDesc->type;
0420         ret = KScanOption::Invalid;
0421         break;
0422     }
0423 
0424 #ifdef DEBUG_GETSET
0425     qCDebug(LIBKOOKASCAN_LOG) << "for SANE type" << mDesc->type << "returning" << ret;
0426 #endif
0427     return (ret);
0428 }
0429 
0430 
0431 bool KScanOption::set(int val)
0432 {
0433     if (!isValid() || mBuffer.isNull()) return (false);
0434 #ifdef DEBUG_GETSET
0435     qCDebug(LIBKOOKASCAN_LOG) << "Setting" << mName << "to" << val;
0436 #endif
0437 
0438     int word_size;
0439     QVector<SANE_Word> qa;
0440     SANE_Word sw;
0441 
0442     switch (mDesc->type)
0443     {
0444 case SANE_TYPE_BUTTON:                  // Activate a button
0445 case SANE_TYPE_BOOL:                    // Assign a Boolean value
0446         sw = (val ? SANE_TRUE : SANE_FALSE);
0447         mBuffer = QByteArray(((const char *) &sw),sizeof(SANE_Word));
0448         break;
0449 
0450 case SANE_TYPE_INT:                 // Fill the whole buffer with that value
0451     word_size = mDesc->size/sizeof(SANE_Word);
0452     qa.resize(word_size);
0453         sw = static_cast<SANE_Word>(val);
0454     qa.fill(sw);
0455         mBuffer = QByteArray(((const char *) qa.data()),mDesc->size);
0456     break;
0457 
0458 case SANE_TYPE_FIXED:                   // Fill the whole buffer with that value
0459         word_size = mDesc->size/sizeof(SANE_Word);
0460         qa.resize(word_size);
0461         sw = SANE_FIX(static_cast<double>(val));
0462         qa.fill(sw);
0463         mBuffer = QByteArray(((const char *) qa.data()),mDesc->size);
0464         break;
0465 
0466 default:
0467         qCDebug(LIBKOOKASCAN_LOG) << "Can't set" << mName << "with type" << mDesc->type;
0468         return (false);
0469     }
0470 
0471     mBufferClean = false;
0472     return (true);
0473 }
0474 
0475 
0476 bool KScanOption::set(double val)
0477 {
0478     if (!isValid() || mBuffer.isNull()) return (false);
0479 #ifdef DEBUG_GETSET
0480     qCDebug(LIBKOOKASCAN_LOG) << "Setting" << mName << "to" << val;
0481 #endif
0482 
0483     int word_size;
0484     QVector<SANE_Word> qa;
0485     SANE_Word sw;
0486 
0487     switch (mDesc->type)
0488     {
0489 case SANE_TYPE_BOOL:                    // Assign a Boolean value
0490         sw = (val>0 ? SANE_TRUE : SANE_FALSE);
0491         mBuffer = QByteArray(((const char *) &sw),sizeof(SANE_Word));
0492         break;
0493 
0494 case SANE_TYPE_INT:                 // Fill the whole buffer with that value
0495         word_size = mDesc->size/sizeof(SANE_Word);
0496         qa.resize(word_size);
0497         sw = static_cast<SANE_Word>(val);
0498         qa.fill(sw);
0499         mBuffer = QByteArray(((const char *) qa.data()),mDesc->size);
0500         break;
0501 
0502 case SANE_TYPE_FIXED:                   // Fill the whole buffer with that value
0503         word_size = mDesc->size/sizeof(SANE_Word);
0504         qa.resize(word_size);
0505         sw = SANE_FIX(val);
0506         qa.fill(sw);
0507         mBuffer = QByteArray(((const char *) qa.data()),mDesc->size);
0508         break;
0509 
0510 default:
0511         qCDebug(LIBKOOKASCAN_LOG) << "Can't set" << mName << "with type" << mDesc->type;
0512         return (false);
0513    }
0514 
0515     mBufferClean = false;
0516     return (true);
0517 }
0518 
0519 
0520 bool KScanOption::set(const int *val, int size)
0521 {
0522     if (!isValid() || mBuffer.isNull()) return (false);
0523     if (val==nullptr) return (false);
0524 #ifdef DEBUG_GETSET
0525     qCDebug(LIBKOOKASCAN_LOG) << "Setting" << mName << "of size" << size;
0526 #endif
0527 
0528     int offset = 0;
0529     int word_size = mDesc->size/sizeof(SANE_Word);
0530     QVector<SANE_Word> qa(1+word_size);     /* add 1 in case offset is needed */
0531 
0532     switch (mDesc->type)
0533     {
0534 case SANE_TYPE_FIXED:                   // must have already used SANE_FIX()
0535 case SANE_TYPE_INT:
0536         for (int i = 0; i<word_size; i++)
0537         {
0538             if (i<size) qa[offset+i] = (SANE_Word) *(val++);
0539             else qa[offset+i] = (SANE_Word) *val;
0540         }
0541         break;
0542 
0543 default:
0544         qCDebug(LIBKOOKASCAN_LOG) << "Can't set" << mName << "with type" << mDesc->type;
0545         return (false);
0546     }
0547 
0548     int copybyte = mDesc->size;
0549     if (offset) copybyte += sizeof(SANE_Word);
0550 
0551     //qCDebug(LIBKOOKASCAN_LOG) << "Copying" << copybyte << "bytes to options buffer";
0552     mBuffer = QByteArray(((const char *) qa.data()), copybyte);
0553     mBufferClean = false;
0554     return (true);
0555 }
0556 
0557 
0558 bool KScanOption::set(const QByteArray &buf)
0559 {
0560     if (!isValid() || mBuffer.isNull()) return (false);
0561 #ifdef DEBUG_GETSET
0562     qCDebug(LIBKOOKASCAN_LOG) << "Setting" << mName << "to" << buf;
0563 #endif
0564 
0565     int val;
0566     int origSize;
0567     bool ok;
0568 
0569     // Check whether the string value looks like a gamma table specification.
0570     // If it is, then convert it to a gamma table and set that.
0571     KGammaTable gt;
0572     if (gt.setFromString(buf))
0573     {
0574 #ifdef DEBUG_GETSET
0575         qCDebug(LIBKOOKASCAN_LOG) << "is a gamma table";
0576 #endif
0577         return (set(&gt));
0578     }
0579 
0580     /* On String-type the buffer gets malloced in Constructor */
0581     switch (mDesc->type)
0582     {
0583 case SANE_TYPE_STRING:
0584         origSize = mBuffer.size();
0585         mBuffer = QByteArray(buf.data(),(buf.length()+1));
0586         mBuffer.resize(origSize);           // restore original size
0587         break;
0588 
0589 case SANE_TYPE_INT:
0590         val = buf.toInt(&ok);
0591         if (ok) set(&val, 1);
0592         else return (false);
0593         break;
0594 
0595 case SANE_TYPE_FIXED:
0596         val = SANE_FIX(buf.toDouble(&ok));
0597         if (ok) set(&val, 1);
0598         else return (false);
0599         break;
0600 
0601 case SANE_TYPE_BOOL:
0602         set(buf=="true");
0603         break;
0604 
0605 default:
0606         qCDebug(LIBKOOKASCAN_LOG) << "Can't set" << mName << "with type" << mDesc->type;
0607         return (false);
0608     }
0609 
0610     mBufferClean = false;
0611     return (true);
0612 }
0613 
0614 
0615 // The parameter here must be 'const'.
0616 // Otherwise, a call of set() with a 'const KGammaTable *' argument appears
0617 // to be silently resolved to a call of set(int) without warning.
0618 bool KScanOption::set(const KGammaTable *gt)
0619 {
0620     if (!isValid() || mBuffer.isNull()) return (false);
0621 
0622     // Remember the set values
0623     if (mGammaTable!=nullptr) delete mGammaTable;
0624     mGammaTable = new KGammaTable(*gt);
0625 
0626     int size = mDesc->size/sizeof(SANE_Word);       // size of scanner gamma table
0627 #ifdef DEBUG_GETSET
0628     qCDebug(LIBKOOKASCAN_LOG) << "Setting gamma table for" << mName << "size" << size << "to" << gt->toString();
0629 #endif
0630     const int *run = mGammaTable->getTable(size);   // get table of that size
0631     QVector<SANE_Word> qa(size);            // converted to SANE values
0632 
0633     switch (mDesc->type)
0634     {
0635 case SANE_TYPE_INT:
0636         for (int i = 0; i<size; ++i) qa[i] = static_cast<SANE_Word>(run[i]);
0637         break;
0638 
0639 case SANE_TYPE_FIXED:
0640         for (int i = 0; i<size; ++i) qa[i] = SANE_FIX(static_cast<double>(run[i]));
0641         break;
0642 
0643 default:
0644         //qCDebug(LIBKOOKASCAN_LOG) << "Can't set" << mName << "with type" << mDesc->type;
0645         return (false);
0646     }
0647 
0648     mBuffer = QByteArray(((const char *) (qa.constData())), mDesc->size);
0649     mBufferClean = false;
0650     return (true);
0651 }
0652 
0653 
0654 bool KScanOption::get(int *val) const
0655 {
0656     if (!isValid() || mBuffer.isNull()) return (false);
0657 
0658     switch (mDesc->type)
0659     {
0660 case SANE_TYPE_BOOL:                    /* Buffer has a SANE_Word */
0661         *val = (*((SANE_Word *) mBuffer.constData()))==SANE_TRUE ? 1 : 0;
0662         break;
0663 
0664 case SANE_TYPE_INT:                 /* reading just the first is OK */
0665         *val = *((SANE_Word *) mBuffer.constData());
0666         break;
0667 
0668 case SANE_TYPE_FIXED:                   /* reading just the first is OK */
0669         *val = static_cast<int>(SANE_UNFIX(*((SANE_Word *) mBuffer.constData())));
0670         break;
0671 
0672 default:
0673         //qCDebug(LIBKOOKASCAN_LOG) << "Can't get" << mName << "as type" << mDesc->type;
0674         return (false);
0675     }
0676 
0677 #ifdef DEBUG_GETSET
0678     qCDebug(LIBKOOKASCAN_LOG) << "Returning" << mName << "=" << *val;
0679 #endif
0680     return (true);
0681 }
0682 
0683 
0684 QByteArray KScanOption::get() const
0685 {
0686     if (!isValid() || mBuffer.isNull()) return ("");
0687 
0688     QByteArray retstr;
0689 
0690     /* Handle gamma-table correctly */
0691     if (mWidgetType==KScanOption::GammaTable)
0692     {
0693         if (mGammaTable!=nullptr) retstr = mGammaTable->toString().toLocal8Bit();
0694     }
0695     else
0696     {                           // first SANE_Word of buffer
0697         SANE_Word sane_word = *((SANE_Word *) mBuffer.constData());
0698         switch (mDesc->type)
0699         {
0700 case SANE_TYPE_BOOL:
0701             retstr = (sane_word==SANE_TRUE ? "true" : "false");
0702             break;
0703 
0704 case SANE_TYPE_STRING:
0705             // This assignment looks pointless, but mBuffer may include null trailing
0706             // bytes.  Getting a pointer to the buffer data and then converting it
0707             // back to a QByteArray takes a deep copy and eliminates them.
0708             retstr = mBuffer.constData();
0709             break;
0710 
0711 case SANE_TYPE_INT:
0712             retstr.setNum(sane_word);
0713             break;
0714 
0715 case SANE_TYPE_FIXED:
0716             retstr.setNum(SANE_UNFIX(sane_word), 'f');
0717             // Using 'f' format always has the specified number of digits (default 6)
0718             // after the decimal point.  We never want exponential format here so must
0719             // use 'f', but do not want trailing zeros or a decimal point.
0720             while (retstr.endsWith('0')) retstr.chop(1);
0721             if (retstr.endsWith('.')) retstr.chop(1);
0722             break;
0723 
0724 default:    //qCDebug(LIBKOOKASCAN_LOG) << "Can't get" << mName << "as type" << mDesc->type;
0725             retstr = "?";
0726             break;
0727         }
0728     }
0729 
0730 #ifdef DEBUG_GETSET
0731     qCDebug(LIBKOOKASCAN_LOG) << "Returning" << mName << "=" << retstr;
0732 #endif
0733     return (retstr);
0734 }
0735 
0736 
0737 bool KScanOption::get(KGammaTable *gt) const
0738 {
0739     if (mGammaTable==nullptr) return (false);       // has not been set
0740 
0741     gt->setAll(mGammaTable->getGamma(), mGammaTable->getBrightness(), mGammaTable->getContrast());
0742 #ifdef DEBUG_GETSET
0743     qCDebug(LIBKOOKASCAN_LOG) << "Returning" << mName << "=" << gt->toString();
0744 #endif
0745     return (true);
0746 }
0747 
0748 
0749 QList<QByteArray> KScanOption::getList() const
0750 {
0751     const char **sstring = nullptr;
0752     QList<QByteArray> strList;
0753     if (mDesc==nullptr) return (strList);
0754 
0755     if (mDesc->constraint_type==SANE_CONSTRAINT_STRING_LIST)
0756     {
0757         sstring = (const char **)mDesc->constraint.string_list;
0758 
0759         while (*sstring!=nullptr)
0760         {
0761             strList.append(*sstring);
0762             sstring++;
0763         }
0764    }
0765    else if (mDesc->constraint_type==SANE_CONSTRAINT_WORD_LIST)
0766    {
0767        const SANE_Int *sint = mDesc->constraint.word_list;
0768        const int amount_vals = sint[0];
0769        QString s;
0770 
0771        for (int i = 1; i<=amount_vals; i++)
0772        {
0773            if (mDesc->type==SANE_TYPE_FIXED) s = QString::number(SANE_UNFIX(sint[i]), 'f');
0774            else s = QString::number(sint[i]);
0775 
0776            strList.append(s.toLocal8Bit());
0777        }
0778    }
0779    else if (mDesc->constraint_type==SANE_CONSTRAINT_RANGE && mWidgetType==KScanOption::Resolution)
0780    {
0781        double min,max;
0782        int imin,imax;
0783        getRange( &min, &max);
0784        imin = static_cast<int>(min);
0785        imax = static_cast<int>(max);
0786 
0787        for (const int *ip = resList; *ip!=0; ++ip)
0788        {
0789            if (*ip<imin) continue;
0790            if (*ip>imax) continue;
0791            strList.append(QString::number(*ip).toLocal8Bit());
0792        }
0793    }
0794 
0795    return (strList);
0796 }
0797 
0798 
0799 bool KScanOption::getRange(double *minp, double *maxp, double *quantp) const
0800 {
0801     if (mDesc==nullptr) return (false);
0802 
0803     double min = 0.0;
0804     double max = 0.0;
0805     double quant = -1;
0806 
0807     if (mDesc->constraint_type==SANE_CONSTRAINT_RANGE)
0808     {
0809         const SANE_Range *r = mDesc->constraint.range;
0810         if (mDesc->type==SANE_TYPE_FIXED)
0811         {
0812             min = SANE_UNFIX(r->min);
0813             max = SANE_UNFIX(r->max);
0814             quant = SANE_UNFIX(r->quant);
0815         }
0816         else
0817         {
0818             min = r->min;
0819             max = r->max;
0820             quant = r->quant;
0821         }
0822     }
0823     else if (mDesc->constraint_type==SANE_CONSTRAINT_WORD_LIST)
0824     {
0825         // Originally done in KScanOption::getRangeFromList()
0826         const SANE_Int *wl = mDesc->constraint.word_list;
0827         const int num = wl[0];
0828 
0829         double value;
0830         for (int i = 1; i<=num; ++i)
0831         {
0832             if (mDesc->type==SANE_TYPE_FIXED) value = SANE_UNFIX(wl[i]);
0833             else value = wl[i];
0834             if (i==1 || value<min) min = value;
0835             if (i==1 || value>max) max = value;
0836     }
0837 
0838         if (num>=2) quant = (max-min)/(num-1);      // synthesise from total range
0839     }
0840     else
0841     {
0842         qCDebug(LIBKOOKASCAN_LOG) << "Not a range type" << mDesc->name;
0843         return (false);
0844     }
0845 
0846     *minp = min;
0847     *maxp = max;
0848     if (quantp!=nullptr) *quantp = quant;
0849 
0850     return (true);
0851 }
0852 
0853 
0854 KScanControl *KScanOption::createWidget(QWidget *parent)
0855 {
0856     if (!isValid())
0857     {
0858         qCWarning(LIBKOOKASCAN_LOG) << "Option is not valid";
0859     return (nullptr);
0860     }
0861 
0862     delete mControl; mControl = nullptr;        // dispose of the old control
0863     
0864     if (mDesc!=nullptr) mText = i18n(mDesc->title);
0865 
0866     qCDebug(LIBKOOKASCAN_LOG) << "type" << mWidgetType << "name" << mName;
0867 
0868     KScanControl *w = nullptr;
0869 
0870     switch (mWidgetType)
0871     {
0872 case KScanOption::Bool:                 // toggle button
0873     w = new KScanCheckbox(parent, mText);
0874     break;
0875 
0876 case KScanOption::SingleValue:              // numeric entry
0877         // This will have been deduced by the SANE parameter having a type
0878         // of SANE_TYPE_INT or SANE_TYPE_FIXED and a constraint of SANE_CONSTRAINT_NONE.
0879         // In theory a SANE_TYPE_FIXED value should allow floating point user input
0880         // and values, as noted for KScanOption::Range below.  No problem caused by
0881         // only allowing integer values has been reported with any existing scanner.
0882     w = new KScanNumberEntry(parent, mText);
0883     break;
0884 
0885 case KScanOption::Range:                // slider and spinbox
0886     {
0887             KScanSlider *kss = new KScanSlider(parent, mText, true);
0888             w = kss;
0889 
0890             // This is the only option type that has an option to reset
0891             // the value to the default.  SANE does not specify what the
0892             // default value is, so it has to be guessed.  If the allowable
0893             // range includes the value 0 then the default is set to that,
0894             // otherwise the minimum value is used.
0895             //
0896             // Note that in theory the constrained range of a SANE value may
0897             // not have endpoints that are integers and it may not even include
0898             // any integer;  for example, the range could be 12.02 - 12.08
0899             // or even more precisely specified.  However, since the GUI
0900             // controls used (QSlider and QSpinBox) only work in integers then
0901             // the range is rounded to integer values.  No problem caused by
0902             // doing this has been reported with any existing scanner.
0903 
0904             double min, max, quant;
0905             getRange(&min, &max, &quant);
0906 
0907             int stdValue = 0;
0908             if (stdValue>max || stdValue<min) stdValue = qRound(min);
0909             kss->setRange(qRound(min), qRound(max), qRound(quant), stdValue);
0910         }
0911     break;
0912 
0913 case KScanOption::Resolution:               // special resolution combo
0914 case KScanOption::StringList:               // string list combo
0915     w = new KScanCombo(parent, mText);
0916     break;
0917 
0918 case KScanOption::GammaTable:               // no widget for this
0919     qCDebug(LIBKOOKASCAN_LOG) << "GammaTable not implemented here";
0920     break;
0921 
0922 case KScanOption::String:               // free text entry
0923     w = new KScanStringEntry(parent, mText);
0924     break;
0925 
0926 case KScanOption::File:                 // file name requester
0927     w = new KScanFileRequester(parent, mText);
0928         break;
0929 
0930 case KScanOption::Group:                // group separator
0931     w = new KScanGroup(parent, mText);
0932         break;
0933 
0934 case KScanOption::Button:               // action button
0935     w = new KScanPushButton(parent, mText);
0936         break;
0937 
0938 default:
0939     qCWarning(LIBKOOKASCAN_LOG) << "unknown control type " << mWidgetType;
0940     break;
0941     }
0942     
0943     if (w!=nullptr)
0944     {
0945         mControl = w;
0946         updateList();                   // set list for combo box
0947 
0948         switch (w->type())
0949         {
0950 case KScanControl::Number:              // numeric control
0951             connect(w, QOverload<int>::of(&KScanControl::settingChanged), this, QOverload<int>::of(&KScanOption::slotWidgetChange));
0952             break;
0953 
0954 case KScanControl::Text:                // text control
0955             connect(w, QOverload<const QString &>::of(&KScanControl::settingChanged), this, QOverload<const QString &>::of(&KScanOption::slotWidgetChange));
0956             break;
0957 
0958 case KScanControl::Button:              // push button
0959             connect(w, &KScanControl::returnPressed, this, QOverload<>::of(&KScanOption::slotWidgetChange));
0960             break;
0961 
0962 case KScanControl::Group:               // group separator
0963             break;                  // nothing to do here
0964         }
0965 
0966     if (mDesc!=nullptr)             // set tool tip
0967         {
0968             if (qstrlen(mDesc->desc)>0)         // if there is a SANE description
0969             {
0970                 QString tt = i18n(mDesc->desc);
0971                 // KDE tooltips do not normally end with a full stop, unless
0972                 // they are multi-sentence.  But the SANE descriptions often do,
0973                 // so trim it off for consistency.  Is this a good thing to do
0974                 // in non-western languages?
0975                 if (tt.endsWith('.') && tt.count(". ")==0) tt.chop(1);
0976                 // Force the format to be rich text so that it will be word wrapped
0977                 // at a sensible width, see documentation for QToolTip.
0978                 w->setToolTip("<qt>"+tt);
0979             }
0980         }
0981 
0982         // No accelerators for advanced options, so as not to soak up
0983         // too many of the available accelerators for controls that are
0984         // rarely going to be used.  See also getLabel().
0985         if (!isCommonOption()) KAcceleratorManager::setNoAccel(w);
0986     }
0987     
0988     reload();                       // check if active, enabled etc.
0989     if (w!=nullptr) redrawWidget();
0990     return (w);
0991 }
0992 
0993 
0994 QLabel *KScanOption::getLabel(QWidget *parent, bool alwaysBuddy) const
0995 {
0996     if (mControl==nullptr) return (nullptr);
0997     KSqueezedTextLabel *l = new KSqueezedTextLabel(mControl->label(), parent);
0998     if (isCommonOption() || alwaysBuddy) l->setBuddy(mControl->focusProxy());
0999     return (l);
1000 }
1001 
1002 
1003 QLabel *KScanOption::getUnit(QWidget *parent) const
1004 {
1005     if (mControl==nullptr) return (nullptr);
1006 
1007     QString s;
1008     switch (mDesc->unit)
1009     {
1010 case SANE_UNIT_NONE:                        break;
1011 case SANE_UNIT_PIXEL:       s = i18n("pixels");     break;
1012 case SANE_UNIT_BIT:     s = i18n("bits");       break;
1013 case SANE_UNIT_MM:      s = i18n("mm");         break;
1014 case SANE_UNIT_DPI:     s = i18n("dpi");        break;
1015 case SANE_UNIT_PERCENT:     s = i18n("%");          break;
1016 case SANE_UNIT_MICROSECOND: s = i18n("\302\265sec");    break;
1017 default:                            break;
1018     }
1019 
1020     if (s.isEmpty()) return (nullptr);          // no unit label
1021     QLabel *l = new QLabel(s, parent);
1022     return (l);
1023 }
1024 
1025 
1026 void KScanOption::allocForDesc()
1027 {
1028     if (mDesc==nullptr) return;
1029 
1030     switch (mDesc->type)
1031     {
1032 case SANE_TYPE_INT:
1033 case SANE_TYPE_FIXED:
1034 case SANE_TYPE_STRING:      
1035         allocBuffer(mDesc->size);
1036         break;
1037 
1038 case SANE_TYPE_BOOL:
1039         allocBuffer(sizeof(SANE_Word));
1040         break;
1041 
1042 default:
1043         if (mDesc->size>0) allocBuffer(mDesc->size);
1044         break;
1045     }
1046 }
1047 
1048 
1049 void KScanOption::allocBuffer(long size)
1050 {
1051     if (size<1) return;
1052 
1053 #ifdef DEBUG_MEM
1054     qCDebug(LIBKOOKASCAN_LOG) << "Allocating" << size << "bytes for" << name;
1055 #endif
1056     mBuffer.resize(size);               // set buffer size
1057     if (mBuffer.isNull())               // check allocation worked???
1058     {
1059         qCWarning(LIBKOOKASCAN_LOG) << "Allocating" << size << "bytes for" << mName << "failed!";
1060         return;
1061     }
1062 
1063     mBuffer.fill(0);                    // clear allocated buffer
1064 }