diff --git a/app/photocopyprintdialogpage.cpp b/app/photocopyprintdialogpage.cpp index 3ebc04c..36b1ab3 100644 --- a/app/photocopyprintdialogpage.cpp +++ b/app/photocopyprintdialogpage.cpp @@ -1,166 +1,166 @@ /* This file is part of the KDE Project Copyright (C) 2008 Alex Kempshall This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "photocopyprintdialogpage.h" #include #include #include #include #include #include #include #include #include #include #include #include extern "C" { #include } #include "kscanoption.h" #include "kookaimage.h" #define ID_SCREEN 0 #define ID_ORIG 1 #define ID_CUSTOM 2 #define ID_FIT_PAGE 3 PhotoCopyPrintDialogPage::PhotoCopyPrintDialogPage(KScanDevice *newScanDevice) #ifndef KDE4 : KPrintDialogPage() #else : QWidget() #endif { //qDebug(); #ifndef KDE4 setTitle(i18n("PhotoCopier")); #endif sane_device = newScanDevice; QVBoxLayout *leftVBox = new QVBoxLayout(this); Q3HGroupBox *prtgroup1 = new Q3HGroupBox(i18n("Print options"), this); /* Allow for number of Copies */ m_copies = new KIntNumInput(prtgroup1); m_copies->setLabel(i18n("Copies: "), Qt::AlignVCenter); m_copies->setValue(1); m_copies->setMinimum(1); // TODO: is this correct? maximum number of copies 1? m_copies->setMaximum(1); QHBoxLayout *prtHBox = new QHBoxLayout(this); prtHBox->addWidget(prtgroup1); // QWidget *prtSpaceEater = new QWidget( prtgroup1 ); // prtSpaceEater->setSizePolicy( QSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored )); // prtHBox->addWidget( prtSpaceEater ); prtHBox->addStretch(1); // Scanner options QString str = i18n("Scanner Settings: %1", sane_device->scannerDescription()); Q3VGroupBox *scangroup1 = new Q3VGroupBox(str, this); str = i18n("Scan size:"); QString strBR_X; QString strBR_Y; KScanOption *res = sane_device->getOption(SANE_NAME_SCAN_BR_X, true); - if (res != NULL) { + if (res != nullptr) { strBR_X = res->get(); } res = sane_device->getOption(SANE_NAME_SCAN_BR_Y, true); - if (res != NULL) { + if (res != nullptr) { strBR_Y = res->get(); } QString str1 = strBR_Y + "x" + strBR_X; //if (strBR_X == "215" && strBR_Y == "295") str1 ="Letter"; //if (strBR_X == "215" && strBR_Y == "297") str1 ="A4"; //if (strBR_X == "151" && strBR_Y == "211") str1 ="A5"; //if (strBR_X == "107" && strBR_Y == "148") str1 ="A6"; constructLabel(scangroup1, "Scan Size", &str1); constructLabel(scangroup1, "Scan Mode", SANE_NAME_SCAN_MODE); constructLabel(scangroup1, "Resolution", SANE_NAME_SCAN_RESOLUTION); constructLabel(scangroup1, "Brightness", SANE_NAME_BRIGHTNESS); constructLabel(scangroup1, "Contrast", SANE_NAME_CONTRAST); QHBoxLayout *scanHBox = new QHBoxLayout(); scanHBox->addWidget(scangroup1); scanHBox->addStretch(1); leftVBox->addLayout(scanHBox); leftVBox->addLayout(prtHBox); } PhotoCopyPrintDialogPage::~PhotoCopyPrintDialogPage() { //qDebug(); } void PhotoCopyPrintDialogPage::setOptions(const QMap &opts) { //qDebug(); } void PhotoCopyPrintDialogPage::getOptions(QMap &opts, bool) { //qDebug(); } bool PhotoCopyPrintDialogPage::isValid(QString &msg) { return (true); } QLabel *PhotoCopyPrintDialogPage::constructLabel(Q3VGroupBox *group, const char *strTitle, const QByteArray &strSaneOption) { KScanOption *res = sane_device->getOption(strSaneOption, true); - QString str = i18n(strTitle) + ": " + "\t" + (res != NULL ? res->get() : "?"); + QString str = i18n(strTitle) + ": " + "\t" + (res != nullptr ? res->get() : "?"); QLabel *lbl = new QLabel(group); lbl->setText(str); return (lbl); } // The unusual 'const QString *' parameter is here so that this function can // be overloaded with the previous. If this were 'const QString &' then the // overload would be ambiguous (because there is a conversion from QByteArray // to QString). QLabel *PhotoCopyPrintDialogPage::constructLabel(Q3VGroupBox *group, const char *strTitle, const QString *strContents) { QString str = i18n(strTitle) + ": " + "\t" + (*strContents); QLabel *lbl = new QLabel(group); lbl->setText(str); return (lbl); } diff --git a/libkookascan/kscandevice.cpp b/libkookascan/kscandevice.cpp index 06b5d9a..ea5e110 100644 --- a/libkookascan/kscandevice.cpp +++ b/libkookascan/kscandevice.cpp @@ -1,1525 +1,1525 @@ /************************************************************************ * * * This file is part of Kooka, a scanning/OCR application using * * Qt and KDE Frameworks . * * * * Copyright (C) 1999-2016 Klaas Freitag * * Jonathan Marten * * * * Kooka is free software; you can redistribute it and/or modify it * * under the terms of the GNU Library General Public License as * * published by the Free Software Foundation and appearing in the * * file COPYING included in the packaging of this file; either * * version 2 of the License, or (at your option) any later version. * * * * As a special exception, permission is given to link this program * * with any version of the KADMOS OCR/ICR engine (a product of * * reRecognition GmbH, Kreuzlingen), and distribute the resulting * * executable without including the source code for KADMOS in the * * source distribution. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public * * License along with this program; see the file COPYING. If * * not, see . * * * ************************************************************************/ #include "kscandevice.h" #include #include #include #include #include #include #include #include #include #include "scanglobal.h" #include "scandevices.h" #include "kgammatable.h" #include "kscancontrols.h" #include "kscanoption.h" #include "kscanoptset.h" #include "deviceselector.h" #include "imagemetainfo.h" #include "scansettings.h" extern "C" { #include } #define MIN_PREVIEW_DPI 75 #define MAX_PROGRESS 100 // Debugging options #define DEBUG_OPTIONS #undef DEBUG_RELOAD #undef DEBUG_CREATE #define DEBUG_PARAMS #ifdef DEBUG_OPTIONS #include #endif // DEBUG_OPTIONS // Accessing GUI options // --------------------- // Used only by ScanParams::slotVirtScanModeSelect() void KScanDevice::guiSetEnabled(const QByteArray &name, bool state) { KScanOption *so = getExistingGuiElement(name); - if (so==NULL) return; + if (so==nullptr) return; QWidget *w = so->widget(); - if (w==NULL) return; + if (w==nullptr) return; w->setEnabled(state && so->isSoftwareSettable()); } KScanOption *KScanDevice::getOption(const QByteArray &name, bool create) { QByteArray alias = aliasName(name); if (mCreatedOptions.contains(alias)) { #ifdef DEBUG_CREATE qDebug() << "already exists" << alias; #endif // DEBUG_CREATE return (mCreatedOptions.value(alias)); } if (!create) { #ifdef DEBUG_CREATE qDebug() << "does not exist" << alias; #endif // DEBUG_CREATE - return (NULL); + return (nullptr); } #ifdef DEBUG_CREATE qDebug() << "creating new" << alias; #endif // DEBUG_CREATE KScanOption *so = new KScanOption(alias, this); mCreatedOptions.insert(alias, so); return (so); } KScanOption *KScanDevice::getExistingGuiElement(const QByteArray &name) const { - KScanOption *ret = NULL; + KScanOption *ret = nullptr; QByteArray alias = aliasName(name); for (OptionHash::const_iterator it = mCreatedOptions.constBegin(); it!=mCreatedOptions.constEnd(); ++it) { KScanOption *opt = it.value(); if (opt->isGuiElement() && opt->getName()==alias) { ret = opt; break; } } return (ret); } KScanOption *KScanDevice::getGuiElement(const QByteArray &name, QWidget *parent) { - if (name.isEmpty()) return (NULL); - if (!optionExists(name)) return (NULL); + if (name.isEmpty()) return (nullptr); + if (!optionExists(name)) return (nullptr); //qDebug() << "for" << name; KScanOption *so = getExistingGuiElement(name); // see if already exists - if (so!=NULL) return (so); // if so, just return that + if (so!=nullptr) return (so); // if so, just return that so = getOption(name); // create a new scan option if (so->isValid()) // option was created { QWidget *w = so->createWidget(parent); // create widget for option - if (w!=NULL) w->setEnabled(so->isActive() && so->isSoftwareSettable()); + if (w!=nullptr) w->setEnabled(so->isActive() && so->isSoftwareSettable()); //else qDebug() << "no widget created for" << name; } else // option not valid { // (not known by scanner?) //qDebug() << "option invalid" << name; - so = NULL; + so = nullptr; } return (so); } // Constructor & destructor // ------------------------ KScanDevice::KScanDevice(QObject *parent) : QObject(parent) { //qDebug(); ScanGlobal::self()->init(); // do sane_init() first of all - mScannerHandle = NULL; + mScannerHandle = nullptr; mScannerInitialised = false; // is device opened yet? mScannerName = ""; mScanningState = KScanDevice::ScanIdle; - mScanBuf = NULL; // image data buffer while scanning - mScanImage = NULL; // temporary image to scan into - mImageInfo = NULL; // scanned image information + mScanBuf = nullptr; // image data buffer while scanning + mScanImage = nullptr; // temporary image to scan into + mImageInfo = nullptr; // scanned image information - mSocketNotifier = NULL; // socket notifier for async scanning - mSavedOptions = NULL; // options to save during preview + mSocketNotifier = nullptr; // socket notifier for async scanning + mSavedOptions = nullptr; // options to save during preview mBytesRead = 0; mBytesUsed = 0; mPixelX = 0; mPixelY = 0; // TODO: make this just a function call, for predictable order // (sigScanFinished before sigNewImage) connect(this, &KScanDevice::sigScanFinished, this, &KScanDevice::slotScanFinished); } KScanDevice::~KScanDevice() { delete mSavedOptions; delete mImageInfo; // TODO: need to check and do closeDevice() here? - ScanGlobal::self()->setScanDevice(NULL); // going away, don't call me + ScanGlobal::self()->setScanDevice(nullptr); // going away, don't call me //qDebug(); } // Opening/closing the scanner device // ---------------------------------- KScanDevice::Status KScanDevice::openDevice(const QByteArray &backend) { KScanDevice::Status stat = KScanDevice::Ok; //qDebug() << "backend" << backend; mSaneStatus = SANE_STATUS_UNSUPPORTED; if (backend.isEmpty()) return (KScanDevice::ParamError); // search for scanner - if (ScanDevices::self()->deviceInfo(backend)==NULL) return (KScanDevice::NoDevice); + if (ScanDevices::self()->deviceInfo(backend)==nullptr) return (KScanDevice::NoDevice); mScannerName = backend; // set now for authentication QApplication::setOverrideCursor(Qt::WaitCursor); // potential lengthy operation ScanGlobal::self()->setScanDevice(this); // for possible authentication mSaneStatus = sane_open(backend.constData(), &mScannerHandle); if (mSaneStatus==SANE_STATUS_ACCESS_DENIED) // authentication failed? { clearSavedAuth(); // clear any saved password - //qDebug() << "retrying authentication"; // try again once more + //qDebug() << "retrying authentication"; // try again once more mSaneStatus = sane_open(backend.constData(), &mScannerHandle); } if (mSaneStatus==SANE_STATUS_GOOD) { stat = findOptions(); // fill dictionary with options mScannerInitialised = true; // note scanner opened OK } else { stat = KScanDevice::OpenDevice; mScannerName = ""; } QApplication::restoreOverrideCursor(); return (stat); } void KScanDevice::closeDevice() { emit sigCloseDevice(); // tell callers we're closing //qDebug() << "Saving default scan settings"; saveStartupConfig(); // save config for next startup - if (mScannerHandle!=NULL) + if (mScannerHandle!=nullptr) { if (mScanningState!=KScanDevice::ScanIdle) { //qDebug() << "Scanner is still active, calling sane_cancel()"; sane_cancel(mScannerHandle); } sane_close(mScannerHandle); // close the SANE device - mScannerHandle = NULL; // scanner no longer open + mScannerHandle = nullptr; // scanner no longer open } // clear lists of options QList opts = mCreatedOptions.values(); while (!opts.isEmpty()) delete opts.takeFirst(); mCreatedOptions.clear(); mKnownOptions.clear(); mScannerName = ""; mScannerInitialised = false; } // Scanner and image information // ----------------------------- QString KScanDevice::scannerDescription() const { QString ret; if (!mScannerName.isNull() && mScannerInitialised) { ret = ScanDevices::self()->deviceDescription(mScannerName); } else { ret = i18n("No scanner selected"); } //qDebug() << "returning" << ret; return (ret); } QSize KScanDevice::getMaxScanSize() { QSize s; double min, max; KScanOption *so_w = getOption(SANE_NAME_SCAN_BR_X); so_w->getRange(&min, &max); s.setWidth(static_cast(max)); KScanOption *so_h = getOption(SANE_NAME_SCAN_BR_Y); so_h->getRange(&min, &max); s.setHeight(static_cast(max)); return (s); } void KScanDevice::getCurrentFormat(int *format, int *depth) { sane_get_parameters(mScannerHandle, &mSaneParameters); *format = mSaneParameters.format; *depth = mSaneParameters.depth; } // Listing the available options // ----------------------------- KScanDevice::Status KScanDevice::findOptions() { SANE_Int n; SANE_Int opt; if (sane_control_option(mScannerHandle, 0, SANE_ACTION_GET_VALUE, &n, &opt)!=SANE_STATUS_GOOD) { qWarning() << "cannot read option 0 (count)"; return (KScanDevice::ControlError); } mKnownOptions.clear(); for (int i = 1; iname!=NULL && strlen(d->name)>0) name = d->name; + if (d->name!=nullptr && strlen(d->name)>0) name = d->name; if (d->type==SANE_TYPE_GROUP) // option is a group, { // give it a dummy name name = "group-"; name += QByteArray::number(i); } if (!name.isEmpty()) // must now have a name { #ifdef DEBUG_OPTIONS qDebug() << "Option" << i << "is" << name; #endif // DEBUG_OPTIONS mKnownOptions.insert(i, name); } else qWarning() << "Invalid option" << i << "(no name and not a group)"; } return (KScanDevice::Ok); } QList KScanDevice::getAllOptions() const { return (mKnownOptions.values()); } QList KScanDevice::getCommonOptions() const { QList opts; for (OptionHash::const_iterator it = mCreatedOptions.constBegin(); it!=mCreatedOptions.constEnd(); ++it) { KScanOption *so = it.value(); if (so->isCommonOption()) opts.append(it.key()); } return (opts); } QList KScanDevice::getAdvancedOptions() const { QList opts; for (OptionHash::const_iterator it = mCreatedOptions.constBegin(); it!=mCreatedOptions.constEnd(); ++it) { KScanOption *so = it.value(); if (!so->isCommonOption()) opts.append(it.key()); } return (opts); } // Controlling options // ------------------- int KScanDevice::getOptionIndex(const QByteArray &name) const { return (mKnownOptions.key(name)); } bool KScanDevice::optionExists(const QByteArray &name) const { if (name.isEmpty()) return (false); QByteArray alias = aliasName(name); return (mKnownOptions.key(alias)!=0); } /* This function tries to find name aliases which appear from backend to backend. * Example: Custom-Gamma is for epson backends 'gamma-correction' - not a really * cool thing :-| * Maybe this helps us out ? */ QByteArray KScanDevice::aliasName( const QByteArray& name ) const { if (mCreatedOptions.contains(name)) return (name); QByteArray ret = name; if( name == SANE_NAME_CUSTOM_GAMMA ) { if (mCreatedOptions.contains("gamma-correction")) ret = "gamma-correction"; } //if( ret != name ) //qDebug() << "Found alias for" << name << "which is" << ret; return( ret ); } void KScanDevice::applyOption(KScanOption *opt) { bool reload = true; // is a reload needed? - if (opt!=NULL) // an option is specified + if (opt!=nullptr) // an option is specified { #ifdef DEBUG_RELOAD qDebug() << "option" << opt->getName(); #endif // DEBUG_APPLY reload = opt->apply(); // apply this option } if (!reload) // need to reload now? { #ifdef DEBUG_RELOAD qDebug() << "Reload of others not needed"; #endif // DEBUG_RELOAD return; } // reload of all others needed for (OptionHash::const_iterator it = mCreatedOptions.constBegin(); it!=mCreatedOptions.constEnd(); ++it) { KScanOption *so = it.value(); if (!so->isGuiElement()) continue; - if (opt==NULL || so!=opt) + if (opt==nullptr || so!=opt) { #ifdef DEBUG_RELOAD qDebug() << "Reloading" << so->getName(); #endif // DEBUG_RELOAD so->reload(); so->redrawWidget(); } } #ifdef DEBUG_RELOAD qDebug() << "Finished"; #endif // DEBUG_RELOAD } void KScanDevice::reloadAllOptions() { #ifdef DEBUG_RELOAD qDebug(); #endif // DEBUG_RELOAD - applyOption(NULL); + applyOption(nullptr); } // Scanning control // ---------------- void KScanDevice::slotStopScanning() { //qDebug() << "Attempt to stop scanning"; // TODO: needed? will be done by acquireData() if (mScanningState==KScanDevice::ScanInProgress) emit sigScanFinished(KScanDevice::Cancelled); mScanningState = KScanDevice::ScanStopNow; } // Preview image // ------------- const QString KScanDevice::previewFile() const { // TODO: this doesn't work if that directory doesn't exist, // and nothing ever creates that directory! // // Do we want this feature to work? // If so, create the directory in savePreviewImage() below QString dir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)+"/previews/"; QString sname(scannerDescription()); sname.replace('/', "_"); return (dir+sname); } QImage KScanDevice::loadPreviewImage() { const QString prevFile = previewFile(); //qDebug() << "Loading preview from" << prevFile; return (QImage(prevFile)); } bool KScanDevice::savePreviewImage(const QImage &image) const { if (image.isNull()) return (false); const QString prevFile = previewFile(); //qDebug() << "Saving preview to" << prevFile; return (image.save(prevFile, "BMP")); } // Displaying scan options // ----------------------- // // For debugging. Originally showOptions() was called prepareScan() and had // the comment: // // prepareScan tries to set as much as parameters as possible. // // Function which applies all Options which need to be applied. // See SANE-Documentation Table 4.5, description for SANE_CAP_SOFT_DETECT. // The function sets the options which have SANE_CAP_AUTOMATIC set // to automatic adjust. // // But this wasn't true - it only reports the current state of the options. #ifdef DEBUG_OPTIONS inline const char *optionNotifyString(int opt) { return (opt!=0 ? " X |" : " - |"); } void KScanDevice::showOptions() { qDebug() << "for" << mScannerName; std::cerr << "----------------------------------+---+---+---+---+---+---+---+---+-------" << std::endl; std::cerr << " Option |SSL|HSL|SDT|EMU|AUT|INA|ADV|PRI| Value" << std::endl; std::cerr << "----------------------------------+---+---+---+---+---+---+---+---+-------" << std::endl; for (OptionHash::const_iterator it = mCreatedOptions.constBegin(); it!=mCreatedOptions.constEnd(); ++it) { const KScanOption *so = it.value(); if (so->isGroup()) continue; int cap = so->getCapabilities(); QString s = QString(it.key()).left(31).leftJustified(32); std::cerr << " " << qPrintable(s) << " |" << optionNotifyString((cap & SANE_CAP_SOFT_SELECT)) << optionNotifyString((cap & SANE_CAP_HARD_SELECT)) << optionNotifyString((cap & SANE_CAP_SOFT_DETECT)) << optionNotifyString((cap & SANE_CAP_EMULATED)) << optionNotifyString((cap & SANE_CAP_AUTOMATIC)) << optionNotifyString((cap & SANE_CAP_INACTIVE)) << optionNotifyString((cap & SANE_CAP_ADVANCED)) << optionNotifyString(so->isPriorityOption()) << " " << qPrintable(so->get()) << std::endl; } std::cerr << "----------------------------------+---+---+---+---+---+---+---+---+-------" << std::endl; } #endif // DEBUG_OPTIONS // Creating a new image to receive the scan/preview // ------------------------------------------------ static ImageMetaInfo::ImageType getImageFormat(const SANE_Parameters *p) { - if (p==NULL) return (ImageMetaInfo::Unknown); + if (p==nullptr) return (ImageMetaInfo::Unknown); if (p->depth==1) // Line art (bitmap) { return (ImageMetaInfo::BlackWhite); } else if (p->depth==8) // 8 bit RGB { if (p->format==SANE_FRAME_GRAY) // Grey scale { return (ImageMetaInfo::Greyscale); } else // True colour { return (ImageMetaInfo::HighColour); } } else // Error, no others supported { //qDebug() << "Only bit depths 1 or 8 supported!"; return (ImageMetaInfo::Unknown); } } KScanDevice::Status KScanDevice::createNewImage(const SANE_Parameters *p) { QImage::Format fmt; ImageMetaInfo::ImageType itype = getImageFormat(p); // what format should this be? switch (itype) // choose QImage option for that { default: case ImageMetaInfo::Unknown: return (KScanDevice::ParamError); case ImageMetaInfo::BlackWhite: fmt = QImage::Format_Mono; break; case ImageMetaInfo::Greyscale: fmt = QImage::Format_Indexed8; break; case ImageMetaInfo::HighColour: fmt = QImage::Format_RGB32; break; } delete mScanImage; // recreate new image mScanImage = new QImage(p->pixels_per_line,p->lines,fmt); - if (mScanImage==NULL) return (KScanDevice::NoMemory); + if (mScanImage==nullptr) return (KScanDevice::NoMemory); if (itype==ImageMetaInfo::BlackWhite) // Line art (bitmap) { mScanImage->setColor(0,qRgb(0x00,0x00,0x00)); // set black/white palette mScanImage->setColor(1,qRgb(0xFF,0xFF,0xFF)); } else if (itype==ImageMetaInfo::Greyscale) // 8 bit grey { // set grey scale palette for (int i = 0; i<256; i++) mScanImage->setColor(i,qRgb(i,i,i)); } return (KScanDevice::Ok); } // Acquiring preview/scan image // ---------------------------- KScanDevice::Status KScanDevice::acquirePreview( bool forceGray, int dpi ) { - if (mSavedOptions!=NULL) mSavedOptions->clear(); + if (mSavedOptions!=nullptr) mSavedOptions->clear(); else mSavedOptions = new KScanOptSet("TempStore"); /* set Preview = ON if exists */ KScanOption *prev = getOption(SANE_NAME_PREVIEW, false); - if (prev!=NULL) + if (prev!=nullptr) { prev->set(true); prev->apply(); prev->set(false); // Ensure that it gets restored mSavedOptions->backupOption(prev); // back to 'false' after previewing } // TODO: this block doesn't make sense (set the option to true if // it is already true?) /* Gray-Preview only done by widget? */ KScanOption *so = getOption(SANE_NAME_GRAY_PREVIEW, false); - if (so!=NULL) + if (so!=nullptr) { if (so->get()=="true") { /* Gray preview on */ so->set(true); //qDebug() << "Setting GrayPreview ON"; } else { so->set(false); //qDebug() << "Setting GrayPreview OFF"; } so->apply(); } KScanOption *mode = getOption(SANE_NAME_SCAN_MODE, false); - if (mode!=NULL) + if (mode!=nullptr) { //qDebug() << "Scan mode before preview is" << mode->get(); mSavedOptions->backupOption(mode); /* apply if it has a widget, or apply always? */ if (mode->isGuiElement()) mode->apply(); } /* Some sort of Scan Resolution option should always exist */ KScanOption *xres = getOption(SANE_NAME_SCAN_X_RESOLUTION, false); - if (xres==NULL) xres = getOption(SANE_NAME_SCAN_RESOLUTION, false); - if (xres!=NULL) + if (xres==nullptr) xres = getOption(SANE_NAME_SCAN_RESOLUTION, false); + if (xres!=nullptr) { //qDebug() << "Scan resolution before preview is" << xres->get(); mSavedOptions->backupOption(xres); int preview_dpi = dpi; if (dpi==0) // preview DPI not specified { double min, max; if (!xres->getRange(&min, &max)) { //qDebug() << "Could not retrieve resolution range!"; min = 75.0; // hope every scanner can do this } preview_dpi = (int) min; if (preview_dpibackupOption(yres); yres->set(preview_dpi); yres->apply(); yres->get(&mCurrScanResolutionY); } else mCurrScanResolutionY = 0; /* Resolution bind switch? */ KScanOption *bind = getOption(SANE_NAME_RESOLUTION_BIND, false); - if (bind!=NULL) + if (bind!=nullptr) { /* Switch binding on if available */ mSavedOptions->backupOption(bind); bind->set(true); bind->apply(); } xres->set(preview_dpi); xres->apply(); /* Store the resulting preview for additional image information */ xres->get(&mCurrScanResolutionX); if (mCurrScanResolutionY==0) mCurrScanResolutionY = mCurrScanResolutionX; } return (acquireData(true)); // perform the preview } void KScanDevice::applyAllOptions(bool prio) { for (OptionHash::const_iterator it = mCreatedOptions.constBegin(); it!=mCreatedOptions.constEnd(); ++it) { KScanOption *so = it.value(); if (!so->isGuiElement()) continue; if (so->isPriorityOption() ^ prio) continue; if (so->isActive() && so->isSoftwareSettable()) so->apply(); } } /* Starts scanning * depending on if a filename is given or not, the function tries to open * the file using the Qt-Image-IO or really scans the image. */ KScanDevice::Status KScanDevice::acquireScan(const QString &filename) { if (filename.isEmpty()) // real scan { applyAllOptions(true); // apply priority options applyAllOptions(false); // apply non-priority options // One of the Scan Resolution parameters should always exist KScanOption *xres = getOption(SANE_NAME_SCAN_X_RESOLUTION, false); - if (xres==NULL) xres = getOption(SANE_NAME_SCAN_RESOLUTION, false); - if (xres!=NULL) + if (xres==nullptr) xres = getOption(SANE_NAME_SCAN_RESOLUTION, false); + if (xres!=nullptr) { xres->get(&mCurrScanResolutionX); KScanOption *yres = getOption(SANE_NAME_SCAN_Y_RESOLUTION, false); - if (yres!=NULL) yres->get(&mCurrScanResolutionY); + if (yres!=nullptr) yres->get(&mCurrScanResolutionY); else mCurrScanResolutionY = mCurrScanResolutionX; } return (acquireData(false)); // perform the scan } else // virtual scan from image file { QFileInfo file(filename); if (!file.exists()) { //qDebug() << "virtual file" << filename << "does not exist"; return (KScanDevice::ParamError); } QImage img(filename); if (img.isNull()) { //qDebug() << "virtual file" << filename << "could not load"; return (KScanDevice::ParamError); } ImageMetaInfo info; info.setXResolution(img.dotsPerMeterX()); // TODO: *2.54/100 info.setYResolution(img.dotsPerMeterY()); // TODO: *2.54/100 info.setScannerName(QFile::encodeName(filename)); emit sigNewImage(&img, &info); return (KScanDevice::Ok); } } #ifdef DEBUG_PARAMS static void dumpParams(const QString &msg, const SANE_Parameters *p) { QByteArray formatName("UNKNOWN"); switch (p->format) { case SANE_FRAME_GRAY: formatName = "GREY"; break; case SANE_FRAME_RGB: formatName = "RGB"; break; case SANE_FRAME_RED: formatName = "RED"; break; case SANE_FRAME_GREEN: formatName = "GREEN"; break; case SANE_FRAME_BLUE: formatName = "BLUE"; break; } qDebug() << msg.toLatin1().constData(); qDebug() << " format: " << p->format << "=" << formatName.constData(); qDebug() << " last_frame: " << p->last_frame; qDebug() << " lines: " << p->lines; qDebug() << " depth: " << p->depth; qDebug() << " pixels_per_line: " << p->pixels_per_line; qDebug() << " bytes_per_line: " << p->bytes_per_line; } #endif // DEBUG_PARAMS KScanDevice::Status KScanDevice::acquireData(bool isPreview) { KScanDevice::Status stat = KScanDevice::Ok; int frames = 0; #ifdef DEBUG_OPTIONS showOptions(); // dump the current noptions #endif mScanningPreview = isPreview; mScanningState = KScanDevice::ScanStarting; mBytesRead = 0; mBlocksRead = 0; ScanGlobal::self()->setScanDevice(this); // for possible authentication - if (mImageInfo!=NULL) delete mImageInfo; // start with this clean - mImageInfo = NULL; + if (mImageInfo!=nullptr) delete mImageInfo; // start with this clean + mImageInfo = nullptr; if (!isPreview) // scanning to eventually save { mImageInfo = new ImageMetaInfo; // create for image information mSaneStatus = sane_get_parameters(mScannerHandle, &mSaneParameters); if (mSaneStatus==SANE_STATUS_GOOD) // get pre-scan parameters { #ifdef DEBUG_PARAMS dumpParams("Before scan:", &mSaneParameters); #endif // DEBUG_PARAMS if (mSaneParameters.lines>=1 && mSaneParameters.pixels_per_line>0) { // check for a plausible image ImageMetaInfo::ImageType fmt = getImageFormat(&mSaneParameters); if (fmt==ImageMetaInfo::Unknown) // find format it will have { // scan format not recognised? stat = KScanDevice::ParamError; // no point starting scan emit sigScanFinished(stat); // scan is now finished return (stat); } mImageInfo->setImageType(fmt); // save result for later } } } // Tell the application that scanning is about to start. emit sigScanStart(mImageInfo); // If the image information was available, the application may have // prompted for a filename. If the user cancelled that, it will have // called our slotStopScanning() which set mScanningState to // KScanDevice::ScanStopNow. If that is the case, then finish here. if (mScanningState==KScanDevice::ScanStopNow) { // user cancelled save dialogue //qDebug() << "user cancelled before start"; stat = KScanDevice::Cancelled; emit sigScanFinished(stat); return (stat); } while (true) // loop while frames available { QApplication::setOverrideCursor(Qt::WaitCursor); // potential lengthy operation mSaneStatus = sane_start(mScannerHandle); if (mSaneStatus==SANE_STATUS_ACCESS_DENIED) // authentication failed? { //qDebug() << "retrying authentication"; clearSavedAuth(); // clear any saved password mSaneStatus = sane_start(mScannerHandle); // try again once more } if (mSaneStatus==SANE_STATUS_GOOD) { mSaneStatus = sane_get_parameters(mScannerHandle, &mSaneParameters); if (mSaneStatus==SANE_STATUS_GOOD) { #ifdef DEBUG_PARAMS dumpParams(QString("For frame %1:").arg(frames+1), &mSaneParameters); #endif // DEBUG_PARAMS // TODO: implement "Hand Scanner" support if (mSaneParameters.lines<1) { //qDebug() << "Hand Scanner not supported"; stat = KScanDevice::NotSupported; } else if (mSaneParameters.pixels_per_line==0) { //qDebug() << "Nothing to acquire!"; stat = KScanDevice::EmptyPic; } } else { stat = KScanDevice::OpenDevice; //qDebug() << "sane_get_parameters() error" << lastSaneErrorMessage(); } } else { stat = KScanDevice::OpenDevice; //qDebug() << "sane_start() error" << lastSaneErrorMessage(); } QApplication::restoreOverrideCursor(); if (stat==KScanDevice::Ok && mScanningState==KScanDevice::ScanStarting) { // first time through loop // Create image to receive scan, based on real SANE parameters stat = createNewImage(&mSaneParameters); // Create/reinitialise buffer for scanning one line if (stat==KScanDevice::Ok) { - if (mScanBuf!=NULL) delete [] mScanBuf; + if (mScanBuf!=nullptr) delete [] mScanBuf; mScanBuf = new SANE_Byte[mSaneParameters.bytes_per_line+4]; - if (mScanBuf==NULL) stat = KScanDevice::NoMemory; + if (mScanBuf==nullptr) stat = KScanDevice::NoMemory; } // can this ever happen? if (stat==KScanDevice::Ok) { int fd = 0; // Don't assume that sane_get_select_fd() will succeed even if // sane_set_io_mode() successfully sets I/O mode to noblocking - // bug 159300 if (sane_set_io_mode(mScannerHandle, SANE_TRUE)==SANE_STATUS_GOOD) { if (sane_get_select_fd(mScannerHandle, &fd)==SANE_STATUS_GOOD) { //qDebug() << "using read socket notifier"; mSocketNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); connect(mSocketNotifier, &QSocketNotifier::activated, this, &KScanDevice::doProcessABlock); } //else qDebug() << "not using socket notifier (sane_get_select_fd() failed)"; } //else qDebug() << "not using socket notifier (sane_set_io_mode() failed)"; } } if (stat!=KScanDevice::Ok) // some problem getting started { // Scanning could not start - give up now //qDebug() << "Scanning failed to start, status" << stat; emit sigScanFinished(stat); return (stat); } if (mScanningState==KScanDevice::ScanStarting) // first time through loop { QApplication::setOverrideCursor(Qt::BusyCursor); emit sigAcquireStart(); } emit sigScanProgress(0); // signal the progress dialog qApp->processEvents(); // update the progress window mPixelX = 0; mPixelY = 0; mBytesUsed = 0; // Set internal status to indicate scanning in progress. // This status might be changed, e.g. by pressing Stop on a // GUI dialog displayed during scanning. mScanningState = KScanDevice::ScanInProgress; // As originally coded, if using the socket notifier // sane_get_parameters() was only called once at the beginning of // the scan - just after sane_start() above. If not using the socket // notifier on the other hand, sane_get_parameters() was called after // each doProcessABlock() in the loop below. // // According to the SANE documentation text, sane_get_parameters() // needs to be called once after sane_start() to get the exact // parameters, but not necessarily in the reading loop that just // needs to call sane_read() repeatedly. The diagram above, though, // seems to imply that sane_get_parameters() should be done in the // reading loop. // // Doing the sane_get_parameters() just once seems to work with all // of the scanners that I have available to test, both using the // socket notifier and not. So that is what we now do, in this // much simpler loop. while (true) // loop for one scanned frame { - if (mSocketNotifier!=NULL) // using the socket notifier + if (mSocketNotifier!=nullptr) // using the socket notifier { qApp->processEvents(); // let it fire away } else // not using socket notifier { doProcessABlock(); // may block while reading } // exit loop when frame done if (mScanningState==KScanDevice::ScanIdle || mScanningState==KScanDevice::ScanStopNow || mScanningState==KScanDevice::ScanNextFrame) break; } ++frames; // count up this frame // more frames to do if (mScanningState==KScanDevice::ScanNextFrame) continue; break; // scan done, exit loop } if (mScanningState==KScanDevice::ScanStopNow) { stat = KScanDevice::Cancelled; } else { if (mSaneStatus!=SANE_STATUS_GOOD && mSaneStatus!=SANE_STATUS_EOF) { stat = KScanDevice::ScanError; } } //qDebug() << "Scan read" << mBytesRead << "bytes in" //<< mBlocksRead << "blocks," << frames << "frames - status" << stat; emit sigScanFinished(stat); // scan is now finished return (stat); } /* This function calls at least sane_read and converts the read data from the scanner * to the qimage. * The function needs: * QImage img valid * the data-buffer set to a appropriate size **/ // TODO: probably needs to be extended for 16-bit scanner support void KScanDevice::doProcessABlock() { int val,i; QRgb col, newCol; - SANE_Byte *rptr = NULL; + SANE_Byte *rptr = nullptr; SANE_Int bytes_read = 0; int chan = 0; mSaneStatus = SANE_STATUS_GOOD; uchar eight_pix = 0; if (mScanningState==KScanDevice::ScanIdle) return; // scan finished, no more to do // block notifications while working - if (mSocketNotifier!=NULL) mSocketNotifier->setEnabled(false); + if (mSocketNotifier!=nullptr) mSocketNotifier->setEnabled(false); while (true) { mSaneStatus = sane_read(mScannerHandle, (mScanBuf+mBytesUsed), mSaneParameters.bytes_per_line, &bytes_read); if (mSaneStatus!=SANE_STATUS_GOOD) { if (mSaneStatus!=SANE_STATUS_EOF) // this is OK, just stop { // any other error //qDebug() << "sane_read() error" << lastSaneErrorMessage() //<< "bytes read" << bytes_read; } break; } if (bytes_read<1) break; // no data, finish loop ++mBlocksRead; mBytesRead += bytes_read; // qDebug( "Bytes read: %d, bytes written: %d", bytes_read, mBytesUsed ); int red = 0; int green = 0; int blue = 0; rptr = mScanBuf; // start of scan data switch (mSaneParameters.format) { case SANE_FRAME_RGB: if (mSaneParameters.lines<1) break; bytes_read += mBytesUsed; // die übergebliebenen Bytes dazu mBytesUsed = bytes_read % 3; for (val = 0; val<((bytes_read-mBytesUsed)/3); val++) { red = *rptr++; green = *rptr++; blue = *rptr++; if (mPixelX>=mSaneParameters.pixels_per_line) { // reached end of a row mPixelX = 0; mPixelY++; } if (mPixelYheight()) // within image height { mScanImage->setPixel(mPixelX, mPixelY, qRgb(red, green, blue)); } mPixelX++; } for (val = 0; val=mSaneParameters.lines) break; if (mSaneParameters.depth==8) // Greyscale { if (mPixelX>=mSaneParameters.pixels_per_line) { // reached end of a row mPixelX = 0; mPixelY++; } mScanImage->setPixel(mPixelX, mPixelY, *rptr++); mPixelX++; } else // Lineart (bitmap) { // needs to be converted to byte eight_pix = *rptr++; for (i = 0; i<8; i++) { if (mPixelY0 ? 0 : 1; eight_pix = eight_pix << 1; mScanImage->setPixel(mPixelX, mPixelY, chan); mPixelX++; if( mPixelX>=mSaneParameters.pixels_per_line) { mPixelX = 0; mPixelY++; break; } } } } } break; case SANE_FRAME_RED: case SANE_FRAME_GREEN: case SANE_FRAME_BLUE: for (val = 0; val=mSaneParameters.pixels_per_line) { // reached end of a row mPixelX = 0; mPixelY++; } if (mPixelYpixel(mPixelX, mPixelY); red = qRed(col); green = qGreen(col); blue = qBlue(col); chan = *rptr++; switch (mSaneParameters.format) { case SANE_FRAME_RED: newCol = qRgba(chan, green, blue, 0xFF); break; case SANE_FRAME_GREEN: newCol = qRgba(red, chan, blue, 0xFF); break; case SANE_FRAME_BLUE: newCol = qRgba(red, green, chan, 0xFF); break; default: newCol = qRgba(0xFF, 0xFF, 0xFF, 0xFF); break; } mScanImage->setPixel(mPixelX, mPixelY, newCol); mPixelX++; } } break; default: //qDebug() << "Undefined SANE format" << mSaneParameters.format; break; } // switch of scan format if ((mSaneParameters.lines>0) && ((mSaneParameters.lines*mPixelY)>0)) { int progress = (int)(((double)MAX_PROGRESS)/mSaneParameters.lines*mPixelY); if (progress Parameter neu belegen und neu starten **/ mScanningState = KScanDevice::ScanNextFrame; //qDebug() << "EOF, but another frame to scan"; } } else if (mSaneStatus!=SANE_STATUS_GOOD) { mScanningState = KScanDevice::ScanIdle; //qDebug() << "Scan error or cancelled, status" << mSaneStatus; } - if (mSocketNotifier!=NULL) mSocketNotifier->setEnabled(true); + if (mSocketNotifier!=nullptr) mSocketNotifier->setEnabled(true); } void KScanDevice::slotScanFinished(KScanDevice::Status status) { - if (mSocketNotifier!=NULL) // clean up if in use + if (mSocketNotifier!=nullptr) // clean up if in use { delete mSocketNotifier; - mSocketNotifier = NULL; + mSocketNotifier = nullptr; } emit sigScanProgress(MAX_PROGRESS); QApplication::restoreOverrideCursor(); //qDebug() << "status" << status; - if (mScanBuf!=NULL) + if (mScanBuf!=nullptr) { delete[] mScanBuf; - mScanBuf = NULL; + mScanBuf = nullptr; } - if (status==KScanDevice::Ok && mScanImage!=NULL) + if (status==KScanDevice::Ok && mScanImage!=nullptr) { ImageMetaInfo info; info.setXResolution(mCurrScanResolutionX); info.setYResolution(mCurrScanResolutionY); info.setScannerName(mScannerName); // put the resolution also into the image itself // TODO: use qRound() mScanImage->setDotsPerMeterX(static_cast(mCurrScanResolutionX / 0.0254 + 0.5)); mScanImage->setDotsPerMeterY(static_cast(mCurrScanResolutionY / 0.0254 + 0.5)); if (mScanningPreview) { savePreviewImage(*mScanImage); emit sigNewPreview(mScanImage, &info); loadOptionSet(mSavedOptions); // restore original scan settings } else { emit sigNewImage(mScanImage, &info); } } sane_cancel(mScannerHandle); /* This follows after sending the signal */ - if (mScanImage!=NULL) + if (mScanImage!=nullptr) { delete mScanImage; - mScanImage = NULL; + mScanImage = nullptr; } mScanningState = KScanDevice::ScanIdle; } // Configuration // ------------- void KScanDevice::saveStartupConfig() { if (mScannerName.isNull()) return; // do not save for no scanner KScanOptSet optSet(KScanOptSet::startupSetName()); getCurrentOptions(&optSet); optSet.saveConfig(mScannerName, i18n("Default startup configuration")); } void KScanDevice::loadOptionSetInternal(const KScanOptSet *optSet, bool prio) { for (KScanOptSet::const_iterator it = optSet->constBegin(); it!=optSet->constEnd(); ++it) { const QByteArray name = it.key(); if (!optionExists(name)) continue; // only for options that exist KScanOption *so = getOption(name, false); - if (so==NULL) continue; // we don't have this option + if (so==nullptr) continue; // we don't have this option if (so->isGroup()) continue; // nothing to do here if (so->isPriorityOption() ^ prio) continue; // check whether requested priority so->set(it.value()); if (so->isInitialised() && so->isSoftwareSettable() && so->isActive()) so->apply(); } } void KScanDevice::loadOptionSet(const KScanOptSet *optSet) { - if (optSet==NULL) return; + if (optSet==nullptr) return; //qDebug() << "Loading set" << optSet->getSetName() << "with" << optSet->count() << "options"; loadOptionSetInternal(optSet, true); loadOptionSetInternal(optSet, false); } // Retrieve the current options from the scanner, i.e. all of those that // have an associated GUI element and also any others (e.g. the TL_X and // other scan area settings) that have been apply()'ed but do not have // a GUI. void KScanDevice::getCurrentOptions(KScanOptSet *optSet) const { - if (optSet==NULL) return; + if (optSet==nullptr) return; for (OptionHash::const_iterator it = mCreatedOptions.constBegin(); it!=mCreatedOptions.constEnd(); ++it) { KScanOption *so = it.value(); if (!so->isReadable()) continue; if (so->isGuiElement() || so->isApplied()) { if (so->isActive()) optSet->backupOption(so); so->setApplied(false); } } } KConfigGroup KScanDevice::configGroup(const QString &groupName) { Q_ASSERT(!groupName.isEmpty()); return (ScanSettings::self()->config()->group(groupName)); } // SANE Authentication // ------------------- // // According to the SANE documentation, this may be requested for any use of // sane_open(), sane_control_option() or sane_start() on a scanner device // that requires authentication. // // The only uses of sane_open() and sane_start() are here in this file, and // they set the current scanner using setScanDevice() before performing the // SANE operation. // // This does not happen for all uses of sane_control_option(), either here or // in KScanOption, so there is a slight possibility that if authentication is // needed for those (and has not been previously requested by sane_open() or // sane_start()) then it will use the wrong scanner device or will not prompt // at all. However, Kooka only supports one scanner open at a time, and does // sane_open() before any use of sane_control_option(). So hopefully this // will not be a problem. bool KScanDevice::authenticate(QByteArray *retuser, QByteArray *retpass) { //qDebug() << "for" << mScannerName; // TODO: use KWallet for username/password? KConfigGroup grp = configGroup(mScannerName); QByteArray user = QByteArray::fromBase64(grp.readEntry("user", QString()).toLocal8Bit()); QByteArray pass = QByteArray::fromBase64(grp.readEntry("pass", QString()).toLocal8Bit()); if (!user.isEmpty() && !pass.isEmpty()) { //qDebug() << "have saved username/password"; } else { //qDebug() << "asking for username/password"; - KPasswordDialog dlg(NULL, KPasswordDialog::ShowKeepPassword|KPasswordDialog::ShowUsernameLine); + KPasswordDialog dlg(nullptr, KPasswordDialog::ShowKeepPassword|KPasswordDialog::ShowUsernameLine); dlg.setPrompt(xi18nc("@info", "The scanner%1requires authentication.", mScannerName.constData())); dlg.setWindowTitle(i18n("Scanner Authentication")); if (!user.isEmpty()) dlg.setUsername(user); if (!pass.isEmpty()) dlg.setPassword(pass); if (!dlg.exec()) return (false); user = dlg.username().toLocal8Bit(); pass = dlg.password().toLocal8Bit(); if (dlg.keepPassword()) { grp.writeEntry("user", user.toBase64()); grp.writeEntry("pass", pass.toBase64()); } } *retuser = user; *retpass = pass; return (true); } void KScanDevice::clearSavedAuth() { KConfigGroup grp = configGroup(mScannerName); grp.deleteEntry("user"); grp.deleteEntry("pass"); grp.sync(); } // Error reporting // --------------- QString KScanDevice::lastSaneErrorMessage() const { return (sane_strstatus(mSaneStatus)); } QString KScanDevice::statusMessage(KScanDevice::Status stat) { switch (stat) { case KScanDevice::Ok: return (i18n("OK")); // shouldn't be reported case KScanDevice::NoDevice: return (i18n("No device")); // never during scanning case KScanDevice::ParamError: return (i18n("Bad parameter")); case KScanDevice::OpenDevice: return (i18n("Cannot open device")); case KScanDevice::ControlError: return (i18n("sane_control_option() failed")); case KScanDevice::ScanError: return (i18n("Error while scanning")); case KScanDevice::EmptyPic: return (i18n("Empty picture")); case KScanDevice::NoMemory: return (i18n("Out of memory")); case KScanDevice::Reload: return (i18n("Needs reload")); // never during scanning case KScanDevice::Cancelled: return (i18n("Cancelled")); // shouldn't be reported case KScanDevice::OptionNotActive: return (i18n("Not active")); // never during scanning case KScanDevice::NotSupported: return (i18n("Not supported")); default: return (i18n("Unknown status %1", stat)); } } diff --git a/libkookascan/kscanoption.cpp b/libkookascan/kscanoption.cpp index d96f464..7e9aa17 100644 --- a/libkookascan/kscanoption.cpp +++ b/libkookascan/kscanoption.cpp @@ -1,1108 +1,1108 @@ /* This file is part of the KDE Project Copyright (C) 2000 Klaas Freitag This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kscanoption.h" #include #include #include #include #include #include #include #include #include extern "C" { #include #include } #include "kgammatable.h" #include "kscandevice.h" #include "kscancontrols.h" #include "kscanoptset.h" // Debugging options #undef DEBUG_MEM #undef DEBUG_GETSET #define DEBUG_APPLY #undef DEBUG_RELOAD // This defines the possible resolutions that will be shown by the combo. // Only resolutions from this list falling within the scanner's allowed range // will be included. static const int resList[] = { 50, 75, 100, 150, 200, 300, 600, 900, 1200, 1800, 2400, 4800, 9600, 0 }; KScanOption::KScanOption(const QByteArray &name, KScanDevice *scandev) { mScanDevice = scandev; if (!initOption(name)) { //qDebug() << "initOption for" << name << "failed!"; return; } if (!mIsReadable) return; // no value to read if (mBuffer.isNull()) return; // no buffer for value // read initial value from the scanner SANE_Status sanestat = sane_control_option(mScanDevice->scannerHandle(), mIndex, SANE_ACTION_GET_VALUE, mBuffer.data(), nullptr); if (sanestat==SANE_STATUS_GOOD) mBufferClean = false; } static bool shouldBePriorityOption(const QByteArray &name) { return (name=="source"); } bool KScanOption::initOption(const QByteArray &name) { mDesc = nullptr; mControl = nullptr; mIsGroup = false; mIsReadable = true; mIsPriority = shouldBePriorityOption(name); mWidgetType = KScanOption::Invalid; if (name.isEmpty()) return (false); mName = name; // Look up the option (which must already exist in the map) by name. // // The default-constructed index is 0 for an invalid option, this is OK // because although a SANE option with that index exists it is never // requested by name. mIndex = mScanDevice->getOptionIndex(mName); if (mIndex<=0) { //qDebug() << "no option descriptor for" << mName; return (false); } mDesc = sane_get_option_descriptor(mScanDevice->scannerHandle(), mIndex); if (mDesc==nullptr) return (false); mBuffer.resize(0); mBufferClean = true; mApplied = false; if (mDesc->type==SANE_TYPE_GROUP) mIsGroup = true; if (mIsGroup || mDesc->type==SANE_TYPE_BUTTON) mIsReadable = false; if (!(mDesc->cap & SANE_CAP_SOFT_DETECT)) mIsReadable = false; mGammaTable = nullptr; // for recording gamma values mWidgetType = resolveWidgetType(); // work out the type of widget allocForDesc(); // allocate initial buffer return (true); } KScanOption::~KScanOption() { #ifdef DEBUG_MEM if (!mBuffer.isNull()) //qDebug() << "Freeing" << mBuffer.size() << "bytes for" << mName; #endif // TODO: need to delete mControl here? } void KScanOption::slotWidgetChange(const QString &t) { set(t.toUtf8()); emit guiChange(this); } void KScanOption::slotWidgetChange(int i) { set(i); emit guiChange(this); } void KScanOption::slotWidgetChange() { set(1); emit guiChange(this); } void KScanOption::updateList() { KScanCombo *combo = qobject_cast(mControl); if (combo==nullptr) return; QList list = getList(); combo->setList(list); } /* called on a widget change, if a widget was created. */ void KScanOption::redrawWidget() { if (!isValid() || !isReadable() || mControl==nullptr || mBuffer.isNull()) return; KScanControl::ControlType type = mControl->type(); if (type==KScanControl::Number) // numeric control { int i = 0; if (get(&i)) mControl->setValue(i); } else if (type==KScanControl::Text) // text control { mControl->setText(get()); } } /* Get and update the current setting from the scanner */ void KScanOption::reload() { if (mControl!=nullptr) { if (isGroup()) { mControl->setEnabled(true); // always enabled return; // no more to do } if (!isActive()) { #ifdef DEBUG_RELOAD qDebug() << "not active" << mName; #endif mControl->setEnabled(false); } else if (!isSoftwareSettable()) { #ifdef DEBUG_RELOAD qDebug() << "not settable" << mName; #endif mControl->setEnabled(false); } else mControl->setEnabled(true); } if (!isReadable()) { #ifdef DEBUG_RELOAD qDebug() << "not readable" << mName; #endif return; } if (mBuffer.isNull()) // first get mem if needed { qDebug() << "need to allocate now"; allocForDesc(); // allocate the buffer now } if (!isActive()) return; if (mDesc->size>mBuffer.size()) { //qDebug() << "buffer too small for" << mName << "type" << mDesc->type //<< "size" << mBuffer.size() << "need" << mDesc->size; allocForDesc(); // grow the buffer } // Starting with SANE 1.0.20, the 'test' device needs this dummy call of // sane_get_option_descriptor() before any use of sane_control_option(), // otherwise the latter fails with a SANE_STATUS_INVAL. From the master // repository at http://anonscm.debian.org/gitweb/?p=sane/sane-backends.git: // // author m. allan noah // Thu, 26 Jun 2008 13:14:23 +0000 (13:14 +0000) // commit 8733651c4b07ac6ccbcee0d39eccca0c08057729 // test backend checks for options that have not been loaded before being controlled // // I'm hoping that this is not in general an expensive operation - it certainly // is not so for the 'test' device and a sample of others - so it should not be // necessary to conditionalise it for that device only. const SANE_Option_Descriptor *desc = sane_get_option_descriptor(mScanDevice->scannerHandle(), mIndex); if (desc==nullptr) return; // should never happen SANE_Status sanestat = sane_control_option(mScanDevice->scannerHandle(), mIndex, SANE_ACTION_GET_VALUE, mBuffer.data(), nullptr); if (sanestat!=SANE_STATUS_GOOD) { qWarning() << "Can't get value for" << mName << "status" << sane_strstatus(sanestat); return; } updateList(); // if range changed, update GUI #ifdef DEBUG_RELOAD qDebug() << "reloaded" << mName; #endif mBufferClean = false; } bool KScanOption::apply() { int sane_result = 0; bool reload = false; #ifdef DEBUG_APPLY QString debug = QString("option '%1'").arg(mName.constData()); #endif // DEBUG_APPLY SANE_Status sanestat = SANE_STATUS_GOOD; // See comment in reload() above const SANE_Option_Descriptor *desc = sane_get_option_descriptor(mScanDevice->scannerHandle(), mIndex); if (desc==nullptr) return (false); // should never happen if (mName==SANE_NAME_PREVIEW || mName==SANE_NAME_SCAN_MODE) { sanestat = sane_control_option(mScanDevice->scannerHandle(), mIndex, - SANE_ACTION_SET_AUTO, 0, + SANE_ACTION_SET_AUTO, nullptr, &sane_result); /* No return here, please! Carsten, does it still work than for you? */ } if (!isInitialised() || mBuffer.isNull()) { #ifdef DEBUG_APPLY debug += " nobuffer"; #endif // DEBUG_APPLY if (!isAutoSettable()) goto ret; #ifdef DEBUG_APPLY debug += " auto"; #endif // DEBUG_APPLY sanestat = sane_control_option(mScanDevice->scannerHandle(), mIndex, - SANE_ACTION_SET_AUTO, 0, + SANE_ACTION_SET_AUTO, nullptr, &sane_result); } else { if (!isActive()) { #ifdef DEBUG_APPLY debug += " notactive"; #endif // DEBUG_APPLY goto ret; } else if (!isSoftwareSettable()) { #ifdef DEBUG_APPLY debug += " notsettable"; #endif // DEBUG_APPLY goto ret; } else { sanestat = sane_control_option(mScanDevice->scannerHandle(), mIndex, SANE_ACTION_SET_VALUE, mBuffer.data(), &sane_result); } } if (sanestat!=SANE_STATUS_GOOD) { //qDebug() << "apply" << mName << "failed, SANE status" << sane_strstatus(sanestat); return (false); } #ifdef DEBUG_APPLY debug += QString(" -> '%1'").arg(get().constData()); #endif // DEBUG_APPLY if (sane_result & SANE_INFO_RELOAD_OPTIONS) { #ifdef DEBUG_APPLY debug += " reload"; #endif // DEBUG_APPLY reload = true; } #ifdef DEBUG_APPLY if (sane_result & SANE_INFO_INEXACT) debug += " inexact"; #endif // DEBUG_APPLY mApplied = true; ret: #ifdef DEBUG_APPLY qDebug() << qPrintable(debug); // no quotes, please #endif // DEBUG_APPLY return (reload); } // The name of the option is checked here to detect options which are // a resolution or a gamma table, and therefore are to be treated // specially. This should hopefully be more reliable then the earlier // heuristics. KScanOption::WidgetType KScanOption::resolveWidgetType() const { if (!isValid()) return (KScanOption::Invalid); KScanOption::WidgetType ret; switch (mDesc->type) { case SANE_TYPE_BOOL: ret = KScanOption::Bool; break; case SANE_TYPE_INT: case SANE_TYPE_FIXED: if (qstrcmp(mDesc->name, SANE_NAME_SCAN_RESOLUTION)==0 || qstrcmp(mDesc->name, SANE_NAME_SCAN_X_RESOLUTION)==0 || qstrcmp(mDesc->name, SANE_NAME_SCAN_Y_RESOLUTION)==0) { ret = KScanOption::Resolution; if (mDesc->unit!=SANE_UNIT_DPI) qDebug() << "expected" << mName << "unit" << mDesc->unit << "to be DPI"; } else if (qstrcmp(mDesc->name, SANE_NAME_GAMMA_VECTOR)==0 || qstrcmp(mDesc->name, SANE_NAME_GAMMA_VECTOR_R)==0 || qstrcmp(mDesc->name, SANE_NAME_GAMMA_VECTOR_G)==0 || qstrcmp(mDesc->name, SANE_NAME_GAMMA_VECTOR_B)==0) { ret = KScanOption::GammaTable; if (mDesc->size!=sizeof(SANE_Byte)) qDebug() << "expected" << mName << "size" << mDesc->size << "to be BYTE"; } else if (mDesc->constraint_type==SANE_CONSTRAINT_RANGE) ret = KScanOption::Range; else if (mDesc->constraint_type==SANE_CONSTRAINT_WORD_LIST) ret = KScanOption::StringList; else if (mDesc->constraint_type==SANE_CONSTRAINT_NONE) ret = KScanOption::SingleValue; else ret = KScanOption::Invalid; break; case SANE_TYPE_STRING: if (qstrcmp(mDesc->name, SANE_NAME_FILE)==0) ret = KScanOption::File; else if (mDesc->constraint_type==SANE_CONSTRAINT_STRING_LIST) ret = KScanOption::StringList; else ret = KScanOption::String; break; case SANE_TYPE_BUTTON: ret = KScanOption::Button; break; case SANE_TYPE_GROUP: ret = KScanOption::Group; break; default: qDebug() << "unsupported SANE type" << mDesc->type; ret = KScanOption::Invalid; break; } #ifdef DEBUG_GETSET qDebug() << "for SANE type" << mDesc->type << "returning" << ret; #endif return (ret); } bool KScanOption::set(int val) { if (!isValid() || mBuffer.isNull()) return (false); #ifdef DEBUG_GETSET qDebug() << "Setting" << mName << "to" << val; #endif int word_size; QVector qa; SANE_Word sw; switch (mDesc->type) { case SANE_TYPE_BUTTON: // Activate a button case SANE_TYPE_BOOL: // Assign a Boolean value sw = (val ? SANE_TRUE : SANE_FALSE); mBuffer = QByteArray(((const char *) &sw),sizeof(SANE_Word)); break; case SANE_TYPE_INT: // Fill the whole buffer with that value word_size = mDesc->size/sizeof(SANE_Word); qa.resize(word_size); sw = static_cast(val); qa.fill(sw); mBuffer = QByteArray(((const char *) qa.data()),mDesc->size); break; case SANE_TYPE_FIXED: // Fill the whole buffer with that value word_size = mDesc->size/sizeof(SANE_Word); qa.resize(word_size); sw = SANE_FIX(static_cast(val)); qa.fill(sw); mBuffer = QByteArray(((const char *) qa.data()),mDesc->size); break; default: //qDebug() << "Can't set" << mName << "with this type"; return (false); } mBufferClean = false; return (true); } bool KScanOption::set(double val) { if (!isValid() || mBuffer.isNull()) return (false); #ifdef DEBUG_GETSET qDebug() << "Setting" << mName << "to" << val; #endif int word_size; QVector qa; SANE_Word sw; switch (mDesc->type) { case SANE_TYPE_BOOL: // Assign a Boolean value sw = (val>0 ? SANE_TRUE : SANE_FALSE); mBuffer = QByteArray(((const char *) &sw),sizeof(SANE_Word)); break; case SANE_TYPE_INT: // Fill the whole buffer with that value word_size = mDesc->size/sizeof(SANE_Word); qa.resize(word_size); sw = static_cast(val); qa.fill(sw); mBuffer = QByteArray(((const char *) qa.data()),mDesc->size); break; case SANE_TYPE_FIXED: // Fill the whole buffer with that value word_size = mDesc->size/sizeof(SANE_Word); qa.resize(word_size); sw = SANE_FIX(val); qa.fill(sw); mBuffer = QByteArray(((const char *) qa.data()),mDesc->size); break; default: //qDebug() << "Can't set" << mName << "with this type"; return (false); } mBufferClean = false; return (true); } bool KScanOption::set(const int *val, int size) { if (!isValid() || mBuffer.isNull()) return (false); if (val==nullptr) return (false); #ifdef DEBUG_GETSET qDebug() << "Setting" << mName << "of size" << size; #endif int offset = 0; int word_size = mDesc->size/sizeof(SANE_Word); QVector qa(1+word_size); /* add 1 in case offset is needed */ #if 0 if( mDesc->constraint_type == SANE_CONSTRAINT_WORD_LIST ) { /* That means that the first entry must contain the size */ //qDebug() << "Size" << size << "word_size" << word_size << "mDescr-size"<< mDesc->size; qa[0] = (SANE_Word) 1+size; //qDebug() << "set length field to" << qa[0]; offset = 1; } #endif switch (mDesc->type) { case SANE_TYPE_INT: for (int i = 0; isize; if (offset) copybyte += sizeof(SANE_Word); //kdDebug(29000) << "Copying " << copybyte << " byte to options buffer" << endl; mBuffer = QByteArray(((const char *) qa.data()),copybyte); mBufferClean = false; return (true); } bool KScanOption::set(const QByteArray &buf) { if (!isValid() || mBuffer.isNull()) return (false); #ifdef DEBUG_GETSET qDebug() << "Setting" << mName << "to" << buf; #endif int val; int origSize; // Check whether the string value looks like a gamma table specification. // If it is, then convert it to a gamma table and set that. KGammaTable gt; if (gt.setFromString(buf)) { #ifdef DEBUG_GETSET qDebug() << "is a gamma table"; #endif return (set(>)); } /* On String-type the buffer gets malloced in Constructor */ switch (mDesc->type) { case SANE_TYPE_STRING: origSize = mBuffer.size(); mBuffer = QByteArray(buf.data(),(buf.length()+1)); mBuffer.resize(origSize); // restore original size break; case SANE_TYPE_INT: case SANE_TYPE_FIXED: bool ok; val = buf.toInt(&ok); if (ok) set(&val,1); else { //qDebug() << "Conversion of string value" << buf << "failed!"; return (false); } break; case SANE_TYPE_BOOL: val = (buf=="true") ? 1 : 0; set(val); break; default: //qDebug() << "Can't set" << mName << "with this type"; return (false); } mBufferClean = false; return (true); } // The parameter here must be 'const'. // Otherwise, a call of set() with a 'const KGammaTable *' argument appears // to be silently resolved to a call of set(int) without warning. bool KScanOption::set(const KGammaTable *gt) { if (!isValid() || mBuffer.isNull()) return (false); // Remember the set values if (mGammaTable!=nullptr) delete mGammaTable; mGammaTable = new KGammaTable(*gt); int size = mDesc->size/sizeof(SANE_Word); // size of scanner gamma table #ifdef DEBUG_GETSET qDebug() << "Setting gamma table for" << mName << "size" << size << "to" << gt->toString(); #endif const int *run = mGammaTable->getTable(size); // get table of that size QVector qa(size); // converted to SANE values switch (mDesc->type) { case SANE_TYPE_INT: for (int i = 0; i(run[i]); break; case SANE_TYPE_FIXED: for (int i = 0; i(run[i])); break; default: //qDebug() << "Can't set" << mName << "with this type"; return (false); } mBuffer = QByteArray(((const char *) (qa.constData())), mDesc->size); mBufferClean = false; return (true); } bool KScanOption::get(int *val) const { if (!isValid() || mBuffer.isNull()) return (false); SANE_Word sane_word; double d; switch (mDesc->type) { case SANE_TYPE_BOOL: /* Buffer has a SANE_Word */ sane_word = *((SANE_Word *) mBuffer.data()); *val = (sane_word==SANE_TRUE ? 1 : 0); break; case SANE_TYPE_INT: /* reading just the first is OK */ sane_word = *((SANE_Word *) mBuffer.data()); *val = sane_word; break; case SANE_TYPE_FIXED: /* reading just the first is OK */ d = SANE_UNFIX(*((SANE_Word *) mBuffer.data())); *val = static_cast(d); break; default: //qDebug() << "Can't get" << mName << "as this type"; return (false); } #ifdef DEBUG_GETSET qDebug() << "Returning" << mName << "as" << *val; #endif return (true); } QByteArray KScanOption::get() const { if (!isValid() || mBuffer.isNull()) return (""); QByteArray retstr; SANE_Word sane_word; /* Handle gamma-table correctly */ if (mWidgetType==KScanOption::GammaTable) { if (mGammaTable!=nullptr) retstr = mGammaTable->toString().toLocal8Bit(); } else { switch (mDesc->type) { case SANE_TYPE_BOOL: sane_word = *((SANE_Word *) mBuffer.data()); retstr = (sane_word==SANE_TRUE ? "true" : "false"); break; case SANE_TYPE_STRING: retstr = (const char *) mBuffer.data(); break; case SANE_TYPE_INT: sane_word = *((SANE_Word *) mBuffer.data()); retstr.setNum(sane_word); break; case SANE_TYPE_FIXED: sane_word = (SANE_Word) SANE_UNFIX(*((SANE_Word *) mBuffer.data())); retstr.setNum(sane_word); break; default: //qDebug() << "Can't get" << mName << "as this type"; retstr = "?"; break; } } #ifdef DEBUG_GETSET qDebug() << "Returning" << mName << "as" << retstr; #endif return (retstr); } bool KScanOption::get(KGammaTable *gt) const { if (mGammaTable==nullptr) return (false); // has not been set gt->setAll(mGammaTable->getGamma(), mGammaTable->getBrightness(), mGammaTable->getContrast()); #ifdef DEBUG_GETSET qDebug() << "Returning" << mName << "as" << gt->toString(); #endif return (true); } QList KScanOption::getList() const { const char **sstring = nullptr; QList strList; if (mDesc==nullptr) return (strList); if (mDesc->constraint_type==SANE_CONSTRAINT_STRING_LIST) { sstring = (const char **)mDesc->constraint.string_list; while (*sstring!=nullptr) { strList.append(*sstring); sstring++; } } else if (mDesc->constraint_type==SANE_CONSTRAINT_WORD_LIST) { const SANE_Int *sint = mDesc->constraint.word_list; int amount_vals = *sint; sint++; QString s; for (int i = 0; itype==SANE_TYPE_FIXED) s.sprintf("%f",SANE_UNFIX(*sint)); else s.sprintf("%d",*sint); sint++; strList.append(s.toLocal8Bit()); } } else if (mDesc->constraint_type==SANE_CONSTRAINT_RANGE && mWidgetType==KScanOption::Resolution) { double min,max; int imin,imax; getRange( &min, &max); imin = static_cast(min); imax = static_cast(max); for (const int *ip = resList; *ip!=0; ++ip) { if (*ipimax) continue; strList.append(QString::number(*ip).toLocal8Bit()); } } return (strList); } bool KScanOption::getRange(double *minp, double *maxp, double *quantp) const { if (mDesc==nullptr) return (false); double min = 0.0; double max = 0.0; double quant = -1; if (mDesc->constraint_type==SANE_CONSTRAINT_RANGE) { const SANE_Range *r = mDesc->constraint.range; if (mDesc->type==SANE_TYPE_FIXED) { min = SANE_UNFIX(r->min); max = SANE_UNFIX(r->max); quant = SANE_UNFIX(r->quant); } else { min = r->min; max = r->max; quant = r->quant; } } else if (mDesc->constraint_type==SANE_CONSTRAINT_WORD_LIST) { // Originally done in KScanOption::getRangeFromList() const SANE_Int *wl = mDesc->constraint.word_list; const int num = wl[0]; double value; for (int i = 1; i<=num; ++i) { if (mDesc->type==SANE_TYPE_FIXED) value = SANE_UNFIX(wl[i]); else value = wl[i]; if (i==1 || valuemax) max = value; } if (num>=2) quant = (max-min)/(num-1); // synthesise from total range } else { //qDebug() << "Not a range type" << mDesc->name; return (false); } *minp = min; *maxp = max; if (quantp!=nullptr) *quantp = quant; return (true); } KScanControl *KScanOption::createWidget(QWidget *parent) { if (!isValid()) { //qDebug() << "Option is not valid!"; return (nullptr); } delete mControl; mControl = nullptr; // dispose of the old control if (mDesc!=nullptr) mText = i18n(mDesc->title); //qDebug() << "type" << mWidgetType << "text" << mText; KScanControl *w = nullptr; switch (mWidgetType) { case KScanOption::Bool: w = createToggleButton(parent, mText); // toggle button break; case KScanOption::SingleValue: w = createNumberEntry(parent, mText); // numeric entry break; case KScanOption::Range: w = createSlider(parent, mText); // slider and spinbox break; case KScanOption::Resolution: w = createComboBox(parent, mText); // special resolution combo break; case KScanOption::GammaTable: //qDebug() << "GammaTable not implemented here"; break; // no widget for this case KScanOption::StringList: w = createComboBox(parent, mText); // string list combo break; case KScanOption::String: w = createStringEntry(parent, mText); // free text entry break; case KScanOption::File: w = createFileField(parent, mText); // file name requester break; case KScanOption::Group: w = createGroupSeparator(parent, mText); // group separator break; case KScanOption::Button: w = createActionButton(parent, mText); // button to do action break; default: //qDebug() << "unknown control type " << mWidgetType; break; } if (w!=nullptr) { mControl = w; updateList(); // set list for combo box switch (w->type()) { case KScanControl::Number: // numeric control connect(w, SIGNAL(settingChanged(int)), SLOT(slotWidgetChange(int))); break; case KScanControl::Text: // text control connect(w, SIGNAL(settingChanged(const QString &)), SLOT(slotWidgetChange(const QString &))); break; case KScanControl::Button: // push button connect(w, SIGNAL(returnPressed()), SLOT(slotWidgetChange())); break; case KScanControl::Group: // group separator break; // nothing to do here } if (mDesc!=nullptr) // set tool tip { if (qstrlen(mDesc->desc)>0) // if there is a SANE description { QString tt = i18n(mDesc->desc); // KDE tooltips do not normally end with a full stop, unless // they are multi-sentence. But the SANE descriptions often do, // so trim it off for consistency. Is this a good thing to do // in non-western languages? if (tt.endsWith('.') && tt.count(". ")==0) tt.chop(1); // Force the format to be rich text so that it will be word wrapped // at a sensible width, see documentation for QToolTip. w->setToolTip(""+tt); } } // No accelerators for advanced options, so as not to soak up // too many of the available accelerators for controls that are // rarely going to be used. See also getLabel(). if (!isCommonOption()) KAcceleratorManager::setNoAccel(w); } reload(); // check if active, enabled etc. if (w!=nullptr) redrawWidget(); return (w); } inline KScanControl *KScanOption::createToggleButton(QWidget *parent, const QString &text) { return (new KScanCheckbox(parent, text)); } inline KScanControl *KScanOption::createComboBox(QWidget *parent, const QString &text) { return (new KScanCombo(parent, text)); } inline KScanControl *KScanOption::createStringEntry(QWidget *parent, const QString &text) { return (new KScanStringEntry(parent, text)); } inline KScanControl *KScanOption::createNumberEntry(QWidget *parent, const QString &text) { return (new KScanNumberEntry(parent, text)); } inline KScanControl *KScanOption::createSlider(QWidget *parent, const QString &text) { double min, max; getRange(&min, &max); return (new KScanSlider(parent, text, min, max, true)); } inline KScanControl *KScanOption::createFileField(QWidget *parent, const QString &text) { return (new KScanFileRequester(parent, text)); } inline KScanControl *KScanOption::createGroupSeparator(QWidget *parent, const QString &text) { return (new KScanGroup(parent, text)); } inline KScanControl *KScanOption::createActionButton(QWidget *parent, const QString &text) { return (new KScanPushButton(parent, text)); } QLabel *KScanOption::getLabel(QWidget *parent, bool alwaysBuddy) const { if (mControl==nullptr) return (nullptr); KSqueezedTextLabel *l = new KSqueezedTextLabel(mControl->label(), parent); if (isCommonOption() || alwaysBuddy) l->setBuddy(mControl->focusProxy()); return (l); } QLabel *KScanOption::getUnit(QWidget *parent) const { if (mControl==nullptr) return (nullptr); QString s; switch (mDesc->unit) { case SANE_UNIT_NONE: break; case SANE_UNIT_PIXEL: s = i18n("pixels"); break; case SANE_UNIT_BIT: s = i18n("bits"); break; case SANE_UNIT_MM: s = i18n("mm"); break; case SANE_UNIT_DPI: s = i18n("dpi"); break; case SANE_UNIT_PERCENT: s = i18n("%"); break; case SANE_UNIT_MICROSECOND: s = i18n("\302\265sec"); break; default: break; } if (s.isEmpty()) return (nullptr); // no unit label QLabel *l = new QLabel(s, parent); return (l); } void KScanOption::allocForDesc() { if (mDesc==nullptr) return; switch (mDesc->type) { case SANE_TYPE_INT: case SANE_TYPE_FIXED: case SANE_TYPE_STRING: allocBuffer(mDesc->size); break; case SANE_TYPE_BOOL: allocBuffer(sizeof(SANE_Word)); break; default: if (mDesc->size>0) allocBuffer(mDesc->size); break; } } void KScanOption::allocBuffer(long size) { if (size<1) return; #ifdef DEBUG_MEM //qDebug() << "Allocating" << size << "bytes for" << name; #endif mBuffer.resize(size); // set buffer size if (mBuffer.isNull()) // check allocation worked??? { qWarning() << "Fatal: Allocating" << size << "bytes for" << mName << "failed!"; return; } mBuffer.fill(0); // clear allocated buffer } diff --git a/libkookascan/scansourcedialog.cpp b/libkookascan/scansourcedialog.cpp index 220d5c3..ab804e5 100644 --- a/libkookascan/scansourcedialog.cpp +++ b/libkookascan/scansourcedialog.cpp @@ -1,249 +1,249 @@ /* This file is part of the KDE Project Copyright (C) 2000 Klaas Freitag This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "scansourcedialog.h" #include #include #include #include #include #include #include #include "kscancontrols.h" extern "C" { #include } #ifndef SANE_NAME_DOCUMENT_FEEDER #define SANE_NAME_DOCUMENT_FEEDER "Automatic Document Feeder" #endif /** * Internal private class for ScanSourceDialog */ class ScanSourceDialogPrivate { public: KScanCombo *sources; QGroupBox *bgroup; QVBoxLayout *bgroupLayout; QRadioButton *rbADFTillEnd, *rbADFOnce; AdfBehaviour adf; bool adf_enabled; public: ScanSourceDialogPrivate() : sources(0), bgroup(0), bgroupLayout(0), rbADFTillEnd(0), rbADFOnce(0), adf(ADF_OFF), adf_enabled(false) { } }; ScanSourceDialog::ScanSourceDialog(QWidget *parent, const QList list, AdfBehaviour adfBehave) : KDialog(parent) { d = new ScanSourceDialogPrivate(); setObjectName("ScanSourceDialog"); setButtons(KDialog::Ok | KDialog::Cancel); setCaption(i18n("Custom Gamma Tables")); setModal(true); showButtonSeparator(true); KVBox *vbox = new KVBox(this); setMainWidget(vbox); (void) new QLabel(i18n("Source selection
" "Note that you may see more sources than actually exist"), vbox); /* Combo Box for sources */ d->sources = new KScanCombo(vbox, QString()); d->sources->setList(list); connect(d->sources, SIGNAL(activated(int)), SLOT(slotChangeSource(int))); if (sourceAdfEntry() > -1) { d->bgroup = new QGroupBox(i18n("Advanced ADF Options")); /* Two buttons inside */ d->bgroupLayout = new QVBoxLayout(); d->rbADFTillEnd = new QRadioButton(i18n("Scan until ADF reports out of paper")); connect(d->rbADFTillEnd, SIGNAL(toggled(bool)), this, SLOT(slotNotifyADF(bool))); d->bgroupLayout->addWidget(d->rbADFTillEnd); d->rbADFOnce = new QRadioButton(i18n("Scan only one sheet of ADF per click")); connect(d->rbADFOnce, SIGNAL(toggled(bool)), this, SLOT(slotNotifyADF(bool))); d->bgroupLayout->addWidget(d->rbADFOnce); d->bgroup->setLayout(d->bgroupLayout); switch (adfBehave) { case ADF_OFF: d->rbADFOnce->setChecked(true); enableBGroup(false); break; case ADF_SCAN_ONCE: d->rbADFOnce->setChecked(true); break; case ADF_SCAN_ALONG: d->rbADFTillEnd->setChecked(true); break; default: //qDebug() << "Undefined Source!"; // Hmmm. break; } d->adf = adfBehave; } } ScanSourceDialog::~ScanSourceDialog() { if (d) { delete d; - d = NULL; + d = nullptr; } } QString ScanSourceDialog::getText(void) const { return (d->sources->text()); } AdfBehaviour ScanSourceDialog::getAdfBehave(void) const { return (d->adf); } void ScanSourceDialog::slotNotifyADF(bool checked) { if (checked) { // check which one sent the event: d->adf = ADF_OFF; if (sender() == d->rbADFOnce) { d->adf = ADF_SCAN_ONCE; } else if (sender() == d->rbADFTillEnd) { d->adf = ADF_SCAN_ALONG; } } #if 0 // debug( "reported adf-select %d", adf_group ); /* this seems to be broken, adf_text is a visible string? * needs rework if SANE 2 comes up which supports i18n */ QString adf_text = getText(); adf = ADF_OFF; if (adf_text == "Automatic Document Feeder" || adf_text == "ADF") { if (adf_group == 0) { adf = ADF_SCAN_ALONG; } else { adf = ADF_SCAN_ONCE; } } #endif } void ScanSourceDialog::slotChangeSource(int i) { - if (d->bgroup == NULL) { + if (d->bgroup == nullptr) { return; } if (i == sourceAdfEntry()) { /* Adf was switched on */ enableBGroup(true); d->rbADFOnce->setChecked(true); d->adf = ADF_SCAN_ALONG; d->adf_enabled = true; } else { enableBGroup(false); // d->adf = ADF_OFF; d->adf_enabled = false; } } int ScanSourceDialog::sourceAdfEntry() const { - if (d->sources == NULL) { + if (d->sources == nullptr) { return (-1); } int cou = d->sources->count(); for (int i = 0; i < cou; i++) { QString q = d->sources->textAt(i); // TODO: this enables advanced ADF options, not implemented yet #if 0 if (q == "ADF" || q == SANE_NAME_DOCUMENT_FEEDER) { return (i); } #endif } return (-1); } void ScanSourceDialog::slotSetSource(const QString &source) { - if (d->sources == NULL) { + if (d->sources == nullptr) { return; } //qDebug() << "Setting source to" << source; enableBGroup(false); d->adf_enabled = false ; for (int i = 0; i < d->sources->count(); i++) { if (d->sources->textAt(i) == source) { d->sources->setValue(i); if (source == QString::number(sourceAdfEntry())) { if (d->bgroup) { enableBGroup(true); } d->adf_enabled = true; } break; } } } /** QGroupBox doesn't have a way to enable / disable all contained radio buttons, so we need to implement a dedicated function for it. */ void ScanSourceDialog::enableBGroup(bool enable) { if (d->bgroup) { d->rbADFOnce->setEnabled(enable); d->rbADFTillEnd->setEnabled(enable); } } diff --git a/plugins/ocr/ocrad/ocrocraddialog.cpp b/plugins/ocr/ocrad/ocrocraddialog.cpp index ab2d00b..b03bca7 100644 --- a/plugins/ocr/ocrad/ocrocraddialog.cpp +++ b/plugins/ocr/ocrad/ocrocraddialog.cpp @@ -1,327 +1,327 @@ /************************************************************************ * * * This file is part of Kooka, a scanning/OCR application using * * Qt and KDE Frameworks . * * * * Copyright (C) 2003-2016 Klaas Freitag * * Jonathan Marten * * * * Kooka is free software; you can redistribute it and/or modify it * * under the terms of the GNU Library General Public License as * * published by the Free Software Foundation and appearing in the * * file COPYING included in the packaging of this file; either * * version 2 of the License, or (at your option) any later version. * * * * As a special exception, permission is given to link this program * * with any version of the KADMOS OCR/ICR engine (a product of * * reRecognition GmbH, Kreuzlingen), and distribute the resulting * * executable without including the source code for KADMOS in the * * source distribution. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public * * License along with this program; see the file COPYING. If * * not, see . * * * ************************************************************************/ #include "ocrocraddialog.h" #include #include #include #include #include #include #include #include #include #include #include #include "kookaimage.h" #include "kookapref.h" #include "kookasettings.h" #include "kscancontrols.h" #include "dialogbase.h" #include "ocrocradengine.h" OcrOcradDialog::OcrOcradDialog(AbstractOcrEngine *plugin, QWidget *pnt) : AbstractOcrDialogue(plugin, pnt), m_setupWidget(nullptr), m_orfUrlRequester(nullptr), - m_layoutMode(0), + m_layoutMode(nullptr), m_ocrCmd(QString()), m_versionNum(0), m_versionStr(QString()) { } bool OcrOcradDialog::setupGui() { AbstractOcrDialogue::setupGui(); // build the standard GUI // Options available vary with the OCRAD version. So we need to find // the OCRAD binary and get its version before creating the GUI. m_ocrCmd = engine()->findExecutable(&KookaSettings::ocrOcradBinary, KookaSettings::self()->ocrOcradBinaryItem()); if (!m_ocrCmd.isEmpty()) getVersion(m_ocrCmd); // found, get its version else // not found or invalid { engine()->setErrorText(i18n("The OCRAD executable is not configured or is not available.")); } QWidget *w = addExtraSetupWidget(); QGridLayout *gl = new QGridLayout(w); // Layout detection mode, dependent on OCRAD version KConfigSkeletonItem *ski = KookaSettings::self()->ocrOcradLayoutDetectionItem(); Q_ASSERT(ski!=nullptr); QLabel *l = new QLabel(ski->label(), w); gl->addWidget(l, 0, 0); m_layoutMode = new QComboBox(w); m_layoutMode->addItem(i18n("No Layout Detection"), 0); if (m_versionNum >= 18) { // OCRAD 0.18 or later // has only on/off m_layoutMode->addItem(i18n("Layout Detection"), 1); } else { // OCRAD 0.17 or earlier // had these 3 options m_layoutMode->addItem(i18n("Column Detection"), 1); m_layoutMode->addItem(i18n("Full Layout Detection"), 2); } m_layoutMode->setCurrentIndex(KookaSettings::ocrOcradLayoutDetection()); m_layoutMode->setToolTip(ski->toolTip()); gl->addWidget(m_layoutMode, 0, 1); l->setBuddy(m_layoutMode); gl->setRowMinimumHeight(1, DialogBase::verticalSpacing()); // Character set, auto detected values QStringList vals = getValidValues("charset"); ski = KookaSettings::self()->ocrOcradCharsetItem(); Q_ASSERT(ski!=nullptr); l = new QLabel(ski->label(), w); gl->addWidget(l, 2, 0); m_characterSet = new QComboBox(w); m_characterSet->setToolTip(ski->toolTip()); m_characterSet->addItem(i18n("(default)"), false); for (QStringList::const_iterator it = vals.constBegin(); it != vals.constEnd(); ++it) { m_characterSet->addItem(*it, true); } if (vals.count() == 0) m_characterSet->setEnabled(false); else { int ix = m_characterSet->findText(KookaSettings::ocrOcradCharset()); if (ix != -1) m_characterSet->setCurrentIndex(ix); } gl->addWidget(m_characterSet, 2, 1); l->setBuddy(m_characterSet); // Filter, auto detected values vals = getValidValues("filter"); ski = KookaSettings::self()->ocrOcradFilterItem(); Q_ASSERT(ski!=nullptr); l = new QLabel(ski->label(), w); gl->addWidget(l, 3, 0); m_filter = new QComboBox(w); m_filter->setToolTip(ski->toolTip()); m_filter->addItem(i18n("(default)"), false); for (QStringList::const_iterator it = vals.constBegin(); it != vals.constEnd(); ++it) { m_filter->addItem(*it, true); } if (vals.count() == 0) m_filter->setEnabled(false); else { int ix = m_filter->findText(KookaSettings::ocrOcradFilter()); if (ix != -1) m_filter->setCurrentIndex(ix); } gl->addWidget(m_filter, 3, 1); l->setBuddy(m_filter); // Transform, auto detected values vals = getValidValues("transform"); ski = KookaSettings::self()->ocrOcradTransformItem(); Q_ASSERT(ski!=nullptr); l = new QLabel(ski->label(), w); gl->addWidget(l, 4, 0); m_transform = new QComboBox(w); m_transform->setToolTip(ski->toolTip()); m_transform->addItem(i18n("(default)"), false); for (QStringList::const_iterator it = vals.constBegin(); it != vals.constEnd(); ++it) { m_transform->addItem(*it, true); } if (vals.count() == 0) m_transform->setEnabled(false); else { int ix = m_transform->findText(KookaSettings::ocrOcradTransform()); if (ix != -1) m_transform->setCurrentIndex(ix); } gl->addWidget(m_transform, 4, 1); l->setBuddy(m_transform); gl->setRowMinimumHeight(5, DialogBase::verticalSpacing()); // Invert option, on/off ski = KookaSettings::self()->ocrOcradInvertItem(); Q_ASSERT(ski!=nullptr); m_invert = new QCheckBox(ski->label(), w); m_invert->setChecked(KookaSettings::ocrOcradInvert()); m_invert->setToolTip(ski->toolTip()); gl->addWidget(m_invert, 6, 1, Qt::AlignLeft); gl->setRowMinimumHeight(7, DialogBase::verticalSpacing()); // Threshold, on/off and slider ski = KookaSettings::self()->ocrOcradThresholdEnableItem(); Q_ASSERT(ski!=nullptr); m_thresholdEnable = new QCheckBox(ski->label(), w); m_thresholdEnable->setChecked(KookaSettings::ocrOcradThresholdEnable()); m_thresholdEnable->setToolTip(ski->toolTip()); gl->addWidget(m_thresholdEnable, 8, 1, Qt::AlignLeft); ski = KookaSettings::self()->ocrOcradThresholdValueItem(); Q_ASSERT(ski!=nullptr); m_thresholdSlider = new KScanSlider(w, ski->label(), 0, 100); m_thresholdSlider->setValue(KookaSettings::ocrOcradThresholdValue()); m_thresholdSlider->setToolTip(ski->toolTip()); m_thresholdSlider->spinBox()->setSuffix("%"); gl->addWidget(m_thresholdSlider, 9, 1); l = new QLabel(m_thresholdSlider->label(), w); gl->addWidget(l, 9, 0); l->setBuddy(m_thresholdSlider); connect(m_thresholdEnable, &QCheckBox::toggled, m_thresholdSlider, &KScanSlider::setEnabled); m_thresholdSlider->setEnabled(m_thresholdEnable->isChecked()); gl->setRowStretch(10, 1); // for top alignment gl->setColumnStretch(1, 1); ocrShowInfo(m_ocrCmd, m_versionStr); // show the binary and version progressBar()->setMaximum(0); // progress animation only m_setupWidget = w; return (!m_ocrCmd.isEmpty()); } void OcrOcradDialog::slotWriteConfig() { AbstractOcrDialogue::slotWriteConfig(); KookaSettings::setOcrOcradBinary(getOCRCmd()); KookaSettings::setOcrOcradLayoutDetection(m_layoutMode->currentIndex()); int ix = m_characterSet->currentIndex(); QString value = (m_characterSet->itemData(ix).toBool() ? m_characterSet->currentText() : QString()); KookaSettings::setOcrOcradCharset(value); ix = m_filter->currentIndex(); value = (m_filter->itemData(ix).toBool() ? m_filter->currentText() : QString()); KookaSettings::setOcrOcradFilter(value); ix = m_transform->currentIndex(); value = (m_transform->itemData(ix).toBool() ? m_transform->currentText() : QString()); KookaSettings::setOcrOcradTransform(value); KookaSettings::setOcrOcradInvert(m_invert->isChecked()); KookaSettings::setOcrOcradThresholdEnable(m_thresholdEnable->isChecked()); KookaSettings::setOcrOcradThresholdValue(m_thresholdSlider->value()); } void OcrOcradDialog::enableFields(bool enable) { m_setupWidget->setEnabled(enable); } /* Later: Allow interactive loading of ORF files */ QString OcrOcradDialog::orfUrl() const { if (m_orfUrlRequester != nullptr) { return (m_orfUrlRequester->url().url()); } else { return (QString()); } } void OcrOcradDialog::getVersion(const QString &bin) { //qDebug() << "of" << bin; if (bin.isEmpty()) { return; } KProcess proc; proc.setOutputChannelMode(KProcess::MergedChannels); proc << bin << "-V"; int status = proc.execute(5000); if (status == 0) { QByteArray output = proc.readAllStandardOutput(); QRegExp rx("GNU Ocrad (version )?([\\d\\.]+)"); if (rx.indexIn(output) > -1) { m_ocrCmd = bin; m_versionStr = rx.cap(2); m_versionNum = m_versionStr.mid(2).toInt(); //qDebug() << "version" << m_versionStr << "=" << m_versionNum; } } else { //qDebug() << "failed with status" << status; m_versionStr = i18n("Error"); } } QStringList OcrOcradDialog::getValidValues(const QString &opt) { QStringList result; KConfigSkeletonItem *ski = KookaSettings::self()->ocrOcradValidValuesItem(); Q_ASSERT(ski!=nullptr); QString groupName = QString("%1_v%2").arg(ski->group()).arg(m_versionStr); KConfigGroup grp = KookaSettings::self()->config()->group(groupName); if (grp.hasKey(opt)) { // values in config already //qDebug() << "option" << opt << "already in config"; result = grp.readEntry(opt, QStringList()); } else { // not in config, need to extract if (!m_ocrCmd.isEmpty()) { KProcess proc; proc.setOutputChannelMode(KProcess::MergedChannels); proc << m_ocrCmd << QString("--%1=help").arg(opt); proc.execute(5000); // Ignore return status, because '--OPTION=help' returns exit code 1 QByteArray output = proc.readAllStandardOutput(); QRegExp rx("Valid .*are:([^\n]+)"); if (rx.indexIn(output) > -1) { QString values = rx.cap(1); result = rx.cap(1).split(QRegExp("\\s+"), QString::SkipEmptyParts); } else { //qDebug() << "cannot get values, no match in" << output; } } else { //qDebug() << "cannot get values, no binary"; } } //qDebug() << "values for" << opt << "=" << result.join(","); if (!result.isEmpty()) { grp.writeEntry(opt, result); // save for next time grp.sync(); } return (result); }