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) 2000-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 "kscancontrols.h"
33 
34 #include <qgroupbox.h>
35 #include <qlayout.h>
36 #include <qtoolbutton.h>
37 #include <qspinbox.h>
38 #include <qcombobox.h>
39 #include <qcheckbox.h>
40 #include <qlabel.h>
41 #include <qslider.h>
42 #include <qlineedit.h>
43 #include <qdebug.h>
44 #include <qicon.h>
45 #include <qimagereader.h>
46 #include <qmimetype.h>
47 #include <qmimedatabase.h>
48 
49 #include <klocalizedstring.h>
50 #include <kurlrequester.h>
51 
52 #include "imagefilter.h"
53 
54 
55 //  KScanControl - base class
56 //  -------------------------
57 
KScanControl(QWidget * parent,const QString & text)58 KScanControl::KScanControl(QWidget *parent, const QString &text)
59     : QWidget(parent)
60 {
61     mLayout = new QHBoxLayout(this);
62     mLayout->setMargin(0);
63 
64     mText = text;
65     if (mText.isEmpty()) {
66         mText = i18n("(Unknown)");
67     }
68 }
69 
~KScanControl()70 KScanControl::~KScanControl()               {}
71 
text() const72 QString KScanControl::text() const
73 {
74     return (QString());
75 }
setText(const QString & text)76 void KScanControl::setText(const QString &text)     {}
77 
value() const78 int KScanControl::value() const
79 {
80     return (0);
81 }
setValue(int val)82 void KScanControl::setValue(int val)            {}
83 
label() const84 QString KScanControl::label() const
85 {
86     return (mText + ":");
87 }
88 
89 //  KScanSlider - slider, spin box and optional reset button
90 //  --------------------------------------------------------
91 
KScanSlider(QWidget * parent,const QString & text,double min,double max,bool haveStdButt,int stdValue)92 KScanSlider::KScanSlider(QWidget *parent, const QString &text,
93                          double min, double max,
94                          bool haveStdButt, int stdValue)
95     : KScanControl(parent, text)
96 {
97     mValue = mStdValue = stdValue;
98     mStdButt = nullptr;
99 
100     mSlider = new QSlider(Qt::Horizontal, this);    // slider
101     mSlider->setRange(((int) min), ((int) max));
102     mSlider->setTickPosition(QSlider::TicksBelow);
103     mSlider->setTickInterval(qMax(((int)((max - min) / 10)), 1));
104     mSlider->setSingleStep(qMax(((int)((max - min) / 20)), 1));
105     mSlider->setPageStep(qMax(((int)((max - min) / 10)), 1));
106     mSlider->setMinimumWidth(140);
107     mSlider->setValue(mValue);              // initial value
108     mLayout->addWidget(mSlider, 1);
109 
110     mSpinbox = new QSpinBox(this);          // spin box
111     mSpinbox->setRange((int) min, (int) max);
112     mSpinbox->setSingleStep(1);
113     mSpinbox->setValue(mValue);             // initial value
114     mLayout->addWidget(mSpinbox);
115 
116     if (haveStdButt) {
117         mStdButt = new QToolButton(this);       // reset button
118         mStdButt->setIcon(QIcon::fromTheme("edit-undo"));
119         mStdButt->setToolTip(i18n("Reset this setting to its standard value, %1", stdValue));
120         mLayout->addWidget(mStdButt);
121     }
122 
123     connect(mSlider, SIGNAL(valueChanged(int)), SLOT(slotSliderSpinboxChange(int)));
124     connect(mSpinbox, SIGNAL(valueChanged(int)), SLOT(slotSliderSpinboxChange(int)));
125     if (mStdButt != nullptr) {
126         connect(mStdButt, SIGNAL(clicked()), SLOT(slotRevertValue()));
127     }
128 
129     setFocusProxy(mSlider);
130     setFocusPolicy(Qt::StrongFocus);
131 }
132 
setValue(int val)133 void KScanSlider::setValue(int val)
134 {
135     if (val == mValue) {
136         return;    // avoid recursive signals
137     }
138     mValue = val;
139 
140     int spin = mSpinbox->value();
141     if (spin != val) {
142         mSpinbox->blockSignals(true);
143         mSpinbox->setValue(val);            // track in spin box
144         mSpinbox->blockSignals(false);
145     }
146 
147     int slid = mSlider->value();
148     if (slid != val) {
149         mSlider->blockSignals(true);
150         mSlider->setValue(val);             // track in slider
151         mSlider->blockSignals(false);
152     }
153 }
154 
value() const155 int KScanSlider::value() const
156 {
157     return (mValue);
158 }
159 
slotSliderSpinboxChange(int val)160 void KScanSlider::slotSliderSpinboxChange(int val)
161 {
162     setValue(val);
163     emit settingChanged(val);
164 }
165 
slotRevertValue()166 void KScanSlider::slotRevertValue()
167 {
168     // only connected if button exists
169     slotSliderSpinboxChange(mStdValue);
170 }
171 
172 //  KScanStringEntry - free text entry field
173 //  ----------------------------------------
174 
KScanStringEntry(QWidget * parent,const QString & text)175 KScanStringEntry::KScanStringEntry(QWidget *parent, const QString &text)
176     : KScanControl(parent, text)
177 {
178     mEntry = new QLineEdit(this);
179     mLayout->addWidget(mEntry);
180 
181     connect(mEntry, SIGNAL(textChanged(QString)), SIGNAL(settingChanged(QString)));
182     connect(mEntry, SIGNAL(returnPressed()), SIGNAL(returnPressed()));
183 
184     setFocusProxy(mEntry);
185     setFocusPolicy(Qt::StrongFocus);
186 }
187 
text() const188 QString KScanStringEntry::text() const
189 {
190     return (mEntry->text());
191 }
192 
setText(const QString & text)193 void KScanStringEntry::setText(const QString &text)
194 {
195     if (text == mEntry->text()) {
196         return;    // avoid recursive signals
197     }
198     mEntry->setText(text);
199 }
200 
201 //  KScanNumberEntry - number entry field
202 //  -------------------------------------
203 
KScanNumberEntry(QWidget * parent,const QString & text)204 KScanNumberEntry::KScanNumberEntry(QWidget *parent, const QString &text)
205     : KScanControl(parent, text)
206 {
207     mEntry = new QLineEdit(this);
208     mEntry->setValidator(new QIntValidator);
209     mLayout->addWidget(mEntry);
210 
211     connect(mEntry, SIGNAL(textChanged(QString)), SLOT(slotTextChanged(QString)));
212     connect(mEntry, SIGNAL(returnPressed()), SIGNAL(returnPressed()));
213 
214     setFocusProxy(mEntry);
215     setFocusPolicy(Qt::StrongFocus);
216 }
217 
value() const218 int KScanNumberEntry::value() const
219 {
220     return (mEntry->text().toInt());
221 }
222 
setValue(int i)223 void KScanNumberEntry::setValue(int i)
224 {
225     mEntry->setText(QString::number(i));
226 }
227 
slotTextChanged(const QString & s)228 void KScanNumberEntry::slotTextChanged(const QString &s)
229 {
230     emit settingChanged(s.toInt());
231 }
232 
233 //  KScanCheckbox - on/off option
234 //  -----------------------------
235 
KScanCheckbox(QWidget * parent,const QString & text)236 KScanCheckbox::KScanCheckbox(QWidget *parent, const QString &text)
237     : KScanControl(parent, text)
238 {
239     mCheckbox = new QCheckBox(text, this);
240     mLayout->addWidget(mCheckbox);
241 
242     connect(mCheckbox, SIGNAL(stateChanged(int)), SIGNAL(settingChanged(int)));
243 
244     setFocusProxy(mCheckbox);
245     setFocusPolicy(Qt::StrongFocus);
246 }
247 
value() const248 int KScanCheckbox::value() const
249 {
250     return ((int) mCheckbox->isChecked());
251 }
252 
setValue(int i)253 void KScanCheckbox::setValue(int i)
254 {
255     mCheckbox->setChecked((bool) i);
256 }
257 
label() const258 QString KScanCheckbox::label() const
259 {
260     return (QString());
261 }
262 
263 //  KScanCombo - combo box with list of options
264 //  -------------------------------------------
265 //
266 //  This control (and currently only this control) is special, because the
267 //  item text is set to the translated value of the option values.  But these
268 //  values need to reported back unchanged, so the untranslated form is stored
269 //  in the itemData and the translated form is seen by the user as itemText.
270 //  Any access needs to only set or report the itemData, never the itemText,
271 //  which is why the activated(QString) signal cannot be used directly.
272 
KScanCombo(QWidget * parent,const QString & text)273 KScanCombo::KScanCombo(QWidget *parent, const QString &text)
274     : KScanControl(parent, text)
275 {
276     mCombo = new QComboBox(this);
277     mLayout->addWidget(mCombo);
278 
279     connect(mCombo, SIGNAL(activated(int)), SLOT(slotActivated(int)));
280 
281     setFocusProxy(mCombo);
282     setFocusPolicy(Qt::StrongFocus);
283 }
284 
285 
setList(const QList<QByteArray> & list)286 void KScanCombo::setList(const QList<QByteArray> &list)
287 {
288     // An optimisation, which may turn out to be not a valid one:
289     // only update the combo box if the number of items has changed.
290     if (list.count()==mCombo->count()) return;
291     //qDebug() << "count" << mCombo->count() << "->" << list.count() << "=" << list;
292 
293     const QString cur = text();				// get current setting
294 
295     const bool bs = mCombo->blockSignals(true);
296     mCombo->clear();
297 
298     foreach (const QByteArray &item, list)
299     {
300         // See the KI18N Programmer's Guide, "Connecting to Catalogs in Library Code"
301         mCombo->addItem(ki18n(item.constData()).toString("sane-backends"), item);
302     }
303 
304     mCombo->blockSignals(bs);
305     if (!cur.isEmpty()) setText(cur);                   // try to restore old setting
306 }
307 
setText(const QString & text)308 void KScanCombo::setText(const QString &text)
309 {
310     int i = mCombo->findData(text);			// find item with that text
311     if (i == -1) return;				// ignore if not present
312 
313     if (i == mCombo->currentIndex()) return;		// avoid recursive signals
314     mCombo->setCurrentIndex(i);
315 }
316 
setIcon(const QIcon & icon,const char * ent)317 void KScanCombo::setIcon(const QIcon &icon, const char *ent)
318 {
319     int i = mCombo->findData(ent);			// find item with that text
320     if (i != -1) mCombo->setItemIcon(i, icon);
321 }
322 
text() const323 QString KScanCombo::text() const
324 {
325     return (textAt(mCombo->currentIndex()));
326 }
327 
setValue(int i)328 void KScanCombo::setValue(int i)
329 {
330     mCombo->setCurrentIndex(i);
331 }
332 
textAt(int i) const333 QString KScanCombo::textAt(int i) const
334 {
335     return (i == -1 ? QString() : mCombo->itemData(i).toString());
336 }
337 
count() const338 int KScanCombo::count() const
339 {
340     return (mCombo->count());
341 }
342 
slotActivated(int i)343 void KScanCombo::slotActivated(int i)
344 {
345     emit settingChanged(i);
346     emit settingChanged(textAt(i));
347 }
348 
349 //  KScanFileRequester - standard URL requester
350 //  -------------------------------------------
351 
KScanFileRequester(QWidget * parent,const QString & text)352 KScanFileRequester::KScanFileRequester(QWidget *parent, const QString &text)
353     : KScanControl(parent, text)
354 {
355     mEntry = new KUrlRequester(this);
356     mLayout->addWidget(mEntry);
357 
358     QString filter = i18n("*.pnm *.pbm *.pgm *.ppm|PNM Image Files");
359     filter += '\n'+ImageFilter::kdeFilter(ImageFilter::Reading);
360     mEntry->setFilter(filter);
361 
362     connect(mEntry, SIGNAL(textChanged(QString)), SIGNAL(settingChanged(QString)));
363     connect(mEntry, SIGNAL(returnPressed()), SIGNAL(returnPressed()));
364 
365     setFocusProxy(mEntry);
366     setFocusPolicy(Qt::StrongFocus);
367 }
368 
text() const369 QString KScanFileRequester::text() const
370 {
371     return (mEntry->url().url());
372 }
373 
setText(const QString & text)374 void KScanFileRequester::setText(const QString &text)
375 {
376     if (text == mEntry->url().url()) {
377         return;    // avoid recursive signals
378     }
379     mEntry->setUrl(QUrl::fromLocalFile(text));
380 }
381 
382 //  KScanGroup - group separator
383 //  ----------------------------
384 
KScanGroup(QWidget * parent,const QString & text)385 KScanGroup::KScanGroup(QWidget *parent, const QString &text)
386     : KScanControl(parent, text)
387 {
388     mGroup = new QGroupBox(text, this);
389     mGroup->setFlat(true);
390     mLayout->addWidget(mGroup);
391 }
392 
label() const393 QString KScanGroup::label() const
394 {
395     return (QString());
396 }
397 
398 //  KScanPushButton - action button
399 //  -------------------------------
400 
KScanPushButton(QWidget * parent,const QString & text)401 KScanPushButton::KScanPushButton(QWidget *parent, const QString &text)
402     : KScanControl(parent, text)
403 {
404     mButton = new QPushButton(text, this);
405     mLayout->addWidget(mButton);
406 
407     connect(mButton, SIGNAL(clicked()), SIGNAL(returnPressed()));
408 }
409 
label() const410 QString KScanPushButton::label() const
411 {
412     return (QString());
413 }
414