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