File indexing completed on 2025-04-27 03:58:06
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2006-05-08 0007 * Description : Service menu operation methods 0008 * 0009 * SPDX-FileCopyrightText: 2014-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 2006-2010 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "dservicemenu.h" 0017 0018 // Qt includes 0019 0020 #include <QProcess> 0021 #include <QMimeType> 0022 #include <QMimeDatabase> 0023 #include <QStandardPaths> 0024 #include <QRegularExpression> 0025 0026 // KDE includes 0027 0028 #include <kservice_version.h> 0029 0030 #if KSERVICE_VERSION > QT_VERSION_CHECK(5, 81, 0) 0031 # include <kapplicationtrader.h> 0032 #else 0033 # include <kmimetypetrader.h> 0034 #endif 0035 0036 // Local includes 0037 0038 #include "digikam_debug.h" 0039 #include "digikam_config.h" 0040 #include "digikam_globals.h" 0041 0042 namespace Digikam 0043 { 0044 0045 bool DServiceMenu::runFiles(const KService::Ptr& service, 0046 const QList<QUrl>& urls) 0047 { 0048 return (runFiles(service->exec(), urls, service)); 0049 } 0050 0051 bool DServiceMenu::runFiles(const QString& appCmd, 0052 const QList<QUrl>& urls, 0053 const KService::Ptr& service) 0054 { 0055 QRegularExpression split(QLatin1String(" +(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)")); 0056 QStringList cmdList = appCmd.split(split, QT_SKIP_EMPTY_PARTS); 0057 QList<QUrl> urlList = urls; 0058 0059 if (cmdList.isEmpty() || urlList.isEmpty()) 0060 { 0061 return false; 0062 } 0063 0064 if (!appCmd.contains(QLatin1String("%f"), Qt::CaseInsensitive) && 0065 !appCmd.contains(QLatin1String("%u"), Qt::CaseInsensitive) && 0066 !appCmd.contains(QLatin1String("%d"), Qt::CaseInsensitive)) 0067 { 0068 cmdList << QLatin1String("%f"); 0069 } 0070 0071 QString exec; 0072 QString name; 0073 QString icon; 0074 QString term; 0075 0076 QStringList dirs; 0077 QStringList files; 0078 QStringList cmdArgs; 0079 QStringList termOpts; 0080 0081 bool useTerminal = false; 0082 bool openNewRun = false; 0083 0084 if (service) 0085 { 0086 name = service->desktopEntryName(); 0087 icon = service->icon(); 0088 0089 #ifdef Q_OS_LINUX 0090 0091 if (service->terminal()) 0092 { 0093 termOpts = service->terminalOptions().split(split, QT_SKIP_EMPTY_PARTS); 0094 term = QStandardPaths::findExecutable(QLatin1String("konsole")); 0095 0096 if (term.isEmpty()) 0097 { 0098 term = QStandardPaths::findExecutable(QLatin1String("xterm")); 0099 termOpts.replaceInStrings(QLatin1String("--noclose"), 0100 QLatin1String("-hold")); 0101 } 0102 0103 useTerminal = !term.isEmpty(); 0104 } 0105 0106 #endif // Q_OS_LINUX 0107 0108 } 0109 0110 QProcess* const process = new QProcess(); 0111 QProcessEnvironment env = adjustedEnvironmentForAppImage(); 0112 0113 Q_FOREACH (const QUrl& url, urlList) 0114 { 0115 dirs << url.adjusted(QUrl::RemoveFilename).toLocalFile(); 0116 files << url.toLocalFile(); 0117 } 0118 0119 Q_FOREACH (const QString& cmdString, cmdList) 0120 { 0121 QString cmd = cmdString; 0122 0123 if (cmd.startsWith(QLatin1Char('"')) && cmd.endsWith(QLatin1Char('"'))) 0124 { 0125 cmd.remove(0, 1).chop(1); 0126 } 0127 0128 if (exec.isEmpty() && cmd.contains(QLatin1Char('='))) 0129 { 0130 QStringList envList = cmd.split(QLatin1Char('='), QT_SKIP_EMPTY_PARTS); 0131 0132 if (envList.count() == 2) 0133 { 0134 env.insert(envList[0], envList[1]); 0135 } 0136 0137 continue; 0138 } 0139 else if (exec.isEmpty()) 0140 { 0141 exec = cmd; 0142 continue; 0143 } 0144 0145 if (cmd == QLatin1String("%c")) 0146 { 0147 cmdArgs << name; 0148 } 0149 else if (cmd == QLatin1String("%i")) 0150 { 0151 cmdArgs << icon; 0152 } 0153 else if (cmd == QLatin1String("%f")) 0154 { 0155 cmdArgs << files.first(); 0156 openNewRun = true; 0157 } 0158 else if (cmd == QLatin1String("%F")) 0159 { 0160 cmdArgs << files; 0161 } 0162 else if (cmd == QLatin1String("%u")) 0163 { 0164 cmdArgs << files.first(); 0165 openNewRun = true; 0166 } 0167 else if (cmd == QLatin1String("%U")) 0168 { 0169 cmdArgs << files; 0170 } 0171 else if (cmd == QLatin1String("%d")) 0172 { 0173 cmdArgs << dirs.first(); 0174 openNewRun = true; 0175 } 0176 else if (cmd == QLatin1String("%D")) 0177 { 0178 cmdArgs << dirs; 0179 } 0180 else 0181 { 0182 cmdArgs << cmd; 0183 } 0184 } 0185 0186 process->setProcessEnvironment(env); 0187 0188 if (useTerminal) 0189 { 0190 termOpts << QLatin1String("-e") << exec << cmdArgs; 0191 process->start(term, termOpts); 0192 } 0193 else 0194 { 0195 process->start(exec, cmdArgs); 0196 } 0197 0198 bool ret = true; 0199 ret &= process->waitForStarted(); 0200 0201 if (openNewRun) 0202 { 0203 urlList.removeFirst(); 0204 0205 if (!urlList.isEmpty()) 0206 { 0207 ret &= runFiles(appCmd, urlList, service); 0208 } 0209 } 0210 0211 return ret; 0212 } 0213 0214 KService::List DServiceMenu::servicesForOpenWith(const QList<QUrl>& urls) 0215 { 0216 // This code is inspired by KonqMenuActions: 0217 // kdebase/apps/lib/konq/konq_menuactions.cpp 0218 0219 QStringList mimeTypes; 0220 KService::List offers; 0221 0222 Q_FOREACH (const QUrl& item, urls) 0223 { 0224 const QString mimeType = QMimeDatabase().mimeTypeForFile(item.toLocalFile(), 0225 QMimeDatabase::MatchExtension).name(); 0226 0227 if (!mimeTypes.contains(mimeType)) 0228 { 0229 mimeTypes << mimeType; 0230 } 0231 } 0232 0233 if (!mimeTypes.isEmpty()) 0234 { 0235 // Query trader 0236 0237 const QString firstMimeType = mimeTypes.takeFirst(); 0238 const QString constraintTemplate = QLatin1String("'%1' in ServiceTypes"); 0239 QStringList constraints; 0240 0241 Q_FOREACH (const QString& mimeType, mimeTypes) 0242 { 0243 constraints << constraintTemplate.arg(mimeType); 0244 } 0245 0246 #if KSERVICE_VERSION > QT_VERSION_CHECK(5, 81, 0) 0247 offers = KApplicationTrader::queryByMimeType(firstMimeType); 0248 #else 0249 offers = KMimeTypeTrader::self()->query(firstMimeType, 0250 QLatin1String("Application"), 0251 constraints.join(QLatin1String(" and "))); 0252 #endif 0253 0254 // remove duplicate service entries 0255 0256 QSet<QString> seenApps; 0257 0258 for (KService::List::iterator it = offers.begin() ; it != offers.end() ; ) 0259 { 0260 const QString appName((*it)->name()); 0261 0262 if (!seenApps.contains(appName)) 0263 { 0264 seenApps.insert(appName); 0265 ++it; 0266 } 0267 else 0268 { 0269 it = offers.erase(it); 0270 } 0271 } 0272 } 0273 0274 return offers; 0275 } 0276 0277 } // namespace Digikam