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  * SPDX-FileCopyrightText: 2021 Alexander Stippich <a.stippich@gmx.net>
6  *
7  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
8  *
9  * ============================================================ */
10 
11 #include "ksanepagesizeoption.h"
12 
13 #include <QPageSize>
14 #include <QSizeF>
15 
16 #include <ksane_debug.h>
17 
18 static constexpr int PageSizeWiggleRoom = 2; // in mm
19 
20 namespace KSaneIface
21 {
22 
KSanePageSizeOption(KSaneBaseOption * optionTopLeftX,KSaneBaseOption * optionTopLeftY,KSaneBaseOption * optionBottomRightX,KSaneBaseOption * optionBottomRightY,KSaneBaseOption * optionResolution)23 KSanePageSizeOption::KSanePageSizeOption(KSaneBaseOption *optionTopLeftX, KSaneBaseOption *optionTopLeftY,
24                         KSaneBaseOption *optionBottomRightX, KSaneBaseOption *optionBottomRightY,
25                         KSaneBaseOption *optionResolution) : KSaneBaseOption()
26 {
27     if (optionTopLeftX == nullptr || optionTopLeftY == nullptr ||
28         optionBottomRightX == nullptr || optionBottomRightY == nullptr) {
29 
30         m_optionType = KSaneOption::TypeDetectFail;
31         return;
32     }
33 
34     connect(optionTopLeftX, &KSaneBaseOption::valueChanged, this, &KSanePageSizeOption::optionTopLeftXUpdated);
35     connect(optionTopLeftY, &KSaneBaseOption::valueChanged, this, &KSanePageSizeOption::optionTopLeftYUpdated);
36     connect(optionBottomRightX, &KSaneBaseOption::valueChanged, this, &KSanePageSizeOption::optionBottomRightXUpdated);
37     connect(optionBottomRightY, &KSaneBaseOption::valueChanged, this, &KSanePageSizeOption::optionBottomRightYUpdated);
38 
39     m_optionTopLeftX = optionTopLeftX;
40     m_optionTopLeftY = optionTopLeftY;
41     m_optionBottomRightX = optionBottomRightX;
42     m_optionBottomRightY = optionBottomRightY;
43     m_optionResolution = optionResolution;
44 
45     const QList<QPageSize::PageSizeId> possibleSizesList = {
46         QPageSize::A3,
47         QPageSize::A4,
48         QPageSize::A5,
49         QPageSize::A6,
50         QPageSize::Letter,
51         QPageSize::Legal,
52         QPageSize::Tabloid,
53         QPageSize::B3,
54         QPageSize::B4,
55         QPageSize::B5,
56         QPageSize::B6,
57         QPageSize::C5E,
58         QPageSize::Comm10E,
59         QPageSize::DLE,
60         QPageSize::Executive,
61         QPageSize::Folio,
62         QPageSize::Ledger,
63         QPageSize::JisB3,
64         QPageSize::JisB4,
65         QPageSize::JisB5,
66         QPageSize::JisB6,
67     };
68 
69     m_availableSizesList << QPageSize::size(QPageSize::Custom, QPageSize::Millimeter);
70     m_availableSizesListNames << QPageSize::name(QPageSize::Custom);
71 
72     double maxScannerWidth = ensureMilliMeter(m_optionBottomRightX, m_optionBottomRightX->maximumValue().toDouble());
73     double maxScannerHeight = ensureMilliMeter(m_optionBottomRightY, m_optionBottomRightY->maximumValue().toDouble());
74 
75     // Add portrait page sizes
76     for (const auto sizeCode : possibleSizesList) {
77         QSizeF size = QPageSize::size(sizeCode, QPageSize::Millimeter);
78         if (size.width() - PageSizeWiggleRoom > maxScannerWidth) {
79             continue;
80         }
81         if (size.height() - PageSizeWiggleRoom > maxScannerHeight) {
82             continue;
83         }
84         m_availableSizesList << size;
85         m_availableSizesListNames << QPageSize::name(sizeCode);
86     }
87 
88     // Add landscape page sizes
89     for (const auto sizeCode : possibleSizesList) {
90         QSizeF size = QPageSize::size(sizeCode, QPageSize::Millimeter);
91         size.transpose();
92         if (size.width() - PageSizeWiggleRoom > maxScannerWidth) {
93             continue;
94         }
95         if (size.height() - PageSizeWiggleRoom > maxScannerHeight) {
96             continue;
97         }
98         m_availableSizesList << size;
99         m_availableSizesListNames << i18nc("Page size landscape", "Landscape %1", QPageSize::name(sizeCode));
100     }
101 
102     // Set custom as current
103     m_currentIndex = 0;
104     if (m_availableSizesList.count() > 1) {
105         m_state = KSaneOption::StateActive;
106     } else {
107         m_state = KSaneOption::StateHidden;
108     }
109     m_optionType = KSaneOption::TypeValueList;
110 }
111 
setValue(const QVariant & value)112 bool KSanePageSizeOption::setValue(const QVariant &value)
113 {
114     if (static_cast<QMetaType::Type>(value.type()) == QMetaType::QString) {
115         QString newValue = value.toString();
116         if (newValue == m_availableSizesListNames.at(m_currentIndex)) {
117             return true;
118         }
119         for (int i = 0; i < m_availableSizesListNames.size(); i++) {
120             QString sizeEntry = m_availableSizesListNames.at(i).toString();
121             if (sizeEntry == newValue) {
122                 m_currentIndex = i;
123 
124                 if (i != 0) {
125                     const auto size = m_availableSizesList.at(i);
126                     m_optionTopLeftX->setValue(0);
127                     m_optionTopLeftY->setValue(0);
128                     m_optionBottomRightX->setValue(size.width());
129                     m_optionBottomRightY->setValue(size.height());
130                 }
131                 Q_EMIT valueChanged(sizeEntry);
132                 return true;
133             }
134         }
135     }
136     return false;
137 }
138 
value() const139 QVariant KSanePageSizeOption::value() const
140 {
141     if (m_currentIndex >= 0 && m_currentIndex < m_availableSizesListNames.size()) {
142         return m_availableSizesListNames.at(m_currentIndex);
143     } else {
144         return QVariant();
145     }
146 }
147 
valueAsString() const148 QString KSanePageSizeOption::valueAsString() const
149 {
150     if (m_currentIndex >= 0 && m_currentIndex < m_availableSizesListNames.size()) {
151         return m_availableSizesListNames.at(m_currentIndex).toString();
152     } else {
153         return QString();
154     }
155 }
156 
valueList() const157 QVariantList KSanePageSizeOption::valueList() const
158 {
159     return m_availableSizesListNames;
160 }
161 
state() const162 KSaneOption::KSaneOptionState KSanePageSizeOption::state() const
163 {
164     return m_state;
165 }
166 
name() const167 QString KSanePageSizeOption::name() const
168 {
169     return PageSizeOptionName;
170 }
171 
title() const172 QString KSanePageSizeOption::title() const
173 {
174     return i18n("Scan area size");
175 }
176 
description() const177 QString KSanePageSizeOption::description() const
178 {
179     return i18n("Select a predefined page size for the scanning area.");
180 }
181 
optionTopLeftXUpdated()182 void KSanePageSizeOption::optionTopLeftXUpdated()
183 {
184     if (m_currentIndex > 0 && m_currentIndex < m_availableSizesList.size()
185         && m_optionTopLeftY->value().toDouble() != 0 ) {
186         m_currentIndex = 0;
187         Q_EMIT valueChanged(QPageSize::name(QPageSize::Custom));
188     }
189 }
190 
optionTopLeftYUpdated()191 void KSanePageSizeOption::optionTopLeftYUpdated()
192 {
193     if (m_currentIndex > 0 && m_currentIndex < m_availableSizesList.size()
194         && m_optionTopLeftY->value().toDouble() != 0 ) {
195         m_currentIndex = 0;
196         Q_EMIT valueChanged(QPageSize::name(QPageSize::Custom));
197     }
198 }
199 
optionBottomRightXUpdated()200 void KSanePageSizeOption::optionBottomRightXUpdated()
201 {
202     if (m_currentIndex > 0 && m_currentIndex < m_availableSizesList.size()
203         && ensureMilliMeter(m_optionBottomRightX, m_optionBottomRightX->value().toDouble())!= m_availableSizesList.at(m_currentIndex).width() ) {
204         m_currentIndex = 0;
205         Q_EMIT valueChanged(QPageSize::name(QPageSize::Custom));
206     }
207 }
208 
optionBottomRightYUpdated()209 void KSanePageSizeOption::optionBottomRightYUpdated()
210 {
211     if (m_currentIndex > 0 && m_currentIndex < m_availableSizesList.size()
212         && ensureMilliMeter(m_optionBottomRightY, m_optionBottomRightY->value().toDouble()) != m_availableSizesList.at(m_currentIndex).height() ) {
213         m_currentIndex = 0;
214         Q_EMIT valueChanged(QPageSize::name(QPageSize::Custom));
215     }
216 }
217 
ensureMilliMeter(KSaneBaseOption * option,double value)218 double KSanePageSizeOption::ensureMilliMeter(KSaneBaseOption *option, double value)
219 {
220     // convert if necessary with current DPI if available
221     if (option->valueUnit() == KSaneOption::UnitPixel &&  m_optionResolution != nullptr) {
222         double dpi = m_optionResolution->value().toDouble();
223         if (dpi > 1) {
224             return value / (dpi / 25.4);
225         }
226     }
227     return value;
228 }
229 
230 }  // NameSpace KSaneIface
231