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 "ksanelistoption.h"
11 
12 #include <QVarLengthArray>
13 
14 #include <ksane_debug.h>
15 
16 namespace KSaneIface
17 {
18 
KSaneListOption(const SANE_Handle handle,const int index)19 KSaneListOption::KSaneListOption(const SANE_Handle handle, const int index)
20     : KSaneBaseOption(handle, index)
21 {
22     m_optionType = KSaneOption::TypeValueList;
23 }
24 
readValue()25 void KSaneListOption::readValue()
26 {
27     if (state() == KSaneOption::StateHidden) {
28         return;
29     }
30 
31     // read that current value
32     QVarLengthArray<unsigned char> data(m_optDesc->size);
33     SANE_Status status;
34     SANE_Int res;
35     status = sane_control_option(m_handle, m_index, SANE_ACTION_GET_VALUE, data.data(), &res);
36     if (status != SANE_STATUS_GOOD) {
37         return;
38     }
39 
40     QVariant newValue;
41     switch (m_optDesc->type) {
42     case SANE_TYPE_INT:
43         newValue = static_cast<int>(toSANE_Word(data.data()));
44         break;
45     case SANE_TYPE_FIXED:
46         newValue = SANE_UNFIX(toSANE_Word(data.data()));
47         break;
48     case SANE_TYPE_STRING:
49         newValue = sane_i18n(reinterpret_cast<char *>(data.data()));
50         break;
51     default:
52         break;
53     }
54 
55     if (newValue != m_currentValue) {
56         m_currentValue = newValue;
57         Q_EMIT valueChanged(m_currentValue);
58     }
59 }
60 
valueList() const61 QVariantList KSaneListOption::valueList() const
62 {
63     int i;
64     QVariantList list;
65 
66     switch (m_optDesc->type) {
67     case SANE_TYPE_INT:
68         for (i = 1; i <= m_optDesc->constraint.word_list[0]; ++i) {
69             list << static_cast<int>(m_optDesc->constraint.word_list[i]);;
70         }
71         break;
72     case SANE_TYPE_FIXED:
73         for (i = 1; i <= m_optDesc->constraint.word_list[0]; ++i) {
74             list << SANE_UNFIX(m_optDesc->constraint.word_list[i]);
75         }
76         break;
77     case SANE_TYPE_STRING:
78         i = 0;
79         while (m_optDesc->constraint.string_list[i] != nullptr) {
80             list << sane_i18n(m_optDesc->constraint.string_list[i]);
81             i++;
82         }
83         break;
84     default :
85         qCDebug(KSANE_LOG) << "can not handle type:" << m_optDesc->type;
86         break;
87     }
88     return list;
89 }
90 
internalValueList() const91 QVariantList KSaneListOption::internalValueList() const
92 {
93     int i;
94     QVariantList list;
95 
96     switch (m_optDesc->type) {
97     case SANE_TYPE_INT:
98         for (i = 1; i <= m_optDesc->constraint.word_list[0]; ++i) {
99             list << static_cast<int>(m_optDesc->constraint.word_list[i]);;
100         }
101         break;
102     case SANE_TYPE_FIXED:
103         for (i = 1; i <= m_optDesc->constraint.word_list[0]; ++i) {
104             list << SANE_UNFIX(m_optDesc->constraint.word_list[i]);
105         }
106         break;
107     case SANE_TYPE_STRING:
108         i = 0;
109         while (m_optDesc->constraint.string_list[i] != nullptr) {
110             list << QString::fromLatin1(m_optDesc->constraint.string_list[i]);
111             i++;
112         }
113         break;
114     default :
115         qCDebug(KSANE_LOG) << "can not handle type:" << m_optDesc->type;
116         break;
117     }
118     return list;
119 }
120 
setValue(const QVariant & value)121 bool KSaneListOption::setValue(const QVariant &value)
122 {
123     bool success = false;
124     if (static_cast<QMetaType::Type>(value.type()) == QMetaType::QString) {
125         success = setValue(value.toString());
126     } else {
127         success = setValue(value.toDouble());
128     }
129 
130     return success;
131 }
132 
minimumValue() const133 QVariant KSaneListOption::minimumValue() const
134 {
135     QVariant value;
136     if (state() == KSaneOption::StateHidden) {
137         return value;
138     }
139     double dValueMin;
140     int iValueMin;
141     switch (m_optDesc->type) {
142     case SANE_TYPE_INT:
143         iValueMin = static_cast<int>(m_optDesc->constraint.word_list[1]);
144         for (int i = 2; i <= m_optDesc->constraint.word_list[0]; i++) {
145             iValueMin = qMin(static_cast<int>(m_optDesc->constraint.word_list[i]), iValueMin);
146         }
147         value = iValueMin;
148         break;
149     case SANE_TYPE_FIXED:
150         dValueMin = SANE_UNFIX(m_optDesc->constraint.word_list[1]);
151         for (int i = 2; i <= m_optDesc->constraint.word_list[0]; i++) {
152             dValueMin = qMin(SANE_UNFIX(m_optDesc->constraint.word_list[i]), dValueMin);
153         }
154         value = dValueMin;
155         break;
156     default:
157         qCDebug(KSANE_LOG) << "can not handle type:" << m_optDesc->type;
158         return value;
159     }
160     return value;
161 }
162 
value() const163 QVariant KSaneListOption::value() const
164 {
165     if (state() == KSaneOption::StateHidden) {
166         return QVariant();
167     }
168     return m_currentValue;
169 }
170 
setValue(double value)171 bool KSaneListOption::setValue(double value)
172 {
173     unsigned char data[4];
174     double tmp;
175     double minDiff;
176     int i;
177     int minIndex = 1;
178 
179     switch (m_optDesc->type) {
180     case SANE_TYPE_INT:
181         tmp = static_cast<double>(m_optDesc->constraint.word_list[minIndex]);
182         minDiff = qAbs(value - tmp);
183         for (i = 2; i <= m_optDesc->constraint.word_list[0]; ++i) {
184             tmp = static_cast<double>(m_optDesc->constraint.word_list[i]);
185             if (qAbs(value - tmp) < minDiff) {
186                 minDiff = qAbs(value - tmp);
187                 minIndex = i;
188             }
189         }
190         fromSANE_Word(data, m_optDesc->constraint.word_list[minIndex]);
191         writeData(data);
192         readValue();
193         return (minDiff < 1.0);
194     case SANE_TYPE_FIXED:
195         tmp = SANE_UNFIX(m_optDesc->constraint.word_list[minIndex]);
196         minDiff = qAbs(value - tmp);
197         for (i = 2; i <= m_optDesc->constraint.word_list[0]; ++i) {
198             tmp = SANE_UNFIX(m_optDesc->constraint.word_list[i]);
199             if (qAbs(value - tmp) < minDiff) {
200                 minDiff = qAbs(value - tmp);
201                 minIndex = i;
202             }
203         }
204         fromSANE_Word(data, m_optDesc->constraint.word_list[minIndex]);
205         writeData(data);
206         readValue();
207         return (minDiff < 1.0);
208     default:
209         qCDebug(KSANE_LOG) << "can not handle type:" << m_optDesc->type;
210         break;
211     }
212     return false;
213 }
214 
valueAsString() const215 QString KSaneListOption::valueAsString() const
216 {
217     if (state() == KSaneOption::StateHidden) {
218         return QString();
219     }
220     return m_currentValue.toString();
221 }
222 
setValue(const QString & value)223 bool KSaneListOption::setValue(const QString &value)
224 {
225     if (state() == KSaneOption::StateHidden) {
226         return false;
227     }
228 
229     unsigned char data[4];
230     void* data_ptr = nullptr;
231     SANE_Word fixed;
232     int i;
233     double d;
234     bool ok;
235     QString tmp;
236 
237     switch (m_optDesc->type) {
238     case SANE_TYPE_INT:
239         i = value.toInt(&ok);
240         if (ok) {
241             fromSANE_Word(data, i);
242             data_ptr = data;
243         } else {
244             return false;
245         }
246 
247         break;
248     case SANE_TYPE_FIXED:
249         d = value.toDouble(&ok);
250         if (ok) {
251             fixed = SANE_FIX(d);
252             fromSANE_Word(data, fixed);
253             data_ptr = data;
254         } else {
255             return false;
256         }
257 
258         break;
259     case SANE_TYPE_STRING:
260         i = 0;
261         while (m_optDesc->constraint.string_list[i] != nullptr) {
262             tmp = QString::fromLatin1(m_optDesc->constraint.string_list[i]);
263             if (value != tmp) {
264                 tmp = sane_i18n(m_optDesc->constraint.string_list[i]);
265             }
266             if (value == tmp) {
267                 data_ptr = (void *)m_optDesc->constraint.string_list[i];
268                 break;
269             }
270             i++;
271         }
272         if (m_optDesc->constraint.string_list[i] == nullptr) {
273             return false;
274         }
275         break;
276     default:
277         qCDebug(KSANE_LOG) << "can only handle SANE_TYPE: INT, FIXED and STRING";
278         return false;
279     }
280     writeData(data_ptr);
281 
282     readValue();
283     return true;
284 }
285 
286 }  // NameSpace KSaneIface
287