File indexing completed on 2024-05-05 16:27:43

0001 /************************************************************************
0002  *                                  *
0003  *  This file is part of Kooka, a scanning/OCR application using    *
0004  *  Qt <http://www.qt.io> and KDE Frameworks <http://www.kde.org>.  *
0005  *                                  *
0006  *  Copyright (C) 2000-2016 Klaas Freitag <freitag@suse.de>     *
0007  *                          Jonathan Marten <jjm@keelhaul.me.uk>    *
0008  *                                  *
0009  *  Kooka is free software; you can redistribute it and/or modify it    *
0010  *  under the terms of the GNU Library General Public License as    *
0011  *  published by the Free Software Foundation and appearing in the  *
0012  *  file COPYING included in the packaging of this file;  either    *
0013  *  version 2 of the License, or (at your option) any later version.    *
0014  *                                  *
0015  *  As a special exception, permission is given to link this program    *
0016  *  with any version of the KADMOS OCR/ICR engine (a product of     *
0017  *  reRecognition GmbH, Kreuzlingen), and distribute the resulting  *
0018  *  executable without including the source code for KADMOS in the  *
0019  *  source distribution.                        *
0020  *                                  *
0021  *  This program is distributed in the hope that it will be useful, *
0022  *  but WITHOUT ANY WARRANTY; without even the implied warranty of  *
0023  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
0024  *  GNU General Public License for more details.            *
0025  *                                  *
0026  *  You should have received a copy of the GNU General Public       *
0027  *  License along with this program;  see the file COPYING.  If     *
0028  *  not, see <http://www.gnu.org/licenses/>.                *
0029  *                                  *
0030  ************************************************************************/
0031 
0032 #include "abstractdestination.h"
0033 
0034 #include <qdir.h>
0035 #include <qmimetype.h>
0036 #include <qmimedatabase.h>
0037 #include <qtemporaryfile.h>
0038 #include <qcoreapplication.h>
0039 #include <qcombobox.h>
0040 #include <qstandardpaths.h>
0041 #include <qcheckbox.h>
0042 #include <qprocess.h>
0043 
0044 #include <kmessagebox.h>
0045 
0046 #include "formatdialog.h"
0047 #include "destination_logging.h"
0048 
0049 
0050 //  Constructor/destructor and plugin creation
0051 //  ------------------------------------------
0052 
0053 AbstractDestination::AbstractDestination(QObject *pnt, const char *name)
0054     : AbstractPlugin(pnt),
0055       mLastUsedFormat(ImageFormat(""))
0056 {
0057     setObjectName(name);
0058     qCDebug(DESTINATION_LOG) << objectName();
0059 
0060     mGallery = nullptr;
0061 }
0062 
0063 
0064 ImageFormat AbstractDestination::getSaveFormat(const QString &mimeName, ScanImage::Ptr img)
0065 {
0066     ImageFormat fmt("");
0067     if (!mimeName.isEmpty())                // have selected a MIME type
0068     {
0069         QMimeDatabase db;               // get image format to use
0070         fmt = ImageFormat::formatForMime(db.mimeTypeForName(mimeName));
0071         if (!fmt.isValid()) qCWarning(DESTINATION_LOG) << "No MIME type or format for" << mimeName;
0072     }
0073 
0074     // If the format is "Other", or there was an error finding the MIME type,
0075     // then prompt for a format.
0076     if (!fmt.isValid()) fmt = mLastUsedFormat;      // try the remembered format
0077     if (!fmt.isValid())                 // no remembered format
0078     {
0079         FormatDialog fd(parentWidget(),         // parent
0080                         img->imageType(),       // type
0081                         true,               // askForFormat
0082                         fmt,                // default format
0083                         false,              // askForFilename
0084                         QString());         // filename
0085                             // special text for check box
0086         fd.alwaysUseFormatCheck()->setText(i18n("Remember this format"));
0087 
0088         if (!fd.exec()) return (fmt);           // dialogue cancelled
0089         fmt = fd.getFormat();               // get the selected format
0090 
0091         // If the "Remember" check box is ticked, then note the format for
0092         // next time.  The format is only remembered for the lifetime of this
0093         // plugin, and does not affect the main Kooka setting.
0094         if (fd.alwaysUseFormat()) mLastUsedFormat = fmt;
0095     }
0096 
0097     // TODO: even if there is no need to ask for a format then maybe ask
0098     // for a file name, so that it will be shown at the other end of, e.g.
0099     // a Bluetooth share.
0100 
0101     qCDebug(DESTINATION_LOG) << "format" << fmt << "ext" << fmt.extension();
0102     return (fmt);
0103 }
0104 
0105 
0106 QUrl AbstractDestination::saveTempImage(const ImageFormat &fmt, ScanImage::Ptr img)
0107 {
0108     // Save the image to a temporary file, in the format specified.
0109     QTemporaryFile temp(QDir::tempPath()+"/"+QCoreApplication::applicationName()+"XXXXXX."+fmt.extension());
0110     temp.setAutoRemove(false);
0111     temp.open();
0112     QUrl saveUrl = QUrl::fromLocalFile(temp.fileName());
0113     temp.close();                   // now have name, but do not remove
0114     qCDebug(DESTINATION_LOG) << "save to" << saveUrl;   // temporary file location
0115 
0116     ImgSaver saver;                 // save the image
0117     ImgSaver::ImageSaveStatus status = saver.saveImage(img, saveUrl, fmt);
0118     if (status!=ImgSaver::SaveStatusOk)         // image save failed
0119     {
0120         KMessageBox::error(parentWidget(), xi18nc("@info", "Cannot save temporary image file<nl/><filename>%1</filename><nl/><message>%2</message>",
0121                                                   saveUrl.toDisplayString(), saver.errorString(status)));
0122         temp.setAutoRemove(true);           // clean up temporary file
0123         return (QUrl());                // indicate could not save
0124     }
0125 
0126     return (saveUrl);                   // of the temporary file
0127 }
0128 
0129 
0130 QComboBox *AbstractDestination::createFormatCombo(const QStringList &mimeTypes,
0131                                                   const QString &configuredMime)
0132 {
0133     // For the image format combo, we do not yet know the format of the
0134     // scanned image.  Therefore the approach taken here, trying to balance
0135     // versatility against not confusing the user with a long list of obscure
0136     // image file formats, is to offer a small predetermined list of popular
0137     // formats in the combo, followed by an "Other..." option.  The last
0138     // option uses the Save Assistant to select an image type (but without the
0139     // option to enter the file name).
0140     //
0141     // It is the user's responsibility to make sure that the selected
0142     // application accepts the selected format, so the explicit formats should
0143     // be ones that are accepted by most common applications.
0144 
0145     QComboBox *combo = new QComboBox;
0146     int configuredIndex = -1;
0147 
0148     QMimeDatabase db;
0149     for (const QString &mimeName : mimeTypes)
0150     {
0151         const QMimeType mimeType = db.mimeTypeForName(mimeName);
0152         const ImageFormat fmt = ImageFormat::formatForMime(mimeType);
0153         if (!fmt.isValid()) continue;           // this format not supported
0154 
0155         if (mimeName==configuredMime) configuredIndex = combo->count();
0156         combo->addItem(QIcon::fromTheme(mimeType.iconName()), mimeType.comment(), mimeType.name());
0157     }
0158 
0159     if (configuredMime=="") configuredIndex = combo->count();
0160     combo->addItem(QIcon::fromTheme("system-run"), i18n("Other..."));
0161     if (configuredIndex!=-1) combo->setCurrentIndex(configuredIndex);
0162 
0163     return (combo);                 // the created combo box
0164 }
0165 
0166 
0167 void AbstractDestination::delayedDelete(const QUrl &url)
0168 {
0169     // A destination may need the scanned image to persist until a share or
0170     // export is complete, and it may not even be able to tell when an image
0171     // is no longer required.  So as to be absolutely sure that the file
0172     // stays around for enough time, even if the application is quit, use the
0173     // kioexec utility (normally used to open a remote file in an application
0174     // that does not support KIO) to not do anything with the file but delete
0175     // it after three minutes have passed.  Calling this in a detached process
0176     // allows Kooka to exit cleanly even if waiting for that time delay.
0177     //
0178     // If the temporary file is in a subdirectory, then kioexec will also
0179     // delete the containing directory if it is empty.
0180 
0181     if (!url.isLocalFile()) return;             // should always be the case
0182 
0183     // from second test in kio/src/core/desktopexecparser.cpp
0184     const QString kioexec = (LIBEXEC_DIR "/kioexec");
0185     QStringList args;
0186     args << "--tempfiles";                  // delete temporary files
0187     args << QStandardPaths::findExecutable("true")+" %f";   // do nothing with the file
0188     args << url.url();
0189 
0190     qCDebug(DESTINATION_LOG) << "running" << kioexec << args;
0191     if (!QProcess::startDetached(kioexec, args)) qCWarning(DESTINATION_LOG) << "Cannot start detached process";
0192 }