diff --git a/kooka/kookapref.cpp b/kooka/kookapref.cpp index f2ab2a6..e2924be 100644 --- a/kooka/kookapref.cpp +++ b/kooka/kookapref.cpp @@ -1,304 +1,304 @@ /************************************************************************ * * * This file is part of Kooka, a scanning/OCR application using * * Qt and KDE Frameworks . * * * * Copyright (C) 2000-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 "kookapref.h" #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STRERROR #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #include #include #include #include #include #include #include #include #include #include "prefspages.h" #include "kookasettings.h" #include "dialogstatesaver.h" KookaPref::KookaPref(QWidget *parent) : KPageDialog(parent) { setObjectName("KookaPref"); setModal(true); setStandardButtons(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults); buttonBox()->button(QDialogButtonBox::Ok)->setDefault(true); buttonBox()->button(QDialogButtonBox::RestoreDefaults)->setIcon(QIcon::fromTheme("edit-undo")); setWindowTitle(i18n("Preferences")); createPage(new KookaGeneralPage(this), i18n("General"), i18n("General Options"), "configure"); createPage(new KookaStartupPage(this), i18n("Startup"), i18n("Startup Options"), "system-run"); createPage(new KookaSavingPage(this), i18n("Image Saving"), i18n("Image Saving Options"), "document-save"); createPage(new KookaThumbnailPage(this), i18n("Gallery & Thumbnails"), i18n("Image Gallery and Thumbnail View"), "view-list-icons"); createPage(new KookaOcrPage(this), i18n("OCR"), i18n("Optical Character Recognition"), "ocr"); connect(buttonBox()->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &KookaPref::slotSaveSettings); connect(buttonBox()->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &KookaPref::slotSaveSettings); connect(buttonBox()->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this, &KookaPref::slotSetDefaults); setMinimumSize(670, 380); new DialogStateSaver(this); } int KookaPref::createPage(KookaPrefsPage *page, const QString &name, const QString &header, const char *icon) { QVBoxLayout *top = static_cast(page->layout()); if (top != NULL) { top->addStretch(1); } KPageWidgetItem *item = addPage(page, name); item->setHeader(header); item->setIcon(QIcon::fromTheme(icon)); int idx = mPages.count(); // index of new item mPages.append(item); return (idx); // index of item added } void KookaPref::slotSaveSettings() { for (int i = 0; i < mPages.size(); ++i) { KookaPrefsPage *page = static_cast(mPages[i]->widget()); page->saveSettings(); } KSharedConfig::openConfig()->sync(); emit dataSaved(); } void KookaPref::slotSetDefaults() { for (int i = 0; i < mPages.size(); ++i) { KookaPrefsPage *page = static_cast(mPages[i]->widget()); page->defaultSettings(); } } void KookaPref::showPageIndex(int page) { if (page >= 0 && page < mPages.size()) { setCurrentPage(mPages[page]); } } int KookaPref::currentPageIndex() { return (mPages.indexOf(currentPage())); } static QString tryFindBinary(const QString &bin, const QString &configPath) { // First check for a full path in the config file. // Not sure what the point of the 'contains' test is here. if (!configPath.isEmpty() && configPath.contains(bin)) { QFileInfo fi(configPath); // check for valid executable if (fi.exists() && fi.isExecutable() && !fi.isDir()) return (fi.absoluteFilePath()); } // Otherwise try to find the program on the user's search PATH return (QStandardPaths::findExecutable(bin)); } QString KookaPref::tryFindGocr() { return (tryFindBinary("gocr", KookaSettings::ocrGocrBinary())); } QString KookaPref::tryFindOcrad(void) { return (tryFindBinary("ocrad", KookaSettings::ocrOcradBinary())); } // Support for the gallery location - moved here from Previewer class in libkscan QString KookaPref::sGalleryRoot = QString::null; // global resolved location // The static variable above ensures that the user is only asked // at most once in an application run. QString KookaPref::galleryRoot() { if (sGalleryRoot.isNull()) { sGalleryRoot = findGalleryRoot(); } return (sGalleryRoot); } // Get the user's configured KDE documents path. It may not exist yet, in // which case QDir::canonicalPath() will fail - try QDir::absolutePath() in // this case. If all else fails then the last resort is the home directory. static QString docsPath() { QString docpath = QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).canonicalPath(); if (docpath.isEmpty()) { docpath = QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).absolutePath(); } if (docpath.isEmpty()) { docpath = getenv("HOME"); } return (docpath); } // TODO: maybe save a .directory file there which shows a 'scanner' logo? static QString createGallery(const QDir &d, bool *success = NULL) { if (!d.exists()) { // does not already exist if (mkdir(d.path().toLocal8Bit(), 0755) != 0) { // using mkdir(2) so that we can // get the errno if it fails #ifdef HAVE_STRERROR const char *reason = strerror(errno); #else const char *reason = ""; #endif QString docs = docsPath(); KMessageBox::error(NULL, xi18nc("@info", "Unable to create the directory %1" "for the Kooka gallery" #ifdef HAVE_STRERROR " - %3." #else "." #endif "Your document directory %2" "will be used." - "Check the document directory setting and permissions.", + "Check the document directory setting and permissions.", d.absolutePath(), docs, reason), i18n("Error creating gallery")); if (success != NULL) *success = false; return (docs); } } if (success != NULL) { *success = true; } return (d.absolutePath()); } QString KookaPref::findGalleryRoot() { QString galleryName = KookaSettings::galleryName(); // may be base name or absolute path if (galleryName.isEmpty()) { qWarning() << "Gallery name not configured"; return (QString::null); } QString oldpath = QStandardPaths::locate(QStandardPaths::AppDataLocation, "ScanImages", QStandardPaths::LocateDirectory); bool oldexists = !oldpath.isEmpty(); QString newpath(galleryName); if (!QDir::isAbsolutePath(galleryName)) { QString docpath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); newpath = docpath+'/'+galleryName; // already an absolute path } QDir newdir(newpath); bool newexists = newdir.exists(); qDebug() << "old" << oldpath << "exists" << oldexists; qDebug() << "new" << newpath << "exists" << newexists; QString dir; if (!oldexists && !newexists) { // no directories present dir = createGallery(newdir); // create and use new } else if (!oldexists && newexists) { // only new exists dir = newpath; // fine, just use that } else if (oldexists && !newexists) { // only old exists if (KMessageBox::questionYesNo(NULL, xi18nc("@info", "An old Kooka gallery was found at %1." "The preferred new location is now %2." "Do you want to create a new gallery at the new location?", oldpath, newpath), i18n("Create New Gallery"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "GalleryNoMigrate") == KMessageBox::Yes) { // yes, create new bool created; dir = createGallery(newdir, &created); if (created) { // new created OK KMessageBox::information(NULL, xi18nc("@info", "Kooka will use the new gallery, %1." "If you wish to add the images from your old gallery %2," "then you may do so by simply copying or moving the files.", newpath, oldpath), i18n("New Gallery Created"), QString::null, KMessageBox::Notify | KMessageBox::AllowLink); } } else { // no, don't create dir = oldpath; // stay with old location } } else { // both exist KMessageBox::information(NULL, xi18nc("@info", "Kooka will use the new gallery, %1." "If you wish to add the images from your old gallery, %2," "then you may do so by simply copying or moving the files.", newpath, oldpath), i18n("Old Gallery Exists"), "GalleryNoRemind", KMessageBox::Notify | KMessageBox::AllowLink); dir = newpath; // just use new one } if (!dir.endsWith("/")) dir += "/"; qDebug() << "using" << dir; return (dir); } diff --git a/libkookascan/kscanoption.h b/libkookascan/kscanoption.h index 4348fc2..ccf1088 100644 --- a/libkookascan/kscanoption.h +++ b/libkookascan/kscanoption.h @@ -1,525 +1,525 @@ /* This file is part of the KDE Project -*- mode:c++; -*- 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. */ #ifndef KSCANOPTION_H #define KSCANOPTION_H #include "kookascan_export.h" #include #include #include "kscandevice.h" extern "C" { #include } class QLabel; class KGammaTable; class KScanControl; class KScanDevice; /** * @short A single scanner parameter. * - * A scanner may support any number of these parameters, some are are + * A scanner may support any number of these parameters, some are * well-known and common to almost all scanners while others may be * model-specific. * * Most options have an associated GUI element (a @c KScanControl), precisely * which sort of control depending on the type and constraint of the * scanner parameter. * * Only one KScanOption for each scanner parameter may exist. All options * for a particular scanner are owned by a KScanDevice, and options may only * be created by KScanDevice::getOption(). This ensures that all accesses * to a scanner parameter are consistent. * * KScanOption implements an internal memory buffer as an intermediary between * the scanner and its caller. There are four basic operations implemented to * access and control the scanner parameters: * * - @c set - Copy data from a variable or structure of an appropriate type * into the internal memory buffer. * * - @c apply - Send the data from the internal memory buffer to the scanner. * * - @c reload - Fetch the scanner data into the internal memory buffer. * * - @c get - Read the data from the internal memory buffer into a variable * or structure of an appropriate type. * * @author Klaas Freitag * @author Jonathan Marten **/ class KOOKASCAN_EXPORT KScanOption : public QObject { Q_OBJECT public: /** * Check whether the option is valid: that is, the parameter is known * by the scanner. * * @return @c true if the option is valid **/ bool isValid() const { return (mDesc != NULL); } /** * Check whether the option is initialised: that is, if the initial * value of the parameter has been read from the scanner. * * @return @c true if the option has been initialised **/ bool isInitialised() const { return (!mBufferClean); } /** * Check whether the option is a group, if so this is a title only * and many of the operations will not be available. * * @return @c true if the option is a group **/ bool isGroup() const { return (mIsGroup); } /** * Check whether the option value can be read from the scanner * (SANE_CAP_SOFT_SELECT with some special exceptions). Some * options that cannot be read may still be able to be set, * use @c isSoftwareSettable() to check. * * @return @c true if the option is readable * @see isSoftwareSettable **/ bool isReadable() const { return (mIsReadable); } /** * Check whether the option is auto settable (SANE_CAP_AUTOMATIC): * that is, if the scanner can choose a setting for the option * automatically. * * @return @c true if the option can be set automatically **/ bool isAutoSettable() const { return (mDesc!=NULL && (mDesc->cap & SANE_CAP_AUTOMATIC)); } /** * Check whether the option is a common option (not SANE_CAP_ADVANCED). * * @return @c true if the option is a common option, * @c false if it is an advanced option. **/ bool isCommonOption() const { return (mDesc!=NULL && !(mDesc->cap & SANE_CAP_ADVANCED)); } /** * Check whether the option should be set as a priority, before any * other non-priority options. This a workaround for some scanners * which may reset other options when this option is changed. **/ bool isPriorityOption() const { return (mIsPriority); } /** * Check whether the option is currently active (SANE_OPTION_IS_ACTIVE). * This may change at runtime depending on the settings of other options. * * @return @c true if the option is currently active **/ bool isActive() const { return (mDesc!=NULL && SANE_OPTION_IS_ACTIVE(mDesc->cap)); } /** * Check whether the option is can be set by software * (SANE_OPTION_IS_SETTABLE). Some scanner options cannot be set, * use @c isReadable() to check if they can be read. * * @return @c true if the option can be set * @see isReadable **/ bool isSoftwareSettable() const { return (mDesc!=NULL && SANE_OPTION_IS_SETTABLE(mDesc->cap)); } /** * Check whether the option has an associated GUI element * (not all types of options do). * * @return @c true if the option has a GUI widget **/ bool isGuiElement() const { return (mControl != NULL); } /** * Check whether the option value has been sent to the scanner: * that is, whether @c apply() has been used. This is used by * @c KScanDevice to maintain its list of "dirty" options. * * @return @c true if the option has been applied * @see KScanDevice * @see setApplied **/ bool isApplied() const { return (mApplied); } /** * Set or clear the "applied" flag. * * @param app New value for the flag * @see isApplied * @see apply **/ void setApplied(bool app = true) { mApplied = app; } /** * Set the option value. * * @param val A new integer value * @return @c true if the value was set successfully **/ bool set(int val); /** * Set the option value. * * @param val A new @c double floating point value * @return @c true if the value was set successfully **/ bool set(double val); /** * Set the option value. * * @param val A new array of integer values * @param size The length of the array * @return @c true if the value was set successfully **/ bool set(const int *val, int size); /** * Set the option value. * * @param val A new formatted string value * @return @c true if the value was set successfully **/ bool set(const QByteArray &val); /** * Set the option value. * * @param val A new boolean value * @return @c true if the value was set successfully **/ bool set(bool val) { return (set(val ? 1 : 0)); } /** * Set the option value. * * @param gt A new gamma table * @return @c true if the value was set successfully **/ bool set(const KGammaTable *gt); /** * Retrieve the option value. * * @param val An integer to receive the value read * @return @c true if the value was read successfully * * @note If the scanner parameter is an array, only the first value * is retrieved from it. **/ bool get(int *val) const; /** * Retrieve the option value. * * @param gt A gamma table to receive the value read * @return @c true in all cases (unless the @p gt parameter is @c NULL) **/ bool get(KGammaTable *gt) const; /** * Retrieve the option value. * * @return The formatted string value, or @c "?" if the value could * not be read. **/ QByteArray get() const; /** * Retrieve a list of all possible option values. * * @return A list of formatted string values (as would be returned * by @c get()) * * @note This works for options with SANE_CONSTRAINT_STRING_LIST, * SANE_CONSTRAINT_WORD_LIST and for a SANE_CONSTRAINT_RANGE which is a * resolution setting. To retrieve the range for other constraint * types, use @c getRange(). **/ QList getList() const; /** * Retrieve the range of possible numeric values. * * @param minp A @c double to receive the minimum value * @param maxp A @c double to receive the maximum value * @param quantp A @c double to receive the step, if it is not @c NULL. * @return @c true if the option is a range type * * @note For an option with SANE_CONSTRAINT_WORD_LIST, the minimum * and maximum values are those found in the word list and the step * is the range divided by the number of possible values. This does * not imply that any intermediate values calculated from these are valid. **/ bool getRange(double *minp, double *maxp, double *quantp = NULL) const; /** * Send the data (previously set by @c set()) to the scanner, if this * is possible - if the option is initialised, software settable and * active. * * @return @c true if a reload of other parameters is required * (@c sane_control_option() returned SANE_INFO_RELOAD_OPTIONS). **/ bool apply(); /** * Retrieve the current data from the scanner, so that it can be * accessed by @c get(). If the option has an associated GUI control, * the enabled/disabled state of that is set appropriately. **/ void reload(); /** * Create a GUI widget for the scanner option, depending on its type. * * - Boolean options have a check box (a @c KScanCheckbox). * - Numeric ranges have a slider/spinbox combination (a @c KScanSlider), * except for the special case of a resolution option which generates * a combo box (@c KScanCombo) - this is done for user convenience. * - String and numeric lists generate a combo box (@c KScanCombo). * - Unconstrained string and numeric options generate an entry box * (@c KScanStringEntry or @c KScanNumberEntry respectively). * - An option which is a file name (present in the SANE "pnm" test device) * generates a file requester (@c KScanFileRequester). * - Group options generate a separator line (@c KScanGroup), although * obviously no interaction is allowed. * - Button options generate a clickable button (@c KScanPushButton). * * Ownership of the widget is retained by the @c KScanOption object, so it * should not be deleted by the caller. * * @param parent Parent for the created widget. * @return The created widget, or @c NULL if it could not be created. **/ KScanControl *createWidget(QWidget *parent); /** * Create a label widget for the scanner option. @c createWidget() must * have been used to create the control first. * * If the option is a common option (i.e. not advanced), then the label's * buddy will be set to the control. * @param parent Parent widget for the created label. * @param alwaysBuddy If this is @c true, the label's buddy will always * be set even if this is an advanced option. * @return The created label widget. **/ QLabel *getLabel(QWidget *parent, bool alwaysBuddy = false) const; /** * Create a label widget for the SANE unit of this option. * @c createWidget() must have been used to create the control first. * * @param parent Parent widget for the created label. * @return The created label widget, or @c NULL if no unit is * applicable. **/ QLabel *getUnit(QWidget *parent) const; /** * Get the name of the option. * * @return The name of the option **/ QByteArray getName() const { return (mName); } /** * Get the SANE capabilities for the option. * * @return The capabilities, or 0 if the option is not valid **/ int getCapabilities() const { return (mDesc!=NULL ? mDesc->cap : 0); } /** * Get the GUI widget for the option, if applicable and one has been * created by @c createWidget()). * * @return The widget, or @c NULL if there is none. **/ KScanControl *widget() const { return (mControl); } /** * Update the GUI widget to reflect the current value of the option. **/ void redrawWidget(); protected slots: /** * Called when the contents of the GUI widget has changed, if the * option has a GUI element (not all have). This slot is used for * options that have a @c KScanControl of type @c KScanControl::Text. * * @param t New string contents of the widget **/ void slotWidgetChange(const QString &t); /** * Called when the contents of the GUI widget has changed, if the * option has a GUI element (not all have). This slot is used for * options that have a @c KScanControl of type @c KScanControl::Number. * * @param i New index or setting of the widget **/ void slotWidgetChange(int i); /** * Called when a GUI widget is activated, if the option has a GUI * element (not all have). This slot is used for options that have * a @c KScanControl of type @c KScanControl::Button. **/ void slotWidgetChange(); signals: /** * Emitted when the user changes the GUI setting of the option. * The new setting will have been @c set(), but not yet @c apply()'ed. * * @param so The @c KScanOption which has changed **/ void guiChange(KScanOption *so); private: /** * Create a new object for the named option belonging to the * specified scanner device. After construction, if the option * is valid it will initally contain the current parameter value * retrieved from the scanner. * * @param name Name of the scanner option * @param scandev Scanner device * * @note This constructor is private. All @c KScanOption's are * created by @c KScanDevice; a new or existing @c KScanOption can * be obtained via @c KScanDevice::getOption(). **/ KScanOption(const QByteArray &name, KScanDevice *scandev); friend KScanOption *KScanDevice::getOption(const QByteArray &, bool); /** * Destructor. * * @note This destructor is private. All @c KScanOption's are * owned by @c KScanDevice, and are deleted when the scan device * is closed. **/ ~KScanOption(); friend void KScanDevice::closeDevice(); /** * The type of an associated GUI widget (if there is one). **/ enum WidgetType { Invalid, Bool, SingleValue, Range, GammaTable, StringList, String, Resolution, File, Group, Button }; bool initOption(const QByteArray &name); KScanOption::WidgetType resolveWidgetType() const; void allocForDesc(); void allocBuffer(long size); void updateList(); KScanControl *createToggleButton(QWidget *parent, const QString &text); KScanControl *createStringEntry(QWidget *parent, const QString &text); KScanControl *createNumberEntry(QWidget *parent, const QString &text); KScanControl *createSlider(QWidget *parent, const QString &text); KScanControl *createComboBox(QWidget *parent, const QString &text); KScanControl *createFileField(QWidget *parent, const QString &text); KScanControl *createGroupSeparator(QWidget *parent, const QString &text); KScanControl *createActionButton(QWidget *parent, const QString &text); KScanDevice *mScanDevice; int mIndex; const SANE_Option_Descriptor *mDesc; QByteArray mName; QString mText; bool mIsGroup; bool mIsReadable; bool mIsPriority; KScanControl *mControl; KScanOption::WidgetType mWidgetType; QByteArray mBuffer; bool mBufferClean; bool mApplied; KGammaTable *mGammaTable; }; #endif // KSCANOPTION_H