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 }