File indexing completed on 2024-03-24 15:48:03

0001 /************************************************************************
0002  *                                  *
0003  *  This file is part of Kooka, a scanning/OCR application using    *
0004  *  Qt <http://www.qt.io> and KDE Frameworks <http://www.kde.org>.  *
0005  *                                  *
0006  *  Copyright (C) 2000-2016 Klaas Freitag <freitag@suse.de>     *
0007  *                          Jonathan Marten <jjm@keelhaul.me.uk>    *
0008  *                                  *
0009  *  Kooka is free software; you can redistribute it and/or modify it    *
0010  *  under the terms of the GNU Library General Public License as    *
0011  *  published by the Free Software Foundation and appearing in the  *
0012  *  file COPYING included in the packaging of this file;  either    *
0013  *  version 2 of the License, or (at your option) any later version.    *
0014  *                                  *
0015  *  As a special exception, permission is given to link this program    *
0016  *  with any version of the KADMOS OCR/ICR engine (a product of     *
0017  *  reRecognition GmbH, Kreuzlingen), and distribute the resulting  *
0018  *  executable without including the source code for KADMOS in the  *
0019  *  source distribution.                        *
0020  *                                  *
0021  *  This program is distributed in the hope that it will be useful, *
0022  *  but WITHOUT ANY WARRANTY; without even the implied warranty of  *
0023  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
0024  *  GNU General Public License for more details.            *
0025  *                                  *
0026  *  You should have received a copy of the GNU General Public       *
0027  *  License along with this program;  see the file COPYING.  If     *
0028  *  not, see <http://www.gnu.org/licenses/>.                *
0029  *                                  *
0030  ************************************************************************/
0031 
0032 #include "kscancontrols.h"
0033 
0034 #include <qgroupbox.h>
0035 #include <qlayout.h>
0036 #include <qtoolbutton.h>
0037 #include <qspinbox.h>
0038 #include <qcombobox.h>
0039 #include <qcheckbox.h>
0040 #include <qlabel.h>
0041 #include <qslider.h>
0042 #include <qlineedit.h>
0043 #include <qicon.h>
0044 #include <qimagereader.h>
0045 #include <qmimetype.h>
0046 #include <qmimedatabase.h>
0047 
0048 #include <klocalizedstring.h>
0049 #include <kurlrequester.h>
0050 
0051 #include "imagefilter.h"
0052 #include "libkookascan_logging.h"
0053 
0054 
0055 //  KScanControl - base class
0056 //  -------------------------
0057 
0058 KScanControl::KScanControl(QWidget *parent, const QString &text)
0059     : QWidget(parent)
0060 {
0061     mLayout = new QHBoxLayout(this);
0062     mLayout->setMargin(0);
0063 
0064     mText = text;
0065     if (mText.isEmpty()) {
0066         mText = i18n("(Unknown)");
0067     }
0068 }
0069 
0070 KScanControl::~KScanControl()
0071 {
0072 }
0073 
0074 QString KScanControl::text() const
0075 {
0076     return (QString());
0077 }
0078 
0079 void KScanControl::setText(const QString &text)
0080 {
0081 }
0082 
0083 int KScanControl::value() const
0084 {
0085     return (0);
0086 }
0087 
0088 void KScanControl::setValue(int val)
0089 {
0090 }
0091 
0092 QString KScanControl::label() const
0093 {
0094     return (mText + ":");
0095 }
0096 
0097 //  KScanSlider - slider, spin box and optional reset button
0098 //  --------------------------------------------------------
0099 
0100 KScanSlider::KScanSlider(QWidget *parent, const QString &text,
0101                          double min, double max,
0102                          bool haveStdButt, int stdValue)
0103     : KScanControl(parent, text)
0104 {
0105     init(haveStdButt);
0106     setRange(min, max, -1, stdValue);
0107 }
0108 
0109 KScanSlider::KScanSlider(QWidget *parent, const QString &text, bool haveStdButt)
0110     : KScanControl(parent, text)
0111 {
0112     init(haveStdButt);
0113 }
0114 
0115 void KScanSlider::init(bool haveStdButt)
0116 {
0117     mStdButt = nullptr;
0118 
0119     mSlider = new QSlider(Qt::Horizontal, this);    // slider
0120     mSlider->setTickPosition(QSlider::TicksBelow);
0121     mSlider->setMinimumWidth(140);
0122     mLayout->addWidget(mSlider, 1);
0123 
0124     mSpinbox = new QSpinBox(this);          // spin box
0125     mSpinbox->setMinimumWidth(60);
0126     mLayout->addWidget(mSpinbox);
0127 
0128     if (haveStdButt)
0129     {
0130         mStdButt = new QToolButton(this);       // reset button
0131         mStdButt->setIcon(QIcon::fromTheme("edit-undo"));
0132         mLayout->addWidget(mStdButt);
0133     }
0134 
0135     connect(mSlider, &QSlider::valueChanged, this, &KScanSlider::slotSliderSpinboxChange);
0136     connect(mSpinbox, QOverload<int>::of(&QSpinBox::valueChanged), this, &KScanSlider::slotSliderSpinboxChange);
0137     if (mStdButt!=nullptr) connect(mStdButt, &QPushButton::clicked, this, &KScanSlider::slotRevertValue);
0138 
0139     setFocusProxy(mSlider);
0140     setFocusPolicy(Qt::StrongFocus);
0141 }
0142 
0143 void KScanSlider::setRange(int min, int max, int step, int stdValue)
0144 {
0145     const double span = max-min;
0146 
0147     mSlider->setRange(min, max);
0148     mSlider->setTickInterval(qMax(qRound(span/10), 1));
0149     mSpinbox->setRange(min, max);
0150 
0151     if (step==-1)                   // default step value
0152     {
0153         mSlider->setSingleStep(qMax(qRound(span/20), 1));
0154         mSlider->setPageStep(qMax(qRound(span/5), 1));
0155         mSpinbox->setSingleStep(1);
0156     }
0157     else                        // specified step value
0158     {
0159         mSlider->setSingleStep(step);
0160         mSlider->setPageStep(step*4);
0161         mSpinbox->setSingleStep(step);
0162     }
0163 
0164     mStdValue = qBound(stdValue, min, max);     // limit default value to range
0165     mValue = mStdValue;                 // set current value to that
0166     mSlider->setValue(mValue);
0167     mSpinbox->setValue(mValue);
0168 
0169     if (mStdButt!=nullptr) mStdButt->setToolTip(i18n("Reset this setting to its standard value, %1", QString::number(mStdValue)));
0170 }
0171 
0172 void KScanSlider::setValue(int val)
0173 {
0174     if (val == mValue) {
0175         return;    // avoid recursive signals
0176     }
0177     mValue = val;
0178 
0179     int spin = mSpinbox->value();
0180     if (spin != val) {
0181         mSpinbox->blockSignals(true);
0182         mSpinbox->setValue(val);            // track in spin box
0183         mSpinbox->blockSignals(false);
0184     }
0185 
0186     int slid = mSlider->value();
0187     if (slid != val) {
0188         mSlider->blockSignals(true);
0189         mSlider->setValue(val);             // track in slider
0190         mSlider->blockSignals(false);
0191     }
0192 }
0193 
0194 int KScanSlider::value() const
0195 {
0196     return (mValue);
0197 }
0198 
0199 void KScanSlider::slotSliderSpinboxChange(int val)
0200 {
0201     setValue(val);
0202     emit settingChanged(val);
0203 }
0204 
0205 void KScanSlider::slotRevertValue()
0206 {
0207     // only connected if button exists
0208     slotSliderSpinboxChange(mStdValue);
0209 }
0210 
0211 //  KScanStringEntry - free text entry field
0212 //  ----------------------------------------
0213 
0214 KScanStringEntry::KScanStringEntry(QWidget *parent, const QString &text)
0215     : KScanControl(parent, text)
0216 {
0217     mEntry = new QLineEdit(this);
0218     mLayout->addWidget(mEntry);
0219 
0220     connect(mEntry, &QLineEdit::textChanged, this, QOverload<const QString &>::of(&KScanStringEntry::settingChanged));
0221     connect(mEntry, &QLineEdit::returnPressed, this, &KScanStringEntry::returnPressed);
0222 
0223     setFocusProxy(mEntry);
0224     setFocusPolicy(Qt::StrongFocus);
0225 }
0226 
0227 QString KScanStringEntry::text() const
0228 {
0229     return (mEntry->text());
0230 }
0231 
0232 void KScanStringEntry::setText(const QString &text)
0233 {
0234     if (text == mEntry->text()) {
0235         return;    // avoid recursive signals
0236     }
0237     mEntry->setText(text);
0238 }
0239 
0240 //  KScanNumberEntry - number entry field
0241 //  -------------------------------------
0242 
0243 KScanNumberEntry::KScanNumberEntry(QWidget *parent, const QString &text)
0244     : KScanControl(parent, text)
0245 {
0246     mEntry = new QLineEdit(this);
0247     mEntry->setValidator(new QIntValidator);
0248     mLayout->addWidget(mEntry);
0249 
0250     connect(mEntry, &QLineEdit::textChanged, this, QOverload<const QString &>::of(&KScanNumberEntry::settingChanged));
0251     connect(mEntry, &QLineEdit::textChanged, this, &KScanNumberEntry::slotTextChanged);
0252     connect(mEntry, &QLineEdit::returnPressed, this, &KScanNumberEntry::returnPressed);
0253 
0254     setFocusProxy(mEntry);
0255     setFocusPolicy(Qt::StrongFocus);
0256 }
0257 
0258 int KScanNumberEntry::value() const
0259 {
0260     return (mEntry->text().toInt());
0261 }
0262 
0263 void KScanNumberEntry::setValue(int i)
0264 {
0265     mEntry->setText(QString::number(i));
0266 }
0267 
0268 void KScanNumberEntry::slotTextChanged(const QString &s)
0269 {
0270     emit settingChanged(s.toInt());
0271 }
0272 
0273 //  KScanCheckbox - on/off option
0274 //  -----------------------------
0275 
0276 KScanCheckbox::KScanCheckbox(QWidget *parent, const QString &text)
0277     : KScanControl(parent, text)
0278 {
0279     mCheckbox = new QCheckBox(text, this);
0280     mLayout->addWidget(mCheckbox);
0281 
0282     connect(mCheckbox, &QCheckBox::stateChanged, this, QOverload<int>::of(&KScanCheckbox::settingChanged));
0283 
0284     setFocusProxy(mCheckbox);
0285     setFocusPolicy(Qt::StrongFocus);
0286 }
0287 
0288 int KScanCheckbox::value() const
0289 {
0290     return ((int) mCheckbox->isChecked());
0291 }
0292 
0293 void KScanCheckbox::setValue(int i)
0294 {
0295     mCheckbox->setChecked((bool) i);
0296 }
0297 
0298 QString KScanCheckbox::label() const
0299 {
0300     return (QString());
0301 }
0302 
0303 //  KScanCombo - combo box with list of options
0304 //  -------------------------------------------
0305 //
0306 //  This control (and currently only this control) is special, because the
0307 //  item text is set to the translated SANE value.  But these values need
0308 //  to be reported back unchanged, so the untranslated form is stored
0309 //  in the itemData and the translated form is seen by the user as itemText.
0310 //  Any access needs to only set or report the itemData, never the itemText,
0311 //  which is why the activated(QString) signal cannot be used directly.
0312 
0313 KScanCombo::KScanCombo(QWidget *parent, const QString &text)
0314     : KScanControl(parent, text)
0315 {
0316     mCombo = new QComboBox(this);
0317     mLayout->addWidget(mCombo);
0318 
0319     connect(mCombo, QOverload<int>::of(&QComboBox::activated), this, &KScanCombo::slotActivated);
0320 
0321     setFocusProxy(mCombo);
0322     setFocusPolicy(Qt::StrongFocus);
0323 }
0324 
0325 void KScanCombo::setList(const QList<QByteArray> &list)
0326 {
0327     // An optimisation, which may turn out to be not a valid one:
0328     // only update the combo box if the number of items has changed.
0329     if (list.count()==mCombo->count()) return;
0330     //qCDebug(LIBKOOKASCAN_LOG) << "count" << mCombo->count() << "->" << list.count() << "=" << list;
0331 
0332     const QString cur = text();             // get current setting
0333 
0334     const bool bs = mCombo->blockSignals(true);
0335     mCombo->clear();
0336 
0337     for (const QByteArray &item : list)
0338     {
0339         // See the KI18N Programmer's Guide, "Connecting to Catalogs in Library Code"
0340         mCombo->addItem(ki18n(item.constData()).toString("sane-backends"), item);
0341     }
0342 
0343     mCombo->blockSignals(bs);
0344     if (!cur.isEmpty()) setText(cur);                   // try to restore old setting
0345 }
0346 
0347 void KScanCombo::setText(const QString &text)
0348 {
0349     int i = mCombo->findData(text);         // find item with that text
0350     if (i == -1) return;                // ignore if not present
0351 
0352     if (i == mCombo->currentIndex()) return;        // avoid recursive signals
0353     mCombo->setCurrentIndex(i);
0354 }
0355 
0356 void KScanCombo::setIcon(const QIcon &icon, const char *ent)
0357 {
0358     int i = mCombo->findData(ent);          // find item with that text
0359     if (i != -1) mCombo->setItemIcon(i, icon);
0360 }
0361 
0362 QString KScanCombo::text() const
0363 {
0364     return (textAt(mCombo->currentIndex()));
0365 }
0366 
0367 void KScanCombo::setValue(int i)
0368 {
0369     mCombo->setCurrentIndex(i);
0370 }
0371 
0372 QString KScanCombo::textAt(int i) const
0373 {
0374     return (i == -1 ? QString() : mCombo->itemData(i).toString());
0375 }
0376 
0377 int KScanCombo::count() const
0378 {
0379     return (mCombo->count());
0380 }
0381 
0382 void KScanCombo::slotActivated(int i)
0383 {
0384     emit settingChanged(i);
0385     emit settingChanged(textAt(i));
0386 }
0387 
0388 //  KScanFileRequester - standard URL requester
0389 //  -------------------------------------------
0390 
0391 KScanFileRequester::KScanFileRequester(QWidget *parent, const QString &text)
0392     : KScanControl(parent, text)
0393 {
0394     mEntry = new KUrlRequester(this);
0395     mLayout->addWidget(mEntry);
0396 
0397     QString filter = i18n("*.pnm *.pbm *.pgm *.ppm|PNM Image Files");
0398     filter += '\n'+ImageFilter::kdeFilter(ImageFilter::Reading);
0399     mEntry->setFilter(filter);
0400 
0401     connect(mEntry, QOverload<const QString &>::of(&KUrlRequester::textChanged), this, QOverload<const QString &>::of(&KScanFileRequester::settingChanged));
0402     connect(mEntry, QOverload<const QString &>::of(&KUrlRequester::returnPressed), this, &KScanStringEntry::returnPressed);
0403 
0404     setFocusProxy(mEntry);
0405     setFocusPolicy(Qt::StrongFocus);
0406 }
0407 
0408 QString KScanFileRequester::text() const
0409 {
0410     return (mEntry->url().url());
0411 }
0412 
0413 void KScanFileRequester::setText(const QString &text)
0414 {
0415     if (text == mEntry->url().url()) {
0416         return;    // avoid recursive signals
0417     }
0418     mEntry->setUrl(QUrl::fromLocalFile(text));
0419 }
0420 
0421 //  KScanGroup - group separator
0422 //  ----------------------------
0423 
0424 KScanGroup::KScanGroup(QWidget *parent, const QString &text)
0425     : KScanControl(parent, text)
0426 {
0427     mGroup = new QGroupBox(text, this);
0428     mGroup->setFlat(true);
0429     mLayout->addWidget(mGroup);
0430 }
0431 
0432 QString KScanGroup::label() const
0433 {
0434     return (QString());
0435 }
0436 
0437 //  KScanPushButton - action button
0438 //  -------------------------------
0439 
0440 KScanPushButton::KScanPushButton(QWidget *parent, const QString &text)
0441     : KScanControl(parent, text)
0442 {
0443     mButton = new QPushButton(text, this);
0444     mLayout->addWidget(mButton);
0445 
0446     connect(mButton, &QPushButton::clicked, this, &KScanPushButton::returnPressed);
0447 }
0448 
0449 QString KScanPushButton::label() const
0450 {
0451     return (QString());
0452 }