File indexing completed on 2024-05-12 15:55:06
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) 2020 Jonathan Marten <jjm@keelhaul.me.uk> * 0007 * * 0008 * Kooka is free software; you can redistribute it and/or modify it * 0009 * under the terms of the GNU Library General Public License as * 0010 * published by the Free Software Foundation and appearing in the * 0011 * file COPYING included in the packaging of this file; either * 0012 * version 2 of the License, or (at your option) any later version. * 0013 * * 0014 * As a special exception, permission is given to link this program * 0015 * with any version of the KADMOS OCR/ICR engine (a product of * 0016 * reRecognition GmbH, Kreuzlingen), and distribute the resulting * 0017 * executable without including the source code for KADMOS in the * 0018 * source distribution. * 0019 * * 0020 * This program is distributed in the hope that it will be useful, * 0021 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0023 * GNU General Public License for more details. * 0024 * * 0025 * You should have received a copy of the GNU General Public * 0026 * License along with this program; see the file COPYING. If * 0027 * not, see <http://www.gnu.org/licenses/>. * 0028 * * 0029 ************************************************************************/ 0030 0031 #include "destinationapplication.h" 0032 0033 #include <qmimetype.h> 0034 #include <qcombobox.h> 0035 #include <qlabel.h> 0036 0037 #include <kapplicationtrader.h> 0038 #include <kpluginfactory.h> 0039 #include <kservice.h> 0040 #include <klocalizedstring.h> 0041 #include <kio/applicationlauncherjob.h> 0042 #include <kio/jobuidelegatefactory.h> 0043 0044 #include "scanparamspage.h" 0045 #include "kookasettings.h" 0046 #include "destination_logging.h" 0047 0048 0049 K_PLUGIN_FACTORY_WITH_JSON(DestinationApplicationFactory, "kookadestination-application.json", registerPlugin<DestinationApplication>();) 0050 #include "destinationapplication.moc" 0051 0052 0053 DestinationApplication::DestinationApplication(QObject *pnt, const QVariantList &args) 0054 : AbstractDestination(pnt, "DestinationApplication") 0055 { 0056 } 0057 0058 0059 void DestinationApplication::imageScanned(ScanImage::Ptr img) 0060 { 0061 qCDebug(DESTINATION_LOG) << "received image size" << img->size() << "type" << img->imageType(); 0062 const QString appService = mAppsCombo->currentData().toString(); 0063 const QString mimeName = mFormatCombo->currentData().toString(); 0064 qCDebug(DESTINATION_LOG) << "app" << appService << "mime" << mimeName; 0065 0066 ImageFormat fmt = getSaveFormat(mimeName, img); // get format for saving image 0067 if (!fmt.isValid()) return; // must have this now 0068 const QUrl saveUrl = saveTempImage(fmt, img); // save to temporary file 0069 if (!saveUrl.isValid()) return; // could not save image 0070 0071 // Open the temporary file with the selected application service. 0072 // If the service is "Other" (appService is empty), or if there is 0073 // a problem finding the service, then leave 'service' as null and 0074 // the ApplicationLauncherJob will prompt for an application. 0075 // The temporary file will eventually be removed by KIO. 0076 KService::Ptr service; 0077 if (!appService.isEmpty()) 0078 { 0079 service = KService::serviceByDesktopName(appService); 0080 if (service==nullptr) qCWarning(DESTINATION_LOG) << "Cannot find service" << appService; 0081 } 0082 0083 KIO::ApplicationLauncherJob *job = new KIO::ApplicationLauncherJob(service); 0084 job->setUrls(QList<QUrl>() << saveUrl); 0085 job->setRunFlags(KIO::ApplicationLauncherJob::DeleteTemporaryFiles); 0086 job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, parentWidget())); 0087 job->start(); // all done 0088 } 0089 0090 0091 KLocalizedString DestinationApplication::scanDestinationString() 0092 { 0093 const QString appService = mAppsCombo->currentData().toString(); 0094 if (appService.isEmpty()) return (ki18n("Sending to application")); 0095 // selected "Other" 0096 return (kxi18n("Sending to <application>%1</application>").subs(mAppsCombo->currentText())); 0097 } // with application name 0098 0099 0100 void DestinationApplication::createGUI(ScanParamsPage *page) 0101 { 0102 // We do not yet know the eventual image format of the scanned image. 0103 // Therefore we would like, here in the GUI, to offer all of the known 0104 // applications that can handle any image type. However, it does not seem 0105 // to be possible to express this in the trader query language; according 0106 // to https://techbase.kde.org/Development/Tutorials/Services/Traders a 0107 // query such as 0108 // 0109 // 'image/' subin ServiceTypes 0110 // 0111 // should perform a substring match on all of the list entries. However, 0112 // this sort of query appears to return nothing. 0113 // 0114 // Instead we query all of the application service types and then examine 0115 // their supported MIME types. The criterion for including an application 0116 // is that it supports an image MIME type (starting with "image/") which is 0117 // also supported as a QImageWriter format (that is, a format that Kooka can 0118 // save to). 0119 0120 const KService::List allServices = KApplicationTrader::query([](const KService::Ptr &) 0121 { 0122 return (true); 0123 }); 0124 qCDebug(DESTINATION_LOG) << "have" << allServices.count() << "services"; 0125 const QList<QMimeType> *imageMimeTypes = ImageFormat::mimeTypes(); 0126 0127 KService::List validServices; 0128 for (const KService::Ptr &service : allServices) 0129 { 0130 // Okular is an odd case. For whatever reason, the application does 0131 // not have just one desktop file listing all of the MIME types that 0132 // it supports, but a number of such files each listing one or a 0133 // closely related set of MIME types. I suspect that this is so that 0134 // there can be one desktop file for each generator with the MIME 0135 // types that the generator supports. There was a similar hack for 0136 // Ark (https://git.reviewboard.kde.org/r/129617 from 2011) that 0137 // collected a list of MIME types supported by each plugin and passed 0138 // them up to the top level CMakeLists.txt, then using the collected 0139 // list to generate the desktop files, but it was remarked at the time 0140 // that it would be more complicated to do this for Okular. 0141 // 0142 // Since the criteria used to select an application for including in 0143 // the "preferrred applications" list is that it supports at least one 0144 // image MIME type that we also support, this would result in multiple 0145 // entries for Okular. Because the command used to start Okular is the 0146 // same in every case, we special case detect the main Okular application 0147 // here and ignore all of the others (along with any other NoDisplay 0148 // services). 0149 if (service->desktopEntryName()=="org.kde.okular") 0150 { 0151 qCDebug(DESTINATION_LOG) << "accept" << service->desktopEntryName() << "by name, pref" << service->initialPreference(); 0152 validServices.append(service); 0153 continue; 0154 } 0155 0156 if (service->noDisplay()) continue; // ignore hidden services 0157 if (service->mimeTypes().isEmpty()) continue; // ignore those with no MIME types 0158 //qCDebug(DESTINATION_LOG) << " " << service->mimeTypes(); 0159 0160 for (const QString &mimeType : service->mimeTypes()) 0161 { 0162 if (!mimeType.startsWith("image/")) continue; 0163 for (const QMimeType &imt : *imageMimeTypes) 0164 { // supports a MIME type that we also do 0165 if (imt.inherits(mimeType)) goto found; 0166 } 0167 } 0168 continue; // service not accepted 0169 0170 found: qCDebug(DESTINATION_LOG) << "accept" << service->desktopEntryName() << "by MIME, pref" << service->initialPreference(); 0171 validServices.append(service); 0172 } 0173 0174 // Now all of the applications that accept file formats that can be 0175 // saved by Kooka are listed. Fortunately the original trader query 0176 // returned them in priority order, so there is no need to sort them. 0177 qCDebug(DESTINATION_LOG) << "have" << validServices.count() << "valid services"; 0178 0179 mAppsCombo = new QComboBox; 0180 0181 const QString configuredApp = KookaSettings::destinationApplicationApp(); 0182 int configuredIndex = -1; 0183 for (const KService::Ptr &service : qAsConst(validServices)) 0184 { 0185 const QString key = service->desktopEntryName(); 0186 if (key==configuredApp) configuredIndex = mAppsCombo->count(); 0187 mAppsCombo->addItem(QIcon::fromTheme(service->icon()), service->name(), key); 0188 } 0189 if (configuredApp=="") configuredIndex = mAppsCombo->count(); 0190 mAppsCombo->addItem(QIcon::fromTheme("system-run"), i18n("Other...")); 0191 if (configuredIndex!=-1) mAppsCombo->setCurrentIndex(configuredIndex); 0192 page->addRow(i18n("Application:"), mAppsCombo); 0193 0194 // The MIME types that can be selected for sending the image. 0195 QStringList mimeTypes; 0196 mimeTypes << "image/png" << "image/jpeg" << "image/tiff" << "image/x-eps" << "image/bmp"; 0197 mFormatCombo = createFormatCombo(mimeTypes, KookaSettings::destinationApplicationMime()); 0198 page->addRow(i18n("Image format:"), mFormatCombo); 0199 } 0200 0201 0202 void DestinationApplication::saveSettings() const 0203 { 0204 KookaSettings::setDestinationApplicationApp(mAppsCombo->currentData().toString()); 0205 KookaSettings::setDestinationApplicationMime(mFormatCombo->currentData().toString()); 0206 }