File indexing completed on 2025-01-19 12:59:21
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) 2008-2016 Jonathan Marten <jjm@keelhaul.me.uk> * 0007 * * 0008 * Kooka is free software; you can redistribute it and/or modify it * 0009 * under the terms of the GNU Library General Public License as * 0010 * published by the Free Software Foundation and appearing in the * 0011 * file COPYING included in the packaging of this file; either * 0012 * version 2 of the License, or (at your option) any later version. * 0013 * * 0014 * As a special exception, permission is given to link this program * 0015 * with any version of the KADMOS OCR/ICR engine (a product of * 0016 * reRecognition GmbH, Kreuzlingen), and distribute the resulting * 0017 * executable without including the source code for KADMOS in the * 0018 * source distribution. * 0019 * * 0020 * This program is distributed in the hope that it will be useful, * 0021 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0023 * GNU General Public License for more details. * 0024 * * 0025 * You should have received a copy of the GNU General Public * 0026 * License along with this program; see the file COPYING. If * 0027 * not, see <http://www.gnu.org/licenses/>. * 0028 * * 0029 ************************************************************************/ 0030 0031 #include "scansizeselector.h" 0032 0033 #include <qcombobox.h> 0034 #include <qradiobutton.h> 0035 #include <qbuttongroup.h> 0036 #include <qgridlayout.h> 0037 #ifdef HAVE_LIBPAPER 0038 #include <qvector.h> 0039 #endif 0040 0041 #include <klocalizedstring.h> 0042 0043 #ifdef HAVE_LIBPAPER 0044 extern "C" { 0045 #include <paper.h> 0046 } 0047 #endif 0048 0049 #include "libkookascan_logging.h" 0050 0051 0052 struct PaperSize { 0053 const char *name; // no I18N needed here? 0054 int width, height; // in portrait orientation 0055 }; 0056 0057 static const PaperSize defaultSizes[] = { 0058 { "ISO A3", 297, 420 }, 0059 { "ISO A4", 210, 297 }, 0060 { "ISO A5", 148, 210 }, 0061 { "ISO A6", 105, 148 }, 0062 { "US Letter", 215, 279 }, 0063 { "US Legal", 216, 356 }, 0064 { "9x13", 90, 130 }, 0065 { "10x15", 100, 150 }, 0066 { nullptr, 0, 0 } 0067 }; 0068 0069 static const PaperSize *sizes = nullptr; 0070 #ifdef HAVE_LIBPAPER 0071 static QVector<PaperSize> papers; 0072 #endif 0073 0074 ScanSizeSelector::ScanSizeSelector(QWidget *parent, const QSize &bedSize) 0075 : QFrame(parent) 0076 { 0077 qCDebug(LIBKOOKASCAN_LOG) << "bed size" << bedSize; 0078 0079 bedWidth = bedSize.width(); 0080 bedHeight = bedSize.height(); 0081 0082 if (sizes == nullptr) { // initialise paper data 0083 #ifdef HAVE_LIBPAPER 0084 paperinit(); // start to access library 0085 0086 PaperSize ps; 0087 for (const struct paper *p = paperfirst(); p != nullptr; p = papernext(p)) { 0088 // iterate over defined papers 0089 ////qCDebug(LIBKOOKASCAN_LOG) << "paper at" << ((void*) p) 0090 // << "name" << papername(p) 0091 // << "width" << paperpswidth(p) 0092 // << "height" << paperpsheight(p); 0093 0094 // The next line is safe, because a unique pointer (presumably 0095 // into libpaper's internally allocated data) is returned for 0096 // each paper size. 0097 ps.name = papername(p); 0098 // These sizes are in PostScript points (1/72 inch). 0099 ps.width = qRound(paperpswidth(p) / 72.0 * 25.4); 0100 ps.height = qRound(paperpsheight(p) / 72.0 * 25.4); 0101 papers.append(ps); 0102 } 0103 qCDebug(LIBKOOKASCAN_LOG) << "got" << papers.size() << "paper sizes from libpaper"; 0104 0105 ps.name = nullptr; // finish with null terminator 0106 papers.append(ps); 0107 0108 paperdone(); // finished accessing library 0109 sizes = papers.data(); // set pointer to data 0110 #else 0111 sizes = defaultSizes; // set pointer to defaults 0112 #endif 0113 } 0114 0115 QGridLayout *gl = new QGridLayout(this); 0116 gl->setMargin(0); 0117 0118 m_sizeCb = new QComboBox(this); 0119 m_sizeCb->setToolTip(i18n("Set the size of the scanned area")); 0120 connect(m_sizeCb, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &ScanSizeSelector::slotSizeSelected); 0121 setFocusProxy(m_sizeCb); 0122 gl->addWidget(m_sizeCb, 0, 0, 1, -1); 0123 0124 m_sizeCb->addItem(i18n("Full size")); // index 0 0125 m_sizeCb->addItem(i18n("(No selection)")); // index 1 0126 for (int i = 0; sizes[i].name != nullptr; ++i) { // index 2 and upwards 0127 // only those that will fit 0128 if (sizes[i].width > bedWidth || sizes[i].height > bedHeight) { 0129 continue; 0130 } 0131 m_sizeCb->addItem(sizes[i].name); 0132 } 0133 m_sizeCb->setCurrentIndex(0); 0134 0135 QButtonGroup *bg = new QButtonGroup(this); 0136 bg->setExclusive(true); 0137 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) 0138 connect(bg, &QButtonGroup::idClicked, this, &ScanSizeSelector::slotPortraitLandscape); 0139 #else 0140 connect(bg, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &ScanSizeSelector::slotPortraitLandscape); 0141 #endif 0142 0143 m_portraitRb = new QRadioButton(i18n("Portrait"), this); 0144 m_portraitRb->setEnabled(false); 0145 bg->addButton(m_portraitRb); 0146 gl->addWidget(m_portraitRb, 1, 0); 0147 0148 m_landscapeRb = new QRadioButton(i18n("Landscape"), this); 0149 m_landscapeRb->setEnabled(false); 0150 bg->addButton(m_landscapeRb); 0151 gl->addWidget(m_landscapeRb, 1, 2); 0152 0153 gl->setColumnMinimumWidth(1, 8); 0154 gl->setColumnStretch(3, 1); 0155 0156 m_customSize = QRect(); 0157 m_prevSelected = m_sizeCb->currentIndex(); 0158 0159 setToolTip(i18n("Set the orientation for a preset size of the scanned area")); 0160 } 0161 0162 ScanSizeSelector::~ScanSizeSelector() 0163 { 0164 } 0165 0166 static const PaperSize *findPaperSize(const QString &sizename) 0167 { 0168 for (int i = 0; sizes[i].name != nullptr; ++i) { // search for that size 0169 if (sizename == sizes[i].name) { 0170 return (&sizes[i]); 0171 } 0172 } 0173 0174 return (nullptr); 0175 } 0176 0177 void ScanSizeSelector::implementPortraitLandscape(const PaperSize *sp) 0178 { 0179 m_portraitRb->setEnabled(sp->width <= bedWidth && sp->height <= bedHeight); 0180 m_landscapeRb->setEnabled(sp->width <= bedHeight && sp->height <= bedWidth); 0181 0182 if (!m_portraitRb->isChecked() && !m_landscapeRb->isChecked()) { 0183 m_portraitRb->setChecked(true); 0184 } 0185 0186 if (m_portraitRb->isChecked() && !m_portraitRb->isEnabled()) { 0187 m_landscapeRb->setChecked(m_landscapeRb->isEnabled()); 0188 } 0189 if (m_landscapeRb->isChecked() && !m_landscapeRb->isEnabled()) { 0190 m_portraitRb->setChecked(m_portraitRb->isEnabled()); 0191 } 0192 } 0193 0194 void ScanSizeSelector::implementSizeSetting(const PaperSize *sp) 0195 { 0196 for (int i = 2; i < m_sizeCb->count(); ++i) { // search combo box by name 0197 if (m_sizeCb->itemText(i) == sp->name) { 0198 m_sizeCb->setCurrentIndex(i); // set combo box selection 0199 break; 0200 } 0201 } 0202 0203 implementPortraitLandscape(sp); // set up radio buttons 0204 } 0205 0206 void ScanSizeSelector::newScanSize(int width, int height) 0207 { 0208 if (m_portraitRb->isChecked()) { 0209 emit sizeSelected(QRect(0, 0, width, height)); 0210 } else if (m_landscapeRb->isChecked()) { 0211 emit sizeSelected(QRect(0, 0, height, width)); 0212 } else { 0213 //qCDebug(LIBKOOKASCAN_LOG) << "no orientation appropriate!"; 0214 } 0215 } 0216 0217 void ScanSizeSelector::slotPortraitLandscape(int id) 0218 { 0219 int idx = m_sizeCb->currentIndex(); 0220 if (idx < 2) { 0221 return; // "Full size" or "Custom" 0222 } 0223 0224 const PaperSize *sp = findPaperSize(m_sizeCb->itemText(idx)); 0225 if (sp == nullptr) { 0226 return; 0227 } 0228 newScanSize(sp->width, sp->height); 0229 } 0230 0231 void ScanSizeSelector::slotSizeSelected(int idx) 0232 { 0233 if (idx == 0) { // Full size 0234 m_portraitRb->setEnabled(false); 0235 m_landscapeRb->setEnabled(false); 0236 emit sizeSelected(QRect()); 0237 m_prevSelected = idx; 0238 } else if (idx == 1) { // Custom 0239 if (!m_customSize.isValid()) { // is there a saved custom size? 0240 m_sizeCb->setCurrentIndex(m_prevSelected); // no, snap back to previous 0241 } else { 0242 selectCustomSize(m_customSize); 0243 emit sizeSelected(m_customSize); 0244 } 0245 } else { // Named size 0246 const PaperSize *sp = findPaperSize(m_sizeCb->itemText(idx)); 0247 if (sp == nullptr) { 0248 return; 0249 } 0250 0251 implementPortraitLandscape(sp); 0252 newScanSize(sp->width, sp->height); 0253 m_prevSelected = idx; 0254 } 0255 } 0256 0257 void ScanSizeSelector::selectCustomSize(const QRect &rect) 0258 { 0259 m_portraitRb->setEnabled(false); 0260 m_landscapeRb->setEnabled(false); 0261 0262 if (!rect.isValid()) { 0263 m_sizeCb->setCurrentIndex(0); // "Full Size" 0264 } else { 0265 m_customSize = rect; // remember the custom size 0266 m_sizeCb->setItemText(1, i18n("Selection")); // now can use this option 0267 m_sizeCb->setCurrentIndex(1); // "Custom" 0268 } 0269 } 0270 0271 void ScanSizeSelector::selectSize(const QRect &rect) 0272 { 0273 //qCDebug(LIBKOOKASCAN_LOG) << "rect" << rect << "valid" << rect.isValid(); 0274 0275 bool found = false; 0276 0277 // Originally the test was: 0278 // 0279 // if (rect.isValid() && rect.left()==0 && rect.top()==0) 0280 // 0281 // but this one allows the size to be detected even if the rectangle 0282 // is moved around (assuming that the size reported is 100% accurate). 0283 if (rect.isValid()) { 0284 // can this be a preset size? 0285 for (int i = 0; sizes[i].name != nullptr; ++i) { // search for preset that size 0286 if (sizes[i].width == rect.width() && sizes[i].height == rect.height()) { 0287 m_portraitRb->setChecked(true); 0288 m_landscapeRb->setChecked(false); 0289 implementSizeSetting(&sizes[i]); // set up controls 0290 found = true; 0291 break; 0292 } else if (sizes[i].width == rect.height() && sizes[i].height == rect.width()) { 0293 m_portraitRb->setChecked(false); 0294 m_landscapeRb->setChecked(true); 0295 implementSizeSetting(&sizes[i]); 0296 found = true; 0297 break; 0298 } 0299 } 0300 } 0301 0302 if (!found) { 0303 selectCustomSize(rect); 0304 } 0305 }