1 /* ============================================================
2  *
3  * SPDX-FileCopyrightText: 2009 Kare Sars <kare dot sars at iki dot fi>
4  * SPDX-FileCopyrightText: 2014 Gregor Mitsch : port to KDE5 frameworks
5  *
6  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7  *
8  * ============================================================ */
9 
10 #include "ksanebaseoption.h"
11 
12 #include <ksane_debug.h>
13 
14 namespace KSaneIface
15 {
16 
KSaneBaseOption()17 KSaneBaseOption::KSaneBaseOption() : QObject()
18 {
19 }
20 
KSaneBaseOption(const SANE_Handle handle,const int index)21 KSaneBaseOption::KSaneBaseOption(const SANE_Handle handle, const int index)
22     : QObject(), m_handle(handle), m_index(index)
23 {
24 }
25 
~KSaneBaseOption()26 KSaneBaseOption::~KSaneBaseOption()
27 {
28     if (m_data) {
29         free(m_data);
30         m_data = nullptr;
31     }
32 }
33 
readOption()34 void KSaneBaseOption::readOption()
35 {
36     if (m_handle != nullptr) {
37         m_optDesc = sane_get_option_descriptor(m_handle, m_index);
38         Q_EMIT optionReloaded();
39     }
40 }
41 
state() const42 KSaneOption::KSaneOptionState KSaneBaseOption::state() const
43 {
44     if (m_optDesc == nullptr) {
45         return KSaneOption::StateHidden;
46     }
47 
48     if (((m_optDesc->cap & SANE_CAP_SOFT_DETECT) == 0) ||
49             (m_optDesc->cap & SANE_CAP_INACTIVE) ||
50             ((m_optDesc->size == 0) && (type() != KSaneOption::TypeAction))) {
51         return KSaneOption::StateHidden;
52     } else if ((m_optDesc->cap & SANE_CAP_SOFT_SELECT) == 0) {
53         return KSaneOption::StateDisabled;
54     }
55     return KSaneOption::StateActive;
56 }
57 
needsPolling() const58 bool KSaneBaseOption::needsPolling() const
59 {
60     if (!m_optDesc) {
61         return false;
62     }
63 
64     if ((m_optDesc->cap & SANE_CAP_SOFT_DETECT) && !(m_optDesc->cap & SANE_CAP_SOFT_SELECT)) {
65         qCDebug(KSANE_LOG) << name() << "optDesc->cap =" << m_optDesc->cap;
66         return true;
67     }
68 
69     return false;
70 }
71 
name() const72 QString KSaneBaseOption::name() const
73 {
74     if (m_optDesc == nullptr) {
75         return QString();
76     }
77     return QString::fromUtf8(m_optDesc->name);
78 }
79 
title() const80 QString KSaneBaseOption::title() const
81 {
82     if (m_optDesc == nullptr) {
83         return QString();
84     }
85     return sane_i18n(m_optDesc->title);
86 }
87 
description() const88 QString KSaneBaseOption::description() const
89 {
90     if (m_optDesc == nullptr) {
91         return QString();
92     }
93     return sane_i18n(m_optDesc->desc);
94 }
95 
type() const96 KSaneOption::KSaneOptionType KSaneBaseOption::type() const
97 {
98     return m_optionType;
99 }
100 
writeData(void * data)101 bool KSaneBaseOption::writeData(void *data)
102 {
103     SANE_Status status;
104     SANE_Int res;
105 
106     if (state() == KSaneOption::StateDisabled) {
107         return false;
108     }
109 
110     status = sane_control_option(m_handle, m_index, SANE_ACTION_SET_VALUE, data, &res);
111     if (status != SANE_STATUS_GOOD) {
112         qCDebug(KSANE_LOG) << m_optDesc->name << "sane_control_option returned:" << sane_strstatus(status);
113         // write failed. re read the current setting
114         readValue();
115         return false;
116     }
117     if (res & SANE_INFO_INEXACT) {
118         //qCDebug(KSANE_LOG) << "write was inexact. Reload value just in case...";
119         readValue();
120     }
121 
122     if (res & SANE_INFO_RELOAD_OPTIONS) {
123         Q_EMIT optionsNeedReload();
124         // optReload reloads also the values
125     } else if (res & SANE_INFO_RELOAD_PARAMS) {
126         // 'else if' because with optReload we force also valReload :)
127         Q_EMIT valuesNeedReload();
128     }
129 
130     return true;
131 }
132 
readValue()133 void KSaneBaseOption::readValue() {}
134 
toSANE_Word(unsigned char * data)135 SANE_Word KSaneBaseOption::toSANE_Word(unsigned char *data)
136 {
137     SANE_Word tmp;
138     // if __BYTE_ORDER is not defined we get #if 0 == 0
139 #if __BYTE_ORDER == __LITTLE_ENDIAN
140     tmp  = (data[0] & 0xff);
141     tmp += ((SANE_Word)(data[1] & 0xff)) << 8;
142     tmp += ((SANE_Word)(data[2] & 0xff)) << 16;
143     tmp += ((SANE_Word)(data[3] & 0xff)) << 24;
144 #else
145     tmp  = (data[3] & 0xff);
146     tmp += ((SANE_Word)(data[2] & 0xff)) << 8;
147     tmp += ((SANE_Word)(data[1] & 0xff)) << 16;
148     tmp += ((SANE_Word)(data[0] & 0xff)) << 24;
149 #endif
150     return tmp;
151 }
152 
fromSANE_Word(unsigned char * data,SANE_Word from)153 void KSaneBaseOption::fromSANE_Word(unsigned char *data, SANE_Word from)
154 {
155     // if __BYTE_ORDER is not defined we get #if 0 == 0
156 #if __BYTE_ORDER == __LITTLE_ENDIAN
157     data[0] = (from & 0x000000FF);
158     data[1] = (from & 0x0000FF00) >> 8;
159     data[2] = (from & 0x00FF0000) >> 16;
160     data[3] = (from & 0xFF000000) >> 24;
161 #else
162     data[3] = (from & 0x000000FF);
163     data[2] = (from & 0x0000FF00) >> 8;
164     data[1] = (from & 0x00FF0000) >> 16;
165     data[0] = (from & 0xFF000000) >> 24;
166 #endif
167 }
168 
value() const169 QVariant KSaneBaseOption::value() const
170 {
171     return QVariant();
172 }
173 
minimumValue() const174 QVariant KSaneBaseOption::minimumValue() const
175 {
176     return QVariant();
177 }
178 
maximumValue() const179 QVariant KSaneBaseOption::maximumValue() const
180 {
181     return QVariant();
182 }
183 
stepValue() const184 QVariant KSaneBaseOption::stepValue() const
185 {
186     return QVariant();
187 }
188 
valueList() const189 QVariantList KSaneBaseOption::valueList() const
190 {
191     return QVariantList();
192 }
193 
internalValueList() const194 QVariantList KSaneBaseOption::internalValueList() const
195 {
196     return QVariantList();
197 }
198 
valueUnit() const199 KSaneOption::KSaneOptionUnit KSaneBaseOption::valueUnit() const
200 {
201     if (m_optDesc != nullptr) {
202         switch (m_optDesc->unit) {
203         case SANE_UNIT_PIXEL:       return KSaneOption::UnitPixel;
204         case SANE_UNIT_BIT:         return KSaneOption::UnitBit;
205         case SANE_UNIT_MM:          return KSaneOption::UnitMilliMeter;
206         case SANE_UNIT_DPI:         return KSaneOption::UnitDPI;
207         case SANE_UNIT_PERCENT:     return KSaneOption::UnitPercent;
208         case SANE_UNIT_MICROSECOND: return KSaneOption::UnitMicroSecond;
209         default: return KSaneOption::UnitNone;
210         }
211     } else {
212         return KSaneOption::UnitNone;
213     }
214 }
215 
valueSize() const216 int KSaneBaseOption::valueSize() const
217 {
218     if (m_optDesc != nullptr) {
219         return m_optDesc->size / sizeof(SANE_Word);
220     }
221     return 0;
222 }
223 
valueAsString() const224 QString KSaneBaseOption::valueAsString() const
225 {
226     return QString();
227 }
228 
setValue(const QVariant &)229 bool KSaneBaseOption::setValue(const QVariant &)
230 {
231     return false;
232 }
233 
storeCurrentData()234 bool KSaneBaseOption::storeCurrentData()
235 {
236     SANE_Status status;
237     SANE_Int res;
238 
239     // check if we can read the value
240     if (state() == KSaneOption::StateHidden) {
241         return false;
242     }
243 
244     // read that current value
245     if (m_data != nullptr) {
246         free(m_data);
247     }
248     m_data = (unsigned char *)malloc(m_optDesc->size);
249     status = sane_control_option(m_handle, m_index, SANE_ACTION_GET_VALUE, m_data, &res);
250     if (status != SANE_STATUS_GOOD) {
251         qCDebug(KSANE_LOG) << m_optDesc->name << "sane_control_option returned" << status;
252         return false;
253     }
254     return true;
255 }
256 
restoreSavedData()257 bool KSaneBaseOption::restoreSavedData()
258 {
259     // check if we have saved any data
260     if (m_data == nullptr) {
261         return false;
262     }
263 
264     // check if we can write the value
265     if (state() == KSaneOption::StateHidden) {
266         return false;
267     }
268     if (state() == KSaneOption::StateDisabled) {
269         return false;
270     }
271 
272     writeData(m_data);
273     readValue();
274     return true;
275 }
276 
optionType(const SANE_Option_Descriptor * optDesc)277 KSaneOption::KSaneOptionType KSaneBaseOption::optionType(const SANE_Option_Descriptor *optDesc)
278 {
279     if (!optDesc) {
280         return KSaneOption::TypeDetectFail;
281     }
282 
283     switch (optDesc->constraint_type) {
284     case SANE_CONSTRAINT_NONE:
285         switch (optDesc->type) {
286         case SANE_TYPE_BOOL:
287             return KSaneOption::TypeBool;
288         case SANE_TYPE_INT:
289             if (optDesc->size == sizeof(SANE_Word)) {
290                 return KSaneOption::TypeInteger;
291             }
292             qCDebug(KSANE_LOG) << "Can not handle:" << optDesc->title;
293             qCDebug(KSANE_LOG) << "SANE_CONSTRAINT_NONE && SANE_TYPE_INT";
294             qCDebug(KSANE_LOG) << "size" << optDesc->size << "!= sizeof(SANE_Word)";
295             break;
296         case SANE_TYPE_FIXED:
297             if (optDesc->size == sizeof(SANE_Word)) {
298                 return KSaneOption::TypeDouble;
299             }
300             qCDebug(KSANE_LOG) << "Can not handle:" << optDesc->title;
301             qCDebug(KSANE_LOG) << "SANE_CONSTRAINT_NONE && SANE_TYPE_FIXED";
302             qCDebug(KSANE_LOG) << "size" << optDesc->size << "!= sizeof(SANE_Word)";
303             break;
304         case SANE_TYPE_BUTTON:
305             return KSaneOption::TypeAction;
306         case SANE_TYPE_STRING:
307             return KSaneOption::TypeString;
308         case SANE_TYPE_GROUP:
309             return KSaneOption::TypeDetectFail;
310         }
311         break;
312     case SANE_CONSTRAINT_RANGE:
313         switch (optDesc->type) {
314         case SANE_TYPE_BOOL:
315             return KSaneOption::TypeBool;
316         case SANE_TYPE_INT:
317             if (optDesc->size == sizeof(SANE_Word)) {
318                 return KSaneOption::TypeInteger;
319             }
320 
321             if ((strcmp(optDesc->name, SANE_NAME_GAMMA_VECTOR) == 0) ||
322                     (strcmp(optDesc->name, SANE_NAME_GAMMA_VECTOR_R) == 0) ||
323                     (strcmp(optDesc->name, SANE_NAME_GAMMA_VECTOR_G) == 0) ||
324                     (strcmp(optDesc->name, SANE_NAME_GAMMA_VECTOR_B) == 0)) {
325                 return KSaneOption::TypeGamma;
326             }
327             qCDebug(KSANE_LOG) << "Can not handle:" << optDesc->title;
328             qCDebug(KSANE_LOG) << "SANE_CONSTRAINT_RANGE && SANE_TYPE_INT && !SANE_NAME_GAMMA_VECTOR...";
329             qCDebug(KSANE_LOG) << "size" << optDesc->size << "!= sizeof(SANE_Word)";
330             break;
331         case SANE_TYPE_FIXED:
332             if (optDesc->size == sizeof(SANE_Word)) {
333                 return KSaneOption::TypeDouble;
334             }
335             qCDebug(KSANE_LOG) << "Can not handle:" << optDesc->title;
336             qCDebug(KSANE_LOG) << "SANE_CONSTRAINT_RANGE && SANE_TYPE_FIXED";
337             qCDebug(KSANE_LOG) << "size" << optDesc->size << "!= sizeof(SANE_Word)";
338             qCDebug(KSANE_LOG) << "Analog Gamma vector?";
339             break;
340         case SANE_TYPE_STRING:
341             qCDebug(KSANE_LOG) << "Can not handle:" << optDesc->title;
342             qCDebug(KSANE_LOG) << "SANE_CONSTRAINT_RANGE && SANE_TYPE_STRING";
343             return KSaneOption::TypeDetectFail;
344         case SANE_TYPE_BUTTON:
345             return KSaneOption::TypeAction;
346         case SANE_TYPE_GROUP:
347             return KSaneOption::TypeDetectFail;
348         }
349         break;
350     case SANE_CONSTRAINT_WORD_LIST:
351     case SANE_CONSTRAINT_STRING_LIST:
352         return KSaneOption::TypeValueList;
353     }
354     return KSaneOption::TypeDetectFail;
355 }
356 
357 }  // NameSpace KSaneIface
358