File indexing completed on 2024-04-21 15:12:14

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 }