diff --git a/hotkeys_and_scripts/skanlite.khotkeys b/hotkeys_and_scripts/skanlite.khotkeys
index 8eb819f..7d2e9c9 100644
--- a/hotkeys_and_scripts/skanlite.khotkeys
+++ b/hotkeys_and_scripts/skanlite.khotkeys
@@ -1,193 +1,222 @@
[Data]
DataCount=1
[Data_1]
Comment=Shortcuts for Skanlite
-DataCount=6
+DataCount=7
Enabled=true
Name=Skanlite
SystemGroup=0
Type=ACTION_DATA_GROUP
[Data_1Conditions]
Comment=
ConditionsCount=0
[Data_1_1]
Comment=Global hotkey: Alt+S to start scanning. Skanlite must be up and running.
Enabled=true
Name=Scan
Type=SIMPLE_ACTION_DATA
[Data_1_1Actions]
ActionsCount=1
[Data_1_1Actions0]
Arguments=
Call=scan
RemoteApp=org.kde.skanlite
RemoteObj=/
Type=DBUS
[Data_1_1Conditions]
Comment=
ConditionsCount=0
[Data_1_1Triggers]
Comment=Simple_action
TriggersCount=1
[Data_1_1Triggers0]
Key=Alt+S
Type=SHORTCUT
Uuid={3c04e4ea-1f6e-430e-82bb-0e4adb79a6a3}
[Data_1_2]
Comment=Global hotkey: Alt+C to cancel scanning while it's performing. Do nothing otherwise.
Enabled=true
Name=Cancel Scan
Type=SIMPLE_ACTION_DATA
[Data_1_2Actions]
ActionsCount=1
[Data_1_2Actions0]
Arguments=
Call=scanCancel
RemoteApp=org.kde.skanlite
RemoteObj=/
Type=DBUS
[Data_1_2Conditions]
Comment=
ConditionsCount=0
[Data_1_2Triggers]
Comment=Simple_action
TriggersCount=1
[Data_1_2Triggers0]
Key=Alt+C
Type=SHORTCUT
Uuid={aaed7bc1-a7bc-4abc-b319-9b95abb5216b}
[Data_1_3]
Comment=Global hotkey: press Ctrl+Alt+1 to save current Skanlite scanner settings as Profile 1. You can switch to this profile later with Alt+1.
Enabled=true
Name=Save Profile 1
Type=SIMPLE_ACTION_DATA
[Data_1_3Actions]
ActionsCount=1
[Data_1_3Actions0]
Arguments=1
Call=saveCurrentScannerOptionsToProfile
RemoteApp=org.kde.skanlite
RemoteObj=/
Type=DBUS
[Data_1_3Conditions]
Comment=
ConditionsCount=0
[Data_1_3Triggers]
Comment=Simple_action
TriggersCount=1
[Data_1_3Triggers0]
Key=Ctrl+Alt+1
Type=SHORTCUT
Uuid={5d4c37a6-d8b3-40b0-aa6a-92f325302538}
[Data_1_4]
Comment=Global hotkey: press Alt+1 to restore scanner settings from Profile 1 in Skanlite. If profile doesn't exists - restore default settings.
Enabled=true
Name=Load Profile 1
Type=SIMPLE_ACTION_DATA
[Data_1_4Actions]
ActionsCount=1
[Data_1_4Actions0]
Arguments=1
Call=switchToProfile
RemoteApp=org.kde.skanlite
RemoteObj=/
Type=DBUS
[Data_1_4Conditions]
Comment=
ConditionsCount=0
[Data_1_4Triggers]
Comment=Simple_action
TriggersCount=1
[Data_1_4Triggers0]
Key=Alt+1
Type=SHORTCUT
Uuid={81787487-f07d-4111-b1cf-9da1406075ca}
[Data_1_5]
Comment=Global hotkey: press Ctrl+Alt+2 to save current Skanlite scanner settings as Profile 2. You can switch to this profile later with Alt+2.
Enabled=true
Name=Save Profile 2
Type=SIMPLE_ACTION_DATA
[Data_1_5Actions]
ActionsCount=1
[Data_1_5Actions0]
Arguments=2
Call=saveCurrentScannerOptionsToProfile
RemoteApp=org.kde.skanlite
RemoteObj=/
Type=DBUS
[Data_1_5Conditions]
Comment=
ConditionsCount=0
[Data_1_5Triggers]
Comment=Simple_action
TriggersCount=1
[Data_1_5Triggers0]
Key=Ctrl+Alt+2
Type=SHORTCUT
Uuid={e74b442f-47db-4314-a0e0-a56e1b753804}
[Data_1_6]
Comment=Global hotkey: press Alt+2 to restore scanner settings from Profile 2 in Skanlite. If profile doesn't exists - restore default settings.
Enabled=true
Name=Load Profile 2
Type=SIMPLE_ACTION_DATA
[Data_1_6Actions]
ActionsCount=1
[Data_1_6Actions0]
Arguments=2
Call=switchToProfile
RemoteApp=org.kde.skanlite
RemoteObj=/
Type=DBUS
[Data_1_6Conditions]
Comment=
ConditionsCount=0
[Data_1_6Triggers]
Comment=Simple_action
TriggersCount=1
[Data_1_6Triggers0]
Key=Alt+2
Type=SHORTCUT
Uuid={9f894206-fbcf-48f1-9fb3-df9cec37bacb}
+[Data_1_7]
+Comment=Global hotkey: Alt+P to start scanning preview. Skanlite must be up and running.
+Enabled=true
+Name=Preview
+Type=SIMPLE_ACTION_DATA
+
+[Data_1_7Actions]
+ActionsCount=1
+
+[Data_1_7Actions0]
+Arguments=
+Call=preview
+RemoteApp=org.kde.skanlite
+RemoteObj=/
+Type=DBUS
+
+[Data_1_7Conditions]
+Comment=
+ConditionsCount=0
+
+[Data_1_7Triggers]
+Comment=Simple_action
+TriggersCount=1
+
+[Data_1_7Triggers0]
+Key=Alt+P
+Type=SHORTCUT
+Uuid={96c0b3ae-9d0d-45dd-b275-c11c01424ff7}
+
[Main]
AllowMerge=true
ImportId=skanlite
Version=2
diff --git a/src/DBusInterface.h b/src/DBusInterface.h
index 93b9b5b..d36a9f7 100644
--- a/src/DBusInterface.h
+++ b/src/DBusInterface.h
@@ -1,152 +1,156 @@
/* ============================================================
*
* Copyright (C) 2017 by Alexander Trufanov
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* 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. If not, see
*
* ============================================================ */
#ifndef DBusInterface_h
#define DBusInterface_h
#include
#include
#include
#include
static const bool defaultSelectionFiltering = true;
static const QLatin1String defaultProfile("1");
class DBusInterface : public QObject
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.skanlite")
QStringList m_msgBuffer;
public:
explicit DBusInterface(QObject* parent = NULL) : QObject(parent) {}
bool setupDBusInterface();
const QStringList& reply() { return m_msgBuffer; }
void setReply(const QStringList &reply) { m_msgBuffer = reply; }
private:
// helper method for QDbusViewer compatibility. The details are in .cpp
const QStringList ensureStringList(const QStringList &list);
Q_SIGNALS:
// used to communicate with Skanlite class
void requestedScan();
+ void requestedPreview();
void requestedScanCancel();
void requestedGetScannerOptions();
void requestedSetScannerOptions(const QStringList &options, bool ignoreSelection);
void requestedDefaultScannerOptions();
void requestedDeviceName();
void requestedSaveScannerOptionsToProfile(const QStringList &options, const QString &profile, bool ignoreSelection);
void requestedSwitchToProfile(const QString &profile, bool ignoreSelection);
void requestedGetSelection();
void requestedSetSelection(const QStringList &options);
public Q_SLOTS:
// called via D-Bus
// Perform scan operation if is in idle
Q_SCRIPTABLE void scan() { emit requestedScan(); }
+ // Perform preview operation if is in idle
+ Q_SCRIPTABLE void preview() { emit requestedPreview(); }
+
// Cancel any ongoing operation
Q_SCRIPTABLE void scanCancel() { emit requestedScanCancel(); }
// Return device name, like "Hewlett-Packard:Scanjet 4370"
Q_SCRIPTABLE QString getDeviceName()
{
emit requestedDeviceName();
return reply().join(QLatin1String());
}
// Return current scanner options as returned by KSaneWidget::getOptVals()
Q_SCRIPTABLE QStringList getScannerOptions()
{
emit requestedGetScannerOptions();
return reply();
}
// Replaces current scanner options with argument value. Ignores page selection area info if ignoreSelection is true (by default)
Q_SCRIPTABLE void setScannerOptions(const QStringList &options, bool ignoreSelection = defaultSelectionFiltering)
{
emit requestedSetScannerOptions(ensureStringList(options), ignoreSelection);
}
// Return current scanner options
Q_SCRIPTABLE QStringList getDefaultScannerOptions()
{
emit requestedDefaultScannerOptions();
return reply();
}
// Save options to KConfigGroup named ""Options For %Current_Device% - Profile %profile%""
// Thus it's device specific. Default profile is "1". Could be any string value.
// Ignores page selection area info if ignoreSelection is true (by default)
Q_SCRIPTABLE void saveScannerOptionsToProfile(const QStringList &options, const QString &profile = defaultProfile, bool ignoreSelection = defaultSelectionFiltering)
{
emit requestedSaveScannerOptionsToProfile(ensureStringList(options), profile, ignoreSelection);
}
// Gets current options via KSaneWidget::getOptVals() and saves them into provile
// Acts as a combination of saveScannerOptionsToProfile and requestedSaveScannerOptionsToProfile
// Made for easy bind both to hotkey in KHotkeys
Q_SCRIPTABLE void saveCurrentScannerOptionsToProfile(const QString &profile = defaultProfile, bool ignoreSelection = defaultSelectionFiltering)
{
emit requestedGetScannerOptions(); // put result in m_msgBuffer
emit requestedSaveScannerOptionsToProfile(reply(), profile, ignoreSelection);
}
// Loads options from specified profile and applies them. Ignores page selection area info if ignoreSelection is true (by default)
Q_SCRIPTABLE void switchToProfile(const QString &profile = defaultProfile, bool ignoreSelection = defaultSelectionFiltering)
{
emit requestedSwitchToProfile(profile, ignoreSelection);
}
// Returns current selection area in form "{"tl-x=0", "tl-y=0", "br-x=220", "br-y=248"}"
Q_SCRIPTABLE QStringList getSelection()
{
emit requestedGetSelection();
return reply();
}
// Changes current selection area. Argument must be a list of strings in form "{"tl-x=0", "tl-y=0", "br-x=220", "br-y=248"}"
Q_SCRIPTABLE void setSelection(const QStringList &options)
{
emit requestedSetSelection(ensureStringList(options));
}
Q_SIGNALS:
Q_SCRIPTABLE void imageSaved(const QString &strFilename);
// Below are 4 signals which are just forwarded from KSaneWidget.
// You can take a look in KSaneWidget.h for detailed arguments description
Q_SCRIPTABLE void scanDone(int status, const QString &strStatus);
Q_SCRIPTABLE void userMessage(int type, const QString &strStatus);
Q_SCRIPTABLE void scanProgress(int percent);
Q_SCRIPTABLE void buttonPressed(const QString &optionName, const QString &optionLabel, bool pressed);
};
#endif
diff --git a/src/skanlite.cpp b/src/skanlite.cpp
index ded8a6e..215869e 100644
--- a/src/skanlite.cpp
+++ b/src/skanlite.cpp
@@ -1,788 +1,789 @@
/* ============================================================
*
* Copyright (C) 2007-2012 by Kåre Särs
* Copyright (C) 2009 by Arseniy Lartsev
* Copyright (C) 2014 by Gregor Mitsch: port to KDE5 frameworks
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* 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. If not, see
*
* ============================================================ */
#include "skanlite.h"
#include "KSaneImageSaver.h"
#include "SaveLocation.h"
#include "showimagedialog.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
Skanlite::Skanlite(const QString &device, QWidget *parent)
: QDialog(parent)
, m_aboutData(nullptr)
, m_dbusInterface(this)
{
QVBoxLayout *mainLayout = new QVBoxLayout(this);
QDialogButtonBox *dlgButtonBoxBottom = new QDialogButtonBox(this);
dlgButtonBoxBottom->setStandardButtons(QDialogButtonBox::Help | QDialogButtonBox::Close);
// was "User2:
QPushButton *btnAbout = dlgButtonBoxBottom->addButton(i18n("About"), QDialogButtonBox::ButtonRole::ActionRole);
// was "User1":
QPushButton *btnSettings = dlgButtonBoxBottom->addButton(i18n("Settings"), QDialogButtonBox::ButtonRole::ActionRole);
btnSettings->setIcon(QIcon::fromTheme(QLatin1String("configure")));
m_firstImage = true;
m_ksanew = new KSaneIface::KSaneWidget(this);
connect(m_ksanew, &KSaneWidget::imageReady, this, &Skanlite::imageReady);
connect(m_ksanew, &KSaneWidget::availableDevices, this, &Skanlite::availableDevices);
connect(m_ksanew, &KSaneWidget::userMessage, this, &Skanlite::alertUser);
connect(m_ksanew, &KSaneWidget::buttonPressed, this, &Skanlite::buttonPressed);
mainLayout->addWidget(m_ksanew);
mainLayout->addWidget(dlgButtonBoxBottom);
m_ksanew->initGetDeviceList();
// read the size here...
KConfigGroup window(KSharedConfig::openConfig(), "Window");
QSize rect = window.readEntry("Geometry", QSize(740, 400));
resize(rect);
connect(dlgButtonBoxBottom, &QDialogButtonBox::rejected, this, &QDialog::close);
connect(this, &QDialog::finished, this, &Skanlite::saveWindowSize);
connect(this, &QDialog::finished, this, &Skanlite::saveScannerOptions);
connect(btnSettings, &QPushButton::clicked, this, &Skanlite::showSettingsDialog);
connect(btnAbout, &QPushButton::clicked, this, &Skanlite::showAboutDialog);
connect(dlgButtonBoxBottom, &QDialogButtonBox::helpRequested, this, &Skanlite::showHelp);
//
// Create the settings dialog
//
{
m_settingsDialog = new QDialog(this);
QVBoxLayout *mainLayout = new QVBoxLayout(m_settingsDialog);
QWidget *settingsWidget = new QWidget(m_settingsDialog);
m_settingsUi.setupUi(settingsWidget);
m_settingsUi.revertOptions->setIcon(QIcon::fromTheme(QLatin1String("edit-undo")));
m_saveLocation = new SaveLocation(this);
// add the supported image types
const QList tmpList = QImageWriter::supportedMimeTypes();
m_filterList.clear();
foreach (auto ba, tmpList) {
if (ba.isEmpty()) {
continue;
}
m_filterList.append(QString::fromLatin1(ba));
}
qDebug() << m_filterList;
// Put first class citizens at first place
m_filterList.removeAll(QLatin1String("image/jpeg"));
m_filterList.removeAll(QLatin1String("image/tiff"));
m_filterList.removeAll(QLatin1String("image/png"));
m_filterList.insert(0, QLatin1String("image/png"));
m_filterList.insert(1, QLatin1String("image/jpeg"));
m_filterList.insert(2, QLatin1String("image/tiff"));
m_filter16BitList << QLatin1String("image/png");
//m_filter16BitList << QLatin1String("image/tiff");
// fill m_filterList (...) and m_typeList (list of file suffixes)
foreach (QString mimeStr, m_filterList) {
QMimeType mimeType = QMimeDatabase().mimeTypeForName(mimeStr);
m_filterList.append(mimeType.name());
QStringList fileSuffixes = mimeType.suffixes();
if (fileSuffixes.size() > 0) {
m_typeList << fileSuffixes.first();
}
}
m_settingsUi.imgFormat->addItems(m_typeList);
m_saveLocation->u_imgFormat->addItems(m_typeList);
mainLayout->addWidget(settingsWidget);
QDialogButtonBox *dlgButtonBoxBottom = new QDialogButtonBox(this);
dlgButtonBoxBottom->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Close);
connect(dlgButtonBoxBottom, &QDialogButtonBox::accepted, m_settingsDialog, &QDialog::accept);
connect(dlgButtonBoxBottom, &QDialogButtonBox::rejected, m_settingsDialog, &QDialog::reject);
mainLayout->addWidget(dlgButtonBoxBottom);
m_settingsDialog->setWindowTitle(i18n("Skanlite Settings"));
connect(m_settingsUi.getDirButton, &QPushButton::clicked, this, &Skanlite::getDir);
connect(m_settingsUi.revertOptions, &QPushButton::clicked, this, &Skanlite::defaultScannerOptions);
readSettings();
// default directory for the save dialog
m_saveLocation->u_urlRequester->setUrl(QUrl::fromUserInput(m_settingsUi.saveDirLEdit->text()));
m_saveLocation->u_imgPrefix->setText(m_settingsUi.imgPrefix->text());
m_saveLocation->u_imgFormat->setCurrentText(m_settingsUi.imgFormat->currentText());
}
// open the scan device
if (m_ksanew->openDevice(device) == false) {
QString dev = m_ksanew->selectDevice(0);
if (dev.isEmpty()) {
// either no scanner was found or then cancel was pressed.
exit(0);
}
if (m_ksanew->openDevice(dev) == false) {
// could not open a scanner
KMessageBox::sorry(0, i18n("Opening the selected scanner failed."));
exit(1);
}
else {
setWindowTitle(i18nc("@title:window %1 = scanner maker, %2 = scanner model", "%1 %2 - Skanlite", m_ksanew->make(), m_ksanew->model()));
m_deviceName = QString::fromLatin1("%1:%2").arg(m_ksanew->make()).arg(m_ksanew->model());
}
}
else {
setWindowTitle(i18nc("@title:window %1 = scanner device", "%1 - Skanlite", device));
m_deviceName = device;
}
// prepare the Show Image Dialog
m_showImgDialog = new ShowImageDialog(this);
connect(m_showImgDialog, &ShowImageDialog::saveRequested, this, &Skanlite::saveImage);
connect(m_showImgDialog, &ShowImageDialog::rejected, m_ksanew, &KSaneWidget::scanCancel);
// save the default sane options for later use
m_ksanew->getOptVals(m_defaultScanOpts);
// load saved options
loadScannerOptions();
m_ksanew->initGetDeviceList();
m_firstImage = true;
if (m_dbusInterface.setupDBusInterface()) {
// D-Bus related slots
connect(&m_dbusInterface, &DBusInterface::requestedScan, m_ksanew, &KSaneWidget::scanFinal);
+ connect(&m_dbusInterface, &DBusInterface::requestedPreview, m_ksanew, &KSaneWidget::startPreviewScan);
connect(&m_dbusInterface, &DBusInterface::requestedScanCancel, m_ksanew, &KSaneWidget::scanCancel);
connect(&m_dbusInterface, &DBusInterface::requestedSetScannerOptions, this, &Skanlite::setScannerOptions);
connect(&m_dbusInterface, &DBusInterface::requestedSetSelection, this, &Skanlite::setSelection);
// D-Bus related slots below must be Qt::DirectConnection to simplify return value forwarding via DBusInterface
connect(&m_dbusInterface, &DBusInterface::requestedGetScannerOptions, this, &Skanlite::getScannerOptions, Qt::DirectConnection);
connect(&m_dbusInterface, &DBusInterface::requestedDefaultScannerOptions, this, &Skanlite::getDefaultScannerOptions, Qt::DirectConnection);
connect(&m_dbusInterface, &DBusInterface::requestedDeviceName, this, &Skanlite::getDeviceName, Qt::DirectConnection);
connect(&m_dbusInterface, &DBusInterface::requestedSaveScannerOptionsToProfile, this, &Skanlite::saveScannerOptionsToProfile, Qt::DirectConnection);
connect(&m_dbusInterface, &DBusInterface::requestedSwitchToProfile, this, &Skanlite::switchToProfile, Qt::DirectConnection);
connect(&m_dbusInterface, &DBusInterface::requestedGetSelection, this, &Skanlite::getSelection, Qt::DirectConnection);
// D-Bus related signals
connect(m_ksanew, &KSaneWidget::scanDone, &m_dbusInterface, &DBusInterface::scanDone);
connect(m_ksanew, &KSaneWidget::userMessage, &m_dbusInterface, &DBusInterface::userMessage);
connect(m_ksanew, &KSaneWidget::scanProgress, &m_dbusInterface, &DBusInterface::scanProgress);
connect(m_ksanew, &KSaneWidget::buttonPressed, &m_dbusInterface, &DBusInterface::buttonPressed);
}
else {
// keep working without dbus
}
}
void Skanlite::showHelp()
{
KHelpClient::invokeHelp(QLatin1String("index"), QLatin1String("skanlite"));
}
void Skanlite::setAboutData(KAboutData *aboutData)
{
m_aboutData = aboutData;
}
void Skanlite::closeEvent(QCloseEvent *event)
{
saveWindowSize();
saveScannerOptions();
event->accept();
}
void Skanlite::saveWindowSize()
{
KConfigGroup window(KSharedConfig::openConfig(), "Window");
window.writeEntry("Geometry", size());
window.sync();
}
// Pops up message box similar to what perror() would print
//************************************************************
static void perrorMessageBox(const QString &text)
{
if (errno != 0) {
KMessageBox::sorry(0, i18n("%1: %2", text, QString::fromLocal8Bit(strerror(errno))));
}
else {
KMessageBox::sorry(0, text);
}
}
void Skanlite::readSettings(void)
{
// enable the widgets to allow modifying
m_settingsUi.setQuality->setChecked(true);
m_settingsUi.setPreviewDPI->setChecked(true);
// read the saved parameters
KConfigGroup saving(KSharedConfig::openConfig(), "Image Saving");
m_settingsUi.saveModeCB->setCurrentIndex(saving.readEntry("SaveMode", (int)SaveModeManual));
if (m_settingsUi.saveModeCB->currentIndex() != SaveModeAskFirst) {
m_firstImage = false;
}
m_settingsUi.saveDirLEdit->setText(saving.readEntry("Location", QDir::homePath()));
m_settingsUi.imgPrefix->setText(saving.readEntry("NamePrefix", i18nc("prefix for auto naming", "Image-")));
m_settingsUi.imgFormat->setCurrentText(saving.readEntry("ImgFormat", "png"));
m_settingsUi.imgQuality->setValue(saving.readEntry("ImgQuality", 90));
m_settingsUi.setQuality->setChecked(saving.readEntry("SetQuality", false));
m_settingsUi.showB4Save->setChecked(saving.readEntry("ShowBeforeSave", true));
KConfigGroup general(KSharedConfig::openConfig(), "General");
//m_settingsUi.previewDPI->setCurrentItem(general.readEntry("PreviewDPI", "100"), true); // FIXME KF5 is the 'true' parameter still needed?
m_settingsUi.previewDPI->setCurrentText(general.readEntry("PreviewDPI", "100"));
m_settingsUi.setPreviewDPI->setChecked(general.readEntry("SetPreviewDPI", false));
if (m_settingsUi.setPreviewDPI->isChecked()) {
m_ksanew->setPreviewResolution(m_settingsUi.previewDPI->currentText().toFloat());
}
else {
m_ksanew->setPreviewResolution(0.0);
}
m_settingsUi.u_disableSelections->setChecked(general.readEntry("DisableAutoSelection", false));
m_ksanew->enableAutoSelect(!m_settingsUi.u_disableSelections->isChecked());
}
void Skanlite::showSettingsDialog(void)
{
readSettings();
// show the dialog
if (m_settingsDialog->exec()) {
// save the settings
KConfigGroup saving(KSharedConfig::openConfig(), "Image Saving");
saving.writeEntry("SaveMode", m_settingsUi.saveModeCB->currentIndex());
saving.writeEntry("Location", m_settingsUi.saveDirLEdit->text());
saving.writeEntry("NamePrefix", m_settingsUi.imgPrefix->text());
saving.writeEntry("ImgFormat", m_settingsUi.imgFormat->currentText());
saving.writeEntry("SetQuality", m_settingsUi.setQuality->isChecked());
saving.writeEntry("ImgQuality", m_settingsUi.imgQuality->value());
saving.writeEntry("ShowBeforeSave", m_settingsUi.showB4Save->isChecked());
saving.sync();
KConfigGroup general(KSharedConfig::openConfig(), "General");
general.writeEntry("PreviewDPI", m_settingsUi.previewDPI->currentText());
general.writeEntry("SetPreviewDPI", m_settingsUi.setPreviewDPI->isChecked());
general.writeEntry("DisableAutoSelection", m_settingsUi.u_disableSelections->isChecked());
general.sync();
// the previewDPI has to be set here
if (m_settingsUi.setPreviewDPI->isChecked()) {
m_ksanew->setPreviewResolution(m_settingsUi.previewDPI->currentText().toFloat());
}
else {
// 0.0 means default value.
m_ksanew->setPreviewResolution(0.0);
}
m_ksanew->enableAutoSelect(!m_settingsUi.u_disableSelections->isChecked());
// pressing OK in the settings dialog means use those settings.
m_saveLocation->u_urlRequester->setUrl(QUrl::fromUserInput(m_settingsUi.saveDirLEdit->text()));
m_saveLocation->u_imgPrefix->setText(m_settingsUi.imgPrefix->text());
m_saveLocation->u_imgFormat->setCurrentText(m_settingsUi.imgFormat->currentText());
m_firstImage = true;
}
else {
//Forget Changes
readSettings();
}
}
void Skanlite::imageReady(QByteArray &data, int w, int h, int bpl, int f)
{
// save the image data
m_data = data;
m_width = w;
m_height = h;
m_bytesPerLine = bpl;
m_format = f;
if (m_settingsUi.showB4Save->isChecked() == true) {
/* copy the image data into m_img and show it*/
m_img = m_ksanew->toQImageSilent(data, w, h, bpl, (KSaneIface::KSaneWidget::ImageFormat)f);
m_showImgDialog->setQImage(&m_img);
m_showImgDialog->zoom2Fit();
m_showImgDialog->exec();
// save has been done as a result of save or then we got cancel
}
else {
m_img = QImage(); // clear the image to ensure we save the correct one.
saveImage();
}
}
void Skanlite::saveImage()
{
// ask the first time if we are in "ask on first" mode
if ((m_settingsUi.saveModeCB->currentIndex() == SaveModeAskFirst) && m_firstImage) {
if (m_saveLocation->exec() != QFileDialog::Accepted) {
m_ksanew->scanCancel(); // In case we are cancelling a document feeder scan
return;
}
m_firstImage = false;
}
QString dir = QDir::cleanPath(m_saveLocation->u_urlRequester->url().url()).append(QLatin1Char('/')); //make sure whole value is processed as path to directory
QString prefix = m_saveLocation->u_imgPrefix->text();
QString imgFormat = m_saveLocation->u_imgFormat->currentText().toLower();
int fileNumber = m_saveLocation->u_numStartFrom->value();
QStringList filterList = m_filterList;
if ((m_format == KSaneIface::KSaneWidget::FormatRGB_16_C) ||
(m_format == KSaneIface::KSaneWidget::FormatGrayScale16)) {
filterList = m_filter16BitList;
if (imgFormat != QLatin1String("png")) {
imgFormat = QLatin1String("png");
KMessageBox::information(this, i18n("The image will be saved in the PNG format, as Skanlite only supports saving 16 bit color images in the PNG format."));
}
}
//qDebug() << dir << prefix << imgFormat;
// find next available file name for name suggestion
QUrl fileUrl;
QString fname;
for (int i = fileNumber; i <= m_saveLocation->u_numStartFrom->maximum(); ++i) {
fname = QString::fromLatin1("%1%2.%3")
.arg(prefix)
.arg(i, 4, 10, QLatin1Char('0'))
.arg(imgFormat);
fileUrl = QUrl::fromUserInput(QStringLiteral("%1%2").arg(dir, fname));
//qDebug() << fileUrl;
if (fileUrl.isLocalFile()) {
if (!QFileInfo(fileUrl.toLocalFile()).exists()) {
break;
}
}
else {
KIO::StatJob *statJob = KIO::stat(fileUrl, KIO::StatJob::DestinationSide, 0);
KJobWidgets::setWindow(statJob, QApplication::activeWindow());
if (!statJob->exec()) {
break;
}
}
}
if (m_settingsUi.saveModeCB->currentIndex() == SaveModeManual) {
// prepare the save dialog
QFileDialog saveDialog(this, i18n("New Image File Name"));
saveDialog.setAcceptMode(QFileDialog::AcceptSave);
saveDialog.setFileMode(QFileDialog::AnyFile);
// ask for a filename if requested.
saveDialog.setDirectoryUrl(fileUrl.adjusted(QUrl::RemoveFilename));
saveDialog.selectUrl(fileUrl);
// NOTE it is probably a bug that both setDirectoryUrl and selectUrl have
// to be set to get remote urls to work
QStringList actualFilterList = filterList;
QString currentMimeFilter = QLatin1String("image/") + imgFormat;
saveDialog.setMimeTypeFilters(actualFilterList);
saveDialog.selectMimeTypeFilter(currentMimeFilter);
//qDebug() << fileUrl.url() << fileUrl.toLocalFile() << currentMimeFilter;
do {
if (saveDialog.exec() != QFileDialog::Accepted) {
return;
}
fileUrl = saveDialog.selectedUrls()[0];
bool exists;
if (fileUrl.isLocalFile()) {
exists = QFileInfo(fileUrl.toLocalFile()).exists();
}
else {
KIO::StatJob *statJob = KIO::stat(fileUrl, KIO::StatJob::DestinationSide, 0);
KJobWidgets::setWindow(statJob, QApplication::activeWindow());
exists = statJob->exec();
}
if (exists) {
if (KMessageBox::warningContinueCancel(this,
i18n("Do you want to overwrite \"%1\"?", fileUrl.fileName()),
QString(),
KStandardGuiItem::overwrite(),
KStandardGuiItem::cancel(),
QLatin1String("editorWindowSaveOverwrite")
) == KMessageBox::Continue) {
break;
}
}
else {
break;
}
} while (true);
}
m_firstImage = false;
// Get the quality
int quality = -1;
if (m_settingsUi.setQuality->isChecked()) {
quality = m_settingsUi.imgQuality->value();
}
//qDebug() << "suffix" << QFileInfo(fileUrl.fileName()).suffix();
QString localName;
QString suffix = QFileInfo(fileUrl.fileName()).suffix();
const char *fileFormat = nullptr;
if (suffix.isEmpty()) {
fileFormat = "png";
}
if (!fileUrl.isLocalFile()) {
QTemporaryFile tmp;
tmp.open();
if (suffix.isEmpty()) {
localName = tmp.fileName();
}
else {
localName = QStringLiteral("%1.%2").arg(tmp.fileName(), suffix);
}
tmp.close(); // we just want the filename
}
else {
localName = fileUrl.toLocalFile();
}
// Save
if ((m_format == KSaneIface::KSaneWidget::FormatRGB_16_C) ||
(m_format == KSaneIface::KSaneWidget::FormatGrayScale16)) {
KSaneImageSaver saver;
if (saver.savePngSync(localName, m_data, m_width, m_height, m_format, m_ksanew->currentDPI())) {
m_showImgDialog->close(); // closing the window if it is closed should not be a problem.
}
else {
perrorMessageBox(i18n("Failed to save image"));
return;
}
}
else {
// create the image if needed.
if (m_img.width() < 1) {
m_img = m_ksanew->toQImage(m_data, m_width, m_height, m_bytesPerLine, (KSaneIface::KSaneWidget::ImageFormat)m_format);
}
if (m_img.save(localName, fileFormat, quality)) {
m_showImgDialog->close(); // calling close() on a closed window does nothing.
}
else {
perrorMessageBox(i18n("Failed to save image"));
return;
}
}
if (!fileUrl.isLocalFile()) {
QFile tmpFile(localName);
tmpFile.open(QIODevice::ReadOnly);
auto uploadJob = KIO::storedPut(&tmpFile, fileUrl, -1);
KJobWidgets::setWindow(uploadJob, QApplication::activeWindow());
bool ok = uploadJob->exec();
tmpFile.close();
tmpFile.remove();
if (!ok) {
KMessageBox::sorry(0, i18n("Failed to upload image"));
}
else {
emit m_dbusInterface.imageSaved(fileUrl.toString());
}
}
else {
emit m_dbusInterface.imageSaved(localName);
}
// Save the file base name without number
QString baseName = QFileInfo(fileUrl.fileName()).completeBaseName();
while ((!baseName.isEmpty()) && (baseName[baseName.size() - 1].isNumber())) {
baseName.remove(baseName.size() - 1, 1);
}
m_saveLocation->u_imgPrefix->setText(baseName);
// Save the number
QString fileNumStr = QFileInfo(fileUrl.fileName()).completeBaseName();
fileNumStr.remove(baseName);
fileNumber = fileNumStr.toInt();
if (fileNumber) {
m_saveLocation->u_numStartFrom->setValue(fileNumber + 1);
}
if (m_settingsUi.saveModeCB->currentIndex() == SaveModeManual) {
// Save last used dir, prefix and suffix.
m_saveLocation->u_urlRequester->setUrl(KIO::upUrl(fileUrl));
m_saveLocation->u_imgFormat->setCurrentText(QFileInfo(fileUrl.fileName()).suffix());
}
}
void Skanlite::getDir(void)
{
QString dir = QFileDialog::getExistingDirectory(m_settingsDialog, QString(), m_settingsUi.saveDirLEdit->text());
if (!dir.isEmpty()) {
m_settingsUi.saveDirLEdit->setText(dir);
}
}
void Skanlite::showAboutDialog(void)
{
KAboutApplicationDialog(*m_aboutData).exec();
}
void writeScannerOptions(const QString &groupName, const QMap &opts)
{
KConfigGroup options(KSharedConfig::openConfig(), groupName);
QMap::const_iterator it = opts.constBegin();
while (it != opts.constEnd()) {
options.writeEntry(it.key(), it.value());
++it;
}
options.sync();
}
void readScannerOptions(const QString &groupName, QMap &opts)
{
KConfigGroup scannerOptions(KSharedConfig::openConfig(), groupName);
opts = scannerOptions.entryMap();
}
void Skanlite::saveScannerOptions()
{
KConfigGroup saving(KSharedConfig::openConfig(), "Image Saving");
saving.writeEntry("NumberStartsFrom", m_saveLocation->u_numStartFrom->value());
if (!m_ksanew) {
return;
}
KConfigGroup options(KSharedConfig::openConfig(), QString::fromLatin1("Options For %1").arg(m_deviceName));
QMap opts;
m_ksanew->getOptVals(opts);
writeScannerOptions(QString::fromLatin1("Options For %1").arg(m_deviceName), opts);
}
void Skanlite::defaultScannerOptions()
{
if (!m_ksanew) {
return;
}
m_ksanew->setOptVals(m_defaultScanOpts);
}
void Skanlite::loadScannerOptions()
{
KConfigGroup saving(KSharedConfig::openConfig(), "Image Saving");
m_saveLocation->u_numStartFrom->setValue(saving.readEntry("NumberStartsFrom", 1));
if (!m_ksanew) {
return;
}
QMap opts;
readScannerOptions(QString::fromLatin1("Options For %1").arg(m_deviceName), opts);
m_ksanew->setOptVals(opts);
}
void Skanlite::availableDevices(const QList &deviceList)
{
for (int i = 0; i < deviceList.size(); ++i) {
qDebug() << deviceList.at(i).name;
}
}
void Skanlite::alertUser(int type, const QString &strStatus)
{
switch (type) {
case KSaneWidget::ErrorGeneral:
KMessageBox::sorry(0, strStatus, QLatin1String("Skanlite Test"));
break;
default:
KMessageBox::information(0, strStatus, QLatin1String("Skanlite Test"));
}
}
void Skanlite::buttonPressed(const QString &optionName, const QString &optionLabel, bool pressed)
{
qDebug() << "Button" << optionName << optionLabel << ((pressed) ? "pressed" : "released");
}
// D-Bus interface related helper functions
QStringList serializeScannerOptions(const QMap &opts)
{
QStringList sl;
QMap::const_iterator it = opts.constBegin();
while (it != opts.constEnd()) {
sl.append(it.key() + QLatin1String("=") + it.value());
++it;
}
return sl;
}
void deserializeScannerOptions(const QStringList &settings, QMap &opts)
{
foreach (QString s, settings) {
int i = s.lastIndexOf(QLatin1Char('='));
opts[s.left(i)] = s.right(s.length()-i-1);
}
}
static const QStringList selectionSettings = { QLatin1String("tl-x"), QLatin1String("tl-y"),
QLatin1String("br-x"), QLatin1String("br-y") };
void filterSelectionSettings(QMap &opts)
{
foreach (QString s, selectionSettings) {
opts.remove(s);
}
}
bool containsSelectionSettings(const QMap &opts)
{
foreach (QString s, selectionSettings) {
if (opts.contains(s)) {
return true;
}
}
return false;
}
void Skanlite::processSelectionOptions(QMap &opts, bool ignoreSelection)
{
if (ignoreSelection) {
filterSelectionSettings(opts);
}
else {
if (containsSelectionSettings(opts)) { // make sure we really have selection to apply
m_ksanew->setSelection(QPointF(0,0), QPointF(1,1)); // bcs settings have no effect if nothing was selected beforehand (Bug 377009)
}
}
}
// D-Bus interface related slots
void Skanlite::getScannerOptions()
{
QMap opts;
m_ksanew->getOptVals(opts);
m_dbusInterface.setReply(serializeScannerOptions(opts));
}
void Skanlite::setScannerOptions(const QStringList &options, bool ignoreSelection)
{
QMap opts;
deserializeScannerOptions(options, opts);
processSelectionOptions(opts, ignoreSelection);
m_ksanew->setOptVals(opts);
}
void Skanlite::getDefaultScannerOptions()
{
m_dbusInterface.setReply(serializeScannerOptions(m_defaultScanOpts));
}
static const QLatin1String defaultProfileGroup("Options For %1 - Profile %2"); // 1 - device, 2 - arg
void Skanlite::saveScannerOptionsToProfile(const QStringList &options, const QString &profile, bool ignoreSelection)
{
QMap opts;
deserializeScannerOptions(options, opts);
processSelectionOptions(opts, ignoreSelection);
writeScannerOptions(QString(defaultProfileGroup).arg(m_deviceName).arg(profile), opts);
}
void Skanlite::switchToProfile(const QString &profile, bool ignoreSelection)
{
QMap opts;
readScannerOptions(QString(defaultProfileGroup).arg(m_deviceName).arg(profile), opts);
if (opts.empty()) {
opts = m_defaultScanOpts;
}
processSelectionOptions(opts, ignoreSelection);
m_ksanew->setOptVals(opts);
}
void Skanlite::getDeviceName()
{
m_dbusInterface.setReply(QStringList(m_deviceName));
}
void Skanlite::getSelection()
{
QMap opts;
m_ksanew->getOptVals(opts);
QStringList reply;
foreach ( QString key, selectionSettings ) {
if (opts.contains(key)) {
reply.append(key + QLatin1String("=") + opts[key]);
}
}
m_dbusInterface.setReply(reply);
}
void Skanlite::setSelection(const QStringList &options)
{ // here options contains selection related subset of options
setScannerOptions(options, false);
}