diff --git a/src/ksanewidget.cpp b/src/ksanewidget.cpp --- a/src/ksanewidget.cpp +++ b/src/ksanewidget.cpp @@ -103,8 +103,8 @@ // Create the static UI // create the preview d->m_previewViewer = new KSaneViewer(&(d->m_previewImg), this); - connect(d->m_previewViewer, SIGNAL(newSelection(float,float,float,float)), - d, SLOT(handleSelection(float,float,float,float))); + connect(d->m_previewViewer, SIGNAL(newSelection(float, float, float, float)), + d, SLOT(handleSelection(float, float, float, float))); d->m_warmingUp = new QLabel; d->m_warmingUp->setText(i18n("Waiting for the scan to start.")); @@ -219,6 +219,12 @@ d->m_otherScrollA->setFrameShape(QFrame::NoFrame); d->m_optsTabWidget->addTab(d->m_otherScrollA, i18n("Scanner Specific Options")); + // Add the scan area options tab + d->m_areaScrollA = new QScrollArea; + d->m_areaScrollA->setWidgetResizable(true); + d->m_areaScrollA->setFrameShape(QFrame::NoFrame); + d->m_optsTabWidget->addTab(d->m_areaScrollA, i18n("Scan Area Options")); + d->m_splitter = new QSplitter(this); d->m_splitter->addWidget(d->m_optsTabWidget); d->m_splitter->setStretchFactor(0, 0); @@ -470,8 +476,8 @@ d->m_pollList.append(d->m_optList.at(i)); KSaneOptCheckBox *buttonOption = qobject_cast(d->m_optList.at(i)); if (buttonOption) { - connect(buttonOption, SIGNAL(buttonPressed(QString,QString,bool)), - this, SIGNAL(buttonPressed(QString,QString,bool))); + connect(buttonOption, SIGNAL(buttonPressed(QString, QString, bool)), + this, SIGNAL(buttonPressed(QString, QString, bool))); } } } @@ -719,7 +725,7 @@ int KSaneWidget::setOptVals(const QMap &opts) { if (d->m_scanThread->isRunning() || - d->m_previewThread->isRunning()) { + d->m_previewThread->isRunning()) { return -1; } @@ -736,9 +742,9 @@ } } if ((d->m_splitGamChB) && - (d->m_optGamR) && - (d->m_optGamG) && - (d->m_optGamB)) { + (d->m_optGamR) && + (d->m_optGamG) && + (d->m_optGamB)) { // check if the current gamma values are identical. if they are identical, // uncheck the "Separate color intensity tables" checkbox QString redGamma; @@ -760,7 +766,7 @@ if (opts.contains(InvetColorsOption)) { tmp = opts[InvetColorsOption]; if ((tmp.compare(QStringLiteral("true"), Qt::CaseInsensitive) == 0) || - (tmp.compare(QStringLiteral("1")) == 0)) { + (tmp.compare(QStringLiteral("1")) == 0)) { d->m_invertColors->setChecked(true); } else { d->m_invertColors->setChecked(false); @@ -772,7 +778,7 @@ bool KSaneWidget::setOptVal(const QString &option, const QString &value) { if (d->m_scanThread->isRunning() || - d->m_previewThread->isRunning()) { + d->m_previewThread->isRunning()) { return false; } @@ -781,12 +787,12 @@ if ((opt = d->getOption(option)) != nullptr) { if (opt->setValue(value)) { if ((d->m_splitGamChB) && - (d->m_optGamR) && - (d->m_optGamG) && - (d->m_optGamB) && - ((opt == d->m_optGamR) || - (opt == d->m_optGamG) || - (opt == d->m_optGamB))) { + (d->m_optGamR) && + (d->m_optGamG) && + (d->m_optGamB) && + ((opt == d->m_optGamR) || + (opt == d->m_optGamG) || + (opt == d->m_optGamB))) { // check if the current gamma values are identical. if they are identical, // uncheck the "Separate color intensity tables" checkbox QString redGamma; @@ -810,7 +816,7 @@ // special handling for non-sane option if (option == InvetColorsOption) { if ((value.compare(QStringLiteral("true"), Qt::CaseInsensitive) == 0) || - (value.compare(QStringLiteral("1")) == 0)) { + (value.compare(QStringLiteral("1")) == 0)) { d->m_invertColors->setChecked(true); } else { d->m_invertColors->setChecked(false); @@ -896,10 +902,10 @@ return; } - float tlxRatio = topLeft.x()/xmax; - float tlyRatio = topLeft.y()/ymax; - float brxRatio = bottomRight.x()/xmax; - float bryRatio = bottomRight.y()/ymax; + float tlxRatio = topLeft.x() / xmax; + float tlyRatio = topLeft.y() / ymax; + float brxRatio = bottomRight.x() / xmax; + float bryRatio = bottomRight.y() / ymax; d->m_previewViewer->setSelection(tlxRatio, tlyRatio, brxRatio, bryRatio); } diff --git a/src/ksanewidget_p.h b/src/ksanewidget_p.h --- a/src/ksanewidget_p.h +++ b/src/ksanewidget_p.h @@ -46,6 +46,13 @@ #include "ksanewidget.h" #include "ksaneoption.h" +#include "ksaneoptbutton.h" +#include "ksaneoptcheckbox.h" +#include "ksaneoptcombo.h" +#include "ksaneoptentry.h" +#include "ksaneoptfslider.h" +#include "ksaneoptgamma.h" +#include "ksaneoptslider.h" #include "ksaneviewer.h" #include "labeledgamma.h" #include "labeledcheckbox.h" @@ -54,6 +61,8 @@ #include "ksanepreviewthread.h" #include "ksanefinddevicesthread.h" #include "ksaneauth.h" +#include "labeledfslider.h" +#include "labeledcombo.h" #define IMG_DATA_R_SIZE 100000 @@ -98,10 +107,21 @@ void invertPreview(); void pollPollOptions(); + void updateScanarea(); + public: void alertUser(int type, const QString &strStatus); public: + QMap m_paperSizesMap; + LabeledCombo *m_scanareaPapersize; + LabeledFSlider *m_scanareaWidth; + LabeledFSlider *m_scanareaHeight; + LabeledFSlider *m_scanareaX; + LabeledFSlider *m_scanareaY; + QPushButton *m_scanareaFlip; + LabeledCheckbox *m_scanareaEnableManual; + // backend independent QTabWidget *m_optsTabWidget; QScrollArea *m_basicScrollA; @@ -111,6 +131,9 @@ QWidget *m_otherOptsTab; LabeledCheckbox *m_invertColors; + QScrollArea *m_areaScrollA; + QWidget *m_areaOptsTab; + QSplitter *m_splitter; SplitterCollapser *m_optionsCollapser; diff --git a/src/ksanewidget_p.cpp b/src/ksanewidget_p.cpp --- a/src/ksanewidget_p.cpp +++ b/src/ksanewidget_p.cpp @@ -38,6 +38,10 @@ #include #include +#include "labeledfslider.h" +#include "labeledcombo.h" +#include "labeledcheckbox.h" + #define SCALED_PREVIEW_MAX_SIDE 400 static const int ActiveSelection = 100000; @@ -362,9 +366,9 @@ m_commonGamma->setToolTip(i18n(SANE_DESC_GAMMA_VECTOR)); - connect(m_commonGamma, SIGNAL(gammaChanged(int,int,int)), m_optGamR->widget(), SLOT(setValues(int,int,int))); - connect(m_commonGamma, SIGNAL(gammaChanged(int,int,int)), m_optGamG->widget(), SLOT(setValues(int,int,int))); - connect(m_commonGamma, SIGNAL(gammaChanged(int,int,int)), m_optGamB->widget(), SLOT(setValues(int,int,int))); + connect(m_commonGamma, SIGNAL(gammaChanged(int, int, int)), m_optGamR->widget(), SLOT(setValues(int, int, int))); + connect(m_commonGamma, SIGNAL(gammaChanged(int, int, int)), m_optGamG->widget(), SLOT(setValues(int, int, int))); + connect(m_commonGamma, SIGNAL(gammaChanged(int, int, int)), m_optGamB->widget(), SLOT(setValues(int, int, int))); m_splitGamChB = new LabeledCheckbox(m_colorOpts, i18n("Separate color intensity tables")); color_lay->addWidget(m_splitGamChB); @@ -402,12 +406,12 @@ for (int i = 0; i < m_optList.size(); ++i) { KSaneOption *option = m_optList.at(i); if ((option->widget() == nullptr) && - (option->name() != QStringLiteral(SANE_NAME_SCAN_TL_X)) && - (option->name() != QStringLiteral(SANE_NAME_SCAN_TL_Y)) && - (option->name() != QStringLiteral(SANE_NAME_SCAN_BR_X)) && - (option->name() != QStringLiteral(SANE_NAME_SCAN_BR_Y)) && - (option->name() != QStringLiteral(SANE_NAME_PREVIEW)) && - (option->hasGui())) { + (option->name() != QStringLiteral(SANE_NAME_SCAN_TL_X)) && + (option->name() != QStringLiteral(SANE_NAME_SCAN_TL_Y)) && + (option->name() != QStringLiteral(SANE_NAME_SCAN_BR_X)) && + (option->name() != QStringLiteral(SANE_NAME_SCAN_BR_Y)) && + (option->name() != QStringLiteral(SANE_NAME_PREVIEW)) && + (option->hasGui())) { option->createWidget(m_otherOptsTab); other_layout->addWidget(option->widget()); } @@ -416,6 +420,89 @@ // add a stretch to the end to keep the parameters at the top other_layout->addStretch(); + // scan area options + m_areaOptsTab = new QWidget; + m_areaScrollA->setWidget(m_areaOptsTab); + QVBoxLayout *area_layout = new QVBoxLayout(m_areaOptsTab); + + m_paperSizesMap.clear(); + + const QString defaultPaperSize = QStringLiteral("A4"); + + m_paperSizesMap[QStringLiteral("A3")] = QPointF(297.0f, 420.0f); + m_paperSizesMap[QStringLiteral("A4")] = QPointF(210.0f, 297.0f); + m_paperSizesMap[QStringLiteral("A5")] = QPointF(148.0f, 210.0f); + m_paperSizesMap[QStringLiteral("A6")] = QPointF(105.0f, 148.0f); + + m_paperSizesMap[QStringLiteral("B3")] = QPointF(353.0f, 500.0f); + m_paperSizesMap[QStringLiteral("B4")] = QPointF(250.0f, 353.0f); + m_paperSizesMap[QStringLiteral("B5")] = QPointF(176.0f, 250.0f); + m_paperSizesMap[QStringLiteral("B6")] = QPointF(125.0f, 176.0f); + + m_paperSizesMap[QStringLiteral("C3")] = QPointF(324.0f, 458.0f); + m_paperSizesMap[QStringLiteral("C4")] = QPointF(229.0f, 324.0f); + m_paperSizesMap[QStringLiteral("C5")] = QPointF(162.0f, 229.0f); + m_paperSizesMap[QStringLiteral("C6")] = QPointF(114.0f, 162.0f); + + m_paperSizesMap[QStringLiteral("Letter")] = QPointF(216.0f, 279.0f); + m_paperSizesMap[QStringLiteral("Legal")] = QPointF(216.0f, 356.0f); + m_paperSizesMap[QStringLiteral("Tabloid")] = QPointF(279.0f, 432.0f); + m_paperSizesMap[QStringLiteral("Ledger")] = QPointF(432.0f, 279.0f); + + m_paperSizesMap[QStringLiteral("Junior Legal")] = QPointF(127.0f, 203.0f); + m_paperSizesMap[QStringLiteral("Half Letter")] = QPointF(140.0f, 216.0f); + m_paperSizesMap[QStringLiteral("Government Letter")] = QPointF(203.0f, 267.0f); + m_paperSizesMap[QStringLiteral("Government Legal")] = QPointF(216.0f, 330.0f); + + m_scanareaPapersize = new LabeledCombo(m_areaOptsTab, i18n("select paper size"), m_paperSizesMap.keys()); + + m_scanareaWidth = new LabeledFSlider(m_areaOptsTab, i18n("width (mm)"), 0.0f, 500.0f, 0.1f); + m_scanareaHeight = new LabeledFSlider(m_areaOptsTab, i18n("height (mm)"), 0.0f, 500.0f, 0.1f); + + m_scanareaX = new LabeledFSlider(m_areaOptsTab, i18n("x offset (mm)"), 0.0f, 500.0f, 0.1f); + m_scanareaY = new LabeledFSlider(m_areaOptsTab, i18n("y offset (mm)"), 0.0f, 500.0f, 0.1f); + + m_scanareaFlip = new QPushButton(i18n("flip width/height"), m_areaOptsTab); + m_scanareaEnableManual = new LabeledCheckbox(m_areaOptsTab, i18n("enable manual selection")); + + m_scanareaEnableManual->setChecked(false); + + m_scanareaPapersize->setCurrentText(defaultPaperSize); + m_scanareaWidth->setValue(m_paperSizesMap[defaultPaperSize].x()); + m_scanareaHeight->setValue(m_paperSizesMap[defaultPaperSize].y()); + m_scanareaX->setValue(0.0f); + m_scanareaY->setValue(0.0f); + + connect(m_scanareaFlip, &QPushButton::clicked, this, [this]() { + float tmp = this->m_scanareaWidth->value(); + this->m_scanareaWidth->setValue(this->m_scanareaHeight->value()); + this->m_scanareaHeight->setValue(tmp); + }); + + connect(m_scanareaPapersize, &LabeledCombo::activated_str, this, [this](const QString & selStr) { + QPointF selPoint = this->m_paperSizesMap[selStr]; + this->m_scanareaWidth->setValue(selPoint.x()); + this->m_scanareaHeight->setValue(selPoint.y()); + }); + + connect(m_scanareaWidth, SIGNAL(valueChanged(float)), SLOT(updateScanarea())); + connect(m_scanareaHeight, SIGNAL(valueChanged(float)), SLOT(updateScanarea())); + connect(m_scanareaX, SIGNAL(valueChanged(float)), SLOT(updateScanarea())); + connect(m_scanareaY, SIGNAL(valueChanged(float)), SLOT(updateScanarea())); + connect(m_scanareaEnableManual, SIGNAL(toggled(bool)), SLOT(updateScanarea())); + + area_layout->addWidget(m_scanareaPapersize); + area_layout->addWidget(m_scanareaWidth); + area_layout->addWidget(m_scanareaHeight); + + area_layout->addWidget(m_scanareaX); + area_layout->addWidget(m_scanareaY); + + area_layout->addWidget(m_scanareaFlip); + area_layout->addWidget(m_scanareaEnableManual); + + area_layout->addStretch(); + // calculate label widths int labelWidth = 0; KSaneOptionWidget *tmpOption; @@ -429,9 +516,9 @@ } } // Color Options - for (int i = 0; i < basic_layout->count(); ++i) { - if (basic_layout->itemAt(i) && basic_layout->itemAt(i)->widget()) { - tmpOption = qobject_cast(basic_layout->itemAt(i)->widget()); + for (int i = 0; i < color_lay->count(); ++i) { + if (color_lay->itemAt(i) && color_lay->itemAt(i)->widget()) { + tmpOption = qobject_cast(color_lay->itemAt(i)->widget()); if (tmpOption) { labelWidth = qMax(labelWidth, tmpOption->labelWidthHint()); } @@ -472,16 +559,70 @@ } } } + // Scan Area Options + labelWidth = 0; + for (int i = 0; i < area_layout->count(); ++i) { + if (area_layout->itemAt(i) && area_layout->itemAt(i)->widget()) { + tmpOption = qobject_cast(area_layout->itemAt(i)->widget()); + if (tmpOption) { + labelWidth = qMax(labelWidth, tmpOption->labelWidthHint()); + } + } + } + for (int i = 0; i < area_layout->count(); ++i) { + if (area_layout->itemAt(i) && area_layout->itemAt(i)->widget()) { + tmpOption = qobject_cast(area_layout->itemAt(i)->widget()); + if (tmpOption) { + tmpOption->setLabelWidth(labelWidth); + } + } + } // ensure that we do not get a scrollbar at the bottom of the option of the options int min_width = m_basicOptsTab->sizeHint().width(); if (min_width < m_otherOptsTab->sizeHint().width()) { min_width = m_otherOptsTab->sizeHint().width(); } + if (min_width < m_areaOptsTab->sizeHint().width()) { + min_width = m_areaOptsTab->sizeHint().width(); + } m_optsTabWidget->setMinimumWidth(min_width + m_basicScrollA->verticalScrollBar()->sizeHint().width() + 5); } +void KSaneWidgetPrivate::updateScanarea() +{ + if (m_scanareaEnableManual->isChecked()) + return; + + float w = m_scanareaWidth->value(); + float h = m_scanareaHeight->value(); + + float x1 = m_scanareaX->value(); + float y1 = m_scanareaY->value(); + + float x2 = x1 + w; + float y2 = y1 + h; + + float max_x = 0.0f; + float max_y = 0.0f; + m_optBrX->getMaxValue(max_x); + m_optBrY->getMaxValue(max_y); + + x1 = std::min(x1, max_x); + x2 = std::min(x2, max_x); + + y1 = std::min(y1, max_y); + y2 = std::min(y2, max_y); + + m_optTlX->setValue(x1); + m_optTlY->setValue(y1); + m_optBrX->setValue(x2); + m_optBrY->setValue(y2); + + m_previewViewer->setSelection(x1 / max_x, y1 / max_y, x2 / max_x, y2 / max_y); +} + void KSaneWidgetPrivate::setDefaultValues() { KSaneOption *option; @@ -535,6 +676,8 @@ m_optsTabWidget->setMinimumWidth(min_width + m_basicScrollA->verticalScrollBar()->sizeHint().width() + 5); + updateScanarea(); + m_previewViewer->zoom2Fit(); } @@ -547,6 +690,7 @@ m_optList.at(i)->readValue(); } + updateScanarea(); } void KSaneWidgetPrivate::handleSelection(float tl_x, float tl_y, float br_x, float br_y) @@ -752,7 +896,7 @@ // check if we can modify the selection if ((m_optTlX != nullptr) && (m_optTlY != nullptr) && - (m_optBrX != nullptr) && (m_optBrY != nullptr)) { + (m_optBrX != nullptr) && (m_optBrY != nullptr)) { // get maximums m_optBrX->getMaxValue(max_x); m_optBrY->getMaxValue(max_y); @@ -868,7 +1012,7 @@ m_previewViewer->zoom2Fit(); if ((m_previewThread->saneStatus() != SANE_STATUS_GOOD) && - (m_previewThread->saneStatus() != SANE_STATUS_EOF)) { + (m_previewThread->saneStatus() != SANE_STATUS_EOF)) { alertUser(KSaneWidget::ErrorGeneral, i18n(sane_strstatus(m_previewThread->saneStatus()))); } else if (m_autoSelect) { m_previewViewer->findSelections(); @@ -1103,8 +1247,8 @@ if ((source.contains(i18nc("This is compared to the option string returned by sane", "Transparency"), Qt::CaseInsensitive)) && - (filmtype.contains(i18nc("This is compared to the option string returned by sane", - "Negative"), Qt::CaseInsensitive))) { + (filmtype.contains(i18nc("This is compared to the option string returned by sane", + "Negative"), Qt::CaseInsensitive))) { m_invertColors->setChecked(true); } else { m_invertColors->setChecked(false); @@ -1152,7 +1296,7 @@ void KSaneWidgetPrivate::alertUser(int type, const QString &strStatus) { - if (q->receivers(SIGNAL(userMessage(int,QString))) == 0) { + if (q->receivers(SIGNAL(userMessage(int, QString))) == 0) { switch (type) { case KSaneWidget::ErrorGeneral: QMessageBox::critical(nullptr, i18nc("@title:window", "General Error"), strStatus); diff --git a/src/options/ksaneoption.cpp b/src/options/ksaneoption.cpp --- a/src/options/ksaneoption.cpp +++ b/src/options/ksaneoption.cpp @@ -95,8 +95,8 @@ } if (((m_optDesc->cap & SANE_CAP_SOFT_DETECT) == 0) || - (m_optDesc->cap & SANE_CAP_INACTIVE) || - ((m_optDesc->size == 0) && (optionType(m_optDesc) != TYPE_BUTTON))) { + (m_optDesc->cap & SANE_CAP_INACTIVE) || + ((m_optDesc->size == 0) && (optionType(m_optDesc) != TYPE_BUTTON))) { return STATE_HIDDEN; } else if ((m_optDesc->cap & SANE_CAP_SOFT_SELECT) == 0) { return STATE_DISABLED; @@ -317,9 +317,9 @@ } if ((strcmp(optDesc->name, SANE_NAME_GAMMA_VECTOR) == 0) || - (strcmp(optDesc->name, SANE_NAME_GAMMA_VECTOR_R) == 0) || - (strcmp(optDesc->name, SANE_NAME_GAMMA_VECTOR_G) == 0) || - (strcmp(optDesc->name, SANE_NAME_GAMMA_VECTOR_B) == 0)) { + (strcmp(optDesc->name, SANE_NAME_GAMMA_VECTOR_R) == 0) || + (strcmp(optDesc->name, SANE_NAME_GAMMA_VECTOR_G) == 0) || + (strcmp(optDesc->name, SANE_NAME_GAMMA_VECTOR_B) == 0)) { return TYPE_GAMMA; } qDebug() << "Can not handle:" << optDesc->title; diff --git a/src/widgets/labeledcombo.h b/src/widgets/labeledcombo.h --- a/src/widgets/labeledcombo.h +++ b/src/widgets/labeledcombo.h @@ -88,6 +88,7 @@ Q_SIGNALS: void activated(int); + void activated_str(const QString&); private: QComboBox *m_combo; diff --git a/src/widgets/labeledcombo.cpp b/src/widgets/labeledcombo.cpp --- a/src/widgets/labeledcombo.cpp +++ b/src/widgets/labeledcombo.cpp @@ -42,6 +42,7 @@ m_label->setBuddy(m_combo); connect(m_combo, QOverload::of(&QComboBox::activated), this, &LabeledCombo::activated); + connect(m_combo, QOverload::of(&QComboBox::activated), this, &LabeledCombo::activated_str); m_layout->addWidget(m_combo, 0, 1); m_layout->addWidget(new QWidget(this), 0, 2);