KSaneCore

pagesizeoption.cpp
1/*
2 * SPDX-FileCopyrightText: 2009 Kare Sars <kare dot sars at iki dot fi>
3 * SPDX-FileCopyrightText: 2014 Gregor Mitsch : port to KDE5 frameworks
4 * SPDX-FileCopyrightText: 2021 Alexander Stippich <a.stippich@gmx.net>
5 *
6 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7 */
8
9#include "pagesizeoption.h"
10
11#include <QPageSize>
12
13#include <ksanecore_debug.h>
14
15static constexpr int PageSizeWiggleRoom = 2; // in mm
16
17namespace KSaneCore
18{
19
20PageSizeOption::PageSizeOption(BaseOption *optionTopLeftX,
21 BaseOption *optionTopLeftY,
22 BaseOption *optionBottomRightX,
23 BaseOption *optionBottomRightY,
24 BaseOption *optionResolution,
25 BaseOption *optionPageWidth,
26 BaseOption *optionPageHeight)
27 : BaseOption()
28{
29 if (optionTopLeftX == nullptr || optionTopLeftY == nullptr || optionBottomRightX == nullptr || optionBottomRightY == nullptr) {
30 m_optionType = Option::TypeDetectFail;
31 return;
32 }
33
34 connect(optionTopLeftX, &BaseOption::valueChanged, this, &PageSizeOption::optionTopLeftXUpdated);
35 connect(optionTopLeftY, &BaseOption::valueChanged, this, &PageSizeOption::optionTopLeftYUpdated);
36 connect(optionBottomRightX, &BaseOption::valueChanged, this, &PageSizeOption::optionBottomRightXUpdated);
37 connect(optionBottomRightY, &BaseOption::valueChanged, this, &PageSizeOption::optionBottomRightYUpdated);
38
39 m_optionTopLeftX = optionTopLeftX;
40 m_optionTopLeftY = optionTopLeftY;
41 m_optionBottomRightX = optionBottomRightX;
42 m_optionBottomRightY = optionBottomRightY;
43 m_optionResolution = optionResolution;
44 m_optionPageWidth = optionPageWidth;
45 m_optionPageHeight = optionPageHeight;
46
47 m_optionType = Option::TypeValueList;
48
49 computePageSizes();
50}
51
52bool PageSizeOption::setValue(const QVariant &value)
53{
54 if (value.userType() == QMetaType::QString) {
55 QString newValue = value.toString();
56 if (newValue == m_availableSizes.at(m_currentIndex).name) {
57 return true;
58 }
59 for (int i = 0; i < m_availableSizes.size(); i++) {
60 QString sizeEntry = m_availableSizes.at(i).name;
61 if (sizeEntry == newValue) {
62 m_currentIndex = i;
63
64 if (i != 0) {
65 const auto size = m_availableSizes.at(i).pageSize;
66 if (m_optionPageWidth != nullptr && m_optionPageHeight != nullptr) {
67 m_optionPageWidth->setValue(size.width());
68 m_optionPageHeight->setValue(size.height());
69 }
70 m_optionTopLeftX->setValue(0);
71 m_optionTopLeftY->setValue(0);
72 m_optionBottomRightX->setValue(size.width());
73 m_optionBottomRightY->setValue(size.height());
74 }
75 Q_EMIT valueChanged(sizeEntry);
76 return true;
77 }
78 }
79 }
80 return false;
81}
82
83QVariant PageSizeOption::value() const
84{
85 if (m_currentIndex >= 0 && m_currentIndex < m_availableSizes.size()) {
86 return m_availableSizes.at(m_currentIndex).name;
87 } else {
88 return QVariant();
89 }
90}
91
92QString PageSizeOption::valueAsString() const
93{
94 if (m_currentIndex >= 0 && m_currentIndex < m_availableSizes.size()) {
95 return m_availableSizes.at(m_currentIndex).name;
96 } else {
97 return QString();
98 }
99}
100
101QVariantList PageSizeOption::valueList() const
102{
103 QVariantList list;
104 list.reserve(m_availableSizes.size());
105 for (int i = 0; i < m_availableSizes.size(); i++) {
106 list << m_availableSizes.at(i).name;
107 }
108 return list;
109}
110
111QVariantList PageSizeOption::internalValueList() const
112{
113 QVariantList list;
114 list.reserve(m_availableSizes.size());
115 for (int i = 0; i < m_availableSizes.size(); i++) {
116 list << m_availableSizes.at(i).name;
117 }
118 return list;
119}
120
121Option::OptionState PageSizeOption::state() const
122{
123 return m_state;
124}
125
126QString PageSizeOption::name() const
127{
128 return PageSizeOptionName;
129}
130
131QString PageSizeOption::title() const
132{
133 return i18n("Scan area size");
134}
135
136QString PageSizeOption::description() const
137{
138 return i18n("Select a predefined page size for the scanning area.");
139}
140
141void PageSizeOption::optionTopLeftXUpdated()
142{
143 if (m_currentIndex > 0 && m_currentIndex < m_availableSizes.size() && m_optionTopLeftX->value().toDouble() != 0) {
144 m_currentIndex = 0;
145 Q_EMIT valueChanged(QPageSize::name(QPageSize::Custom));
146 }
147}
148
149void PageSizeOption::optionTopLeftYUpdated()
150{
151 if (m_currentIndex > 0 && m_currentIndex < m_availableSizes.size() && m_optionTopLeftY->value().toDouble() != 0) {
152 m_currentIndex = 0;
153 Q_EMIT valueChanged(QPageSize::name(QPageSize::Custom));
154 }
155}
156
157void PageSizeOption::optionBottomRightXUpdated()
158{
159 if (m_currentIndex > 0 && m_currentIndex < m_availableSizes.size()
160 && !qFuzzyCompare(ensureMilliMeter(m_optionBottomRightX, m_optionBottomRightX->value().toDouble()),
161 (m_availableSizes.at(m_currentIndex).pageSize.width() + m_availableSizes.at(m_currentIndex).wiggleRoom.width()))) {
162 m_currentIndex = 0;
163 Q_EMIT valueChanged(QPageSize::name(QPageSize::Custom));
164 }
165}
166
167void PageSizeOption::optionBottomRightYUpdated()
168{
169 if (m_currentIndex > 0 && m_currentIndex < m_availableSizes.size()
170 && !qFuzzyCompare(ensureMilliMeter(m_optionBottomRightY, m_optionBottomRightY->value().toDouble()),
171 (m_availableSizes.at(m_currentIndex).pageSize.height() + m_availableSizes.at(m_currentIndex).wiggleRoom.width()))) {
172 m_currentIndex = 0;
173 Q_EMIT valueChanged(QPageSize::name(QPageSize::Custom));
174 }
175}
176
177double PageSizeOption::ensureMilliMeter(BaseOption *option, double value)
178{
179 // convert if necessary with current DPI if available
180 if (option->valueUnit() == Option::UnitPixel && m_optionResolution != nullptr) {
181 double dpi = m_optionResolution->value().toDouble();
182 if (dpi > 1) {
183 return value / (dpi / 25.4);
184 }
185 }
186 return value;
187}
188
189void PageSizeOption::storeOptions()
190{
191 m_previousCoordinates[0] = m_optionTopLeftX->value().toDouble();
192 m_previousCoordinates[1] = m_optionTopLeftY->value().toDouble();
193 m_previousCoordinates[2] = m_optionBottomRightX->value().toDouble();
194 m_previousCoordinates[3] = m_optionBottomRightY->value().toDouble();
195}
196
197void PageSizeOption::restoreOptions()
198{
199 computePageSizes();
200 Q_EMIT optionReloaded();
201
202 m_optionTopLeftX->setValue(m_previousCoordinates[0]);
203 m_optionTopLeftY->setValue(m_previousCoordinates[1]);
204 m_optionBottomRightX->setValue(m_previousCoordinates[2]);
205 m_optionBottomRightY->setValue(m_previousCoordinates[3]);
206
207 int newIndex = 0;
208 if (m_optionTopLeftX->value().toDouble() == 0 && m_optionTopLeftY->value().toDouble() == 0) {
209 QSizeF currentSize = QSizeF(ensureMilliMeter(m_optionBottomRightX, m_optionBottomRightX->value().toDouble()),
210 ensureMilliMeter(m_optionBottomRightY, m_optionBottomRightY->value().toDouble()));
211
212 for (int i = 0; i < m_availableSizes.count(); i++) {
213 if (qFuzzyCompare(currentSize.height(), m_availableSizes.at(i).pageSize.height())
214 && qFuzzyCompare(currentSize.width(), m_availableSizes.at(i).pageSize.width())) {
215 newIndex = i;
216 break;
217 }
218 }
219 }
220 if (newIndex != m_currentIndex) {
221 m_currentIndex = newIndex;
222 Q_EMIT valueChanged(m_availableSizes.at(m_currentIndex).name);
223 }
224}
225
226void PageSizeOption::computePageSizes()
227{
228 /* some SANE backends set the maximum value of bottom right X and Y to the current page width and height values
229 * set current values of these option to maximum if available, such that we detect possible page sizes correctly
230 * see https://gitlab.com/sane-project/backends/-/issues/730 and https://bugs.kde.org/show_bug.cgi?id=476838 */
231 if (m_optionPageWidth != nullptr && m_optionPageHeight != nullptr) {
232 m_optionPageHeight->storeCurrentData();
233 m_optionPageWidth->storeCurrentData();
234 m_optionPageHeight->setValue(m_optionPageHeight->maximumValue());
235 m_optionPageWidth->setValue(m_optionPageWidth->maximumValue());
236 }
237
238 static const QList<QPageSize::PageSizeId> possibleSizesList = {
242 };
243 m_availableSizes.clear();
245
246 double maxScannerWidth = ensureMilliMeter(m_optionBottomRightX, m_optionBottomRightX->maximumValue().toDouble());
247 double maxScannerHeight = ensureMilliMeter(m_optionBottomRightY, m_optionBottomRightY->maximumValue().toDouble());
248
249 // Add portrait page sizes
250 for (const auto sizeCode : possibleSizesList) {
252 if (size.width() - PageSizeWiggleRoom > maxScannerWidth) {
253 continue;
254 }
255 if (size.height() - PageSizeWiggleRoom > maxScannerHeight) {
256 continue;
257 }
258 m_availableSizes.append(
259 {QPageSize::name(sizeCode), size, QSizeF(qMin(maxScannerWidth - size.width(), 0.0), qMin(maxScannerHeight - size.height(), 0.0))});
260 }
261
262 // Add landscape page sizes
263 for (const auto sizeCode : possibleSizesList) {
265 size.transpose();
266 if (size.width() - PageSizeWiggleRoom > maxScannerWidth) {
267 continue;
268 }
269 if (size.height() - PageSizeWiggleRoom > maxScannerHeight) {
270 continue;
271 }
272 m_availableSizes.append({i18nc("Page size landscape", "Landscape %1", QPageSize::name(sizeCode)),
273 size,
274 QSizeF(qMin(maxScannerWidth - size.width(), 0.0), qMin(maxScannerHeight - size.height(), 0.0))});
275 }
276
277 // Set custom as current
278 m_currentIndex = 0;
279 if (m_availableSizes.count() > 1) {
280 m_state = Option::StateActive;
281 } else {
282 m_state = Option::StateHidden;
283 }
284
285 if (m_optionPageWidth != nullptr && m_optionPageHeight != nullptr) {
286 m_optionPageHeight->restoreSavedData();
287 m_optionPageWidth->restoreSavedData();
288 }
289}
290
291} // namespace KSaneCore
292
293#include "moc_pagesizeoption.cpp"
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
const_reference at(qsizetype i) const const
void clear()
void reserve(qsizetype size)
QString name() const const
QString name(PageSizeId pageSizeId)
QSizeF size(PageSizeId pageSizeId, Unit units)
qreal height() const const
void transpose()
qreal width() const const
const QChar at(qsizetype position) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QString toString() const const
int userType() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri May 2 2025 12:03:20 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.