File indexing completed on 2025-01-19 12:59:19
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(>)); 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 }