/********************************************************************
Copyright 2016 Eike Hein < hein . org >
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation ; either
version 2.1 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 6 of version 3 of the license .
This library 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
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public
License along with this library . If not , see < http : //www.gnu.org/licenses/>.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "tasktools.h"
# include <KActivities/ResourceInstance>
# include <KConfigGroup>
# include <KDesktopFile>
# include <kemailsettings.h>
//#include <KFileItem>
# include <KMimeTypeTrader>
//#include <KRun>
# include <KServiceTypeTrader>
# include <KSharedConfig>
# include <KStartupInfo>
# include <KWindowSystem>
# include <processcore/processes.h>
# include <processcore/process.h>
# include <config-latte.h>
# include <QDir>
# include <QGuiApplication>
# include <QRegularExpression>
# include <QScreen>
# include <QUrlQuery>
# if HAVE_X11
# include <QX11Info>
# endif
namespace Latte
{
namespace WindowSystem
{
AppData appDataFromUrl ( const QUrl & url , const QIcon & fallbackIcon )
{
AppData data ;
data . url = url ;
if ( url . hasQuery ( ) ) {
QUrlQuery uQuery ( url ) ;
if ( uQuery . hasQueryItem ( QLatin1String ( " iconData " ) ) ) {
QString iconData ( uQuery . queryItemValue ( QLatin1String ( " iconData " ) ) ) ;
QPixmap pixmap ;
QByteArray bytes = QByteArray : : fromBase64 ( iconData . toLocal8Bit ( ) , QByteArray : : Base64UrlEncoding ) ;
pixmap . loadFromData ( bytes ) ;
data . icon . addPixmap ( pixmap ) ;
}
if ( uQuery . hasQueryItem ( QLatin1String ( " skipTaskbar " ) ) ) {
QString skipTaskbar ( uQuery . queryItemValue ( QLatin1String ( " skipTaskbar " ) ) ) ;
data . skipTaskbar = ( skipTaskbar = = QStringLiteral ( " true " ) ) ;
}
}
// applications: URLs are used to refer to applications by their KService::menuId
// (i.e. .desktop file name) rather than the absolute path to a .desktop file.
if ( url . scheme ( ) = = QStringLiteral ( " applications " ) ) {
const KService : : Ptr service = KService : : serviceByMenuId ( url . path ( ) ) ;
if ( service & & url . path ( ) = = service - > menuId ( ) ) {
data . name = service - > name ( ) ;
data . genericName = service - > genericName ( ) ;
data . id = service - > storageId ( ) ;
if ( data . icon . isNull ( ) ) {
data . icon = QIcon : : fromTheme ( service - > icon ( ) ) ;
}
}
}
if ( url . isLocalFile ( ) & & KDesktopFile : : isDesktopFile ( url . toLocalFile ( ) ) ) {
const KService : : Ptr service = KService : : serviceByStorageId ( url . fileName ( ) ) ;
// Resolve to non-absolute menuId-based URL if possible.
if ( service ) {
const QString & menuId = service - > menuId ( ) ;
if ( ! menuId . isEmpty ( ) ) {
data . url = QUrl ( QStringLiteral ( " applications: " ) + menuId ) ;
}
}
if ( service & & QUrl : : fromLocalFile ( service - > entryPath ( ) ) = = url ) {
data . name = service - > name ( ) ;
data . genericName = service - > genericName ( ) ;
data . id = service - > storageId ( ) ;
if ( data . icon . isNull ( ) ) {
data . icon = QIcon : : fromTheme ( service - > icon ( ) ) ;
}
} else {
KDesktopFile f ( url . toLocalFile ( ) ) ;
if ( f . tryExec ( ) ) {
data . name = f . readName ( ) ;
data . genericName = f . readGenericName ( ) ;
data . id = QUrl : : fromLocalFile ( f . fileName ( ) ) . fileName ( ) ;
if ( data . icon . isNull ( ) ) {
data . icon = QIcon : : fromTheme ( f . readIcon ( ) ) ;
}
}
}
if ( data . id . endsWith ( " .desktop " ) ) {
data . id = data . id . left ( data . id . length ( ) - 8 ) ;
}
} else if ( url . scheme ( ) = = QLatin1String ( " preferred " ) ) {
data . id = defaultApplication ( url ) ;
const KService : : Ptr service = KService : : serviceByStorageId ( data . id ) ;
if ( service ) {
const QString & menuId = service - > menuId ( ) ;
const QString & desktopFile = service - > entryPath ( ) ;
data . name = service - > name ( ) ;
data . genericName = service - > genericName ( ) ;
data . id = service - > storageId ( ) ;
if ( data . icon . isNull ( ) ) {
data . icon = QIcon : : fromTheme ( service - > icon ( ) ) ;
}
// Update with resolved URL.
if ( ! menuId . isEmpty ( ) ) {
data . url = QUrl ( QStringLiteral ( " applications: " ) + menuId ) ;
} else {
data . url = QUrl : : fromLocalFile ( desktopFile ) ;
}
}
}
if ( data . name . isEmpty ( ) ) {
data . name = url . fileName ( ) ;
}
if ( data . icon . isNull ( ) ) {
data . icon = fallbackIcon ;
}
return data ;
}
AppData appDataFromAppId ( const QString & appId )
{
AppData data ;
KService : : Ptr service = KService : : serviceByStorageId ( appId ) ;
if ( service ) {
data . id = service - > storageId ( ) ;
data . name = service - > name ( ) ;
data . genericName = service - > genericName ( ) ;
const QString & menuId = service - > menuId ( ) ;
// applications: URLs are used to refer to applications by their KService::menuId
// (i.e. .desktop file name) rather than the absolute path to a .desktop file.
if ( ! menuId . isEmpty ( ) ) {
data . url = QUrl ( QStringLiteral ( " applications: " ) + menuId ) ;
} else {
data . url = QUrl : : fromLocalFile ( service - > entryPath ( ) ) ;
}
return data ;
}
QString desktopFile = appId ;
if ( ! desktopFile . endsWith ( QLatin1String ( " .desktop " ) ) ) {
desktopFile . append ( QLatin1String ( " .desktop " ) ) ;
}
if ( KDesktopFile : : isDesktopFile ( desktopFile ) & & QFile : : exists ( desktopFile ) ) {
KDesktopFile f ( desktopFile ) ;
data . id = QUrl : : fromLocalFile ( f . fileName ( ) ) . fileName ( ) ;
if ( data . id . endsWith ( QLatin1String ( " .desktop " ) ) ) {
data . id = data . id . left ( data . id . length ( ) - 8 ) ;
}
data . name = f . readName ( ) ;
data . genericName = f . readGenericName ( ) ;
data . url = QUrl : : fromLocalFile ( desktopFile ) ;
}
return data ;
}
QUrl windowUrlFromMetadata ( const QString & appId , quint32 pid ,
KSharedConfig : : Ptr rulesConfig , const QString & xWindowsWMClassName )
{
if ( ! rulesConfig ) {
return QUrl ( ) ;
}
QUrl url ;
KService : : List services ;
bool triedPid = false ;
// The code below this function goes on a hunt for services based on the metadata
// that has been passed in. Occasionally, it will find more than one matching
// service. In some scenarios (e.g. multiple identically-named .desktop files)
// there's a need to pick the most useful one. The function below promises to "sort"
// a list of services by how closely their KService::menuId() relates to the key that
// has been passed in. The current naive implementation simply looks for a menuId
// that starts with the key, prepends it to the list and returns it. In practice,
// that means a KService with a menuId matching the appId will win over one with a
// menuId that encodes a subfolder hierarchy.
// A concrete example: Valve's Steam client is sometimes installed two times, once
// natively as a Linux application, once via Wine. Both have .desktop files named
// (S|)steam.desktop. The Linux native version is located in the menu by means of
// categorization ("Games") and just has a menuId() matching the .desktop file name,
// but the Wine version is placed in a folder hierarchy by Wine and gets a menuId()
// of wine-Programs-Steam-Steam.desktop. The weighing done by this function makes
// sure the Linux native version gets mapped to the former, while other heuristics
// map the Wine version reliably to the latter.
// In lieu of this weighing we just used whatever KServiceTypeTrader returned first,
// so what we do here can be no worse.
auto sortServicesByMenuId = [ ] ( KService : : List & services , const QString & key ) {
if ( services . count ( ) = = 1 ) {
return ;
}
for ( const auto service : services ) {
if ( service - > menuId ( ) . startsWith ( key , Qt : : CaseInsensitive ) ) {
services . prepend ( service ) ;
return ;
}
}
} ;
if ( ! ( appId . isEmpty ( ) & & xWindowsWMClassName . isEmpty ( ) ) ) {
// Check to see if this wmClass matched a saved one ...
KConfigGroup grp ( rulesConfig , " Mapping " ) ;
KConfigGroup set ( rulesConfig , " Settings " ) ;
// Evaluate MatchCommandLineFirst directives from config first.
// Some apps have different launchers depending upon command line ...
QStringList matchCommandLineFirst = set . readEntry ( " MatchCommandLineFirst " , QStringList ( ) ) ;
if ( ! appId . isEmpty ( ) & & matchCommandLineFirst . contains ( appId ) ) {
triedPid = true ;
services = servicesFromPid ( pid , rulesConfig ) ;
}
// Try to match using xWindowsWMClassName also.
if ( ! xWindowsWMClassName . isEmpty ( ) & & matchCommandLineFirst . contains ( " :: " + xWindowsWMClassName ) ) {
triedPid = true ;
services = servicesFromPid ( pid , rulesConfig ) ;
}
if ( ! appId . isEmpty ( ) ) {
// Evaluate any mapping rules that map to a specific .desktop file.
QString mapped ( grp . readEntry ( appId + " :: " + xWindowsWMClassName , QString ( ) ) ) ;
if ( mapped . endsWith ( QLatin1String ( " .desktop " ) ) ) {
url = QUrl ( mapped ) ;
return url ;
}
if ( mapped . isEmpty ( ) ) {
mapped = grp . readEntry ( appId , QString ( ) ) ;
if ( mapped . endsWith ( QLatin1String ( " .desktop " ) ) ) {
url = QUrl ( mapped ) ;
return url ;
}
}
// Some apps, such as Wine, cannot use xWindowsWMClassName to map to launcher name - as Wine itself is not a GUI app
// So, Settings/ManualOnly lists window classes where the user will always have to manualy set the launcher ...
QStringList manualOnly = set . readEntry ( " ManualOnly " , QStringList ( ) ) ;
if ( ! appId . isEmpty ( ) & & manualOnly . contains ( appId ) ) {
return url ;
}
// Try matching both appId and xWindowsWMClassName against StartupWMClass.
// We do this before evaluating the mapping rules further, because StartupWMClass
// is essentially a mapping rule, and we expect it to be set deliberately and
// sensibly to instruct us what to do. Also, mapping rules
//
// StartupWMClass=STRING
//
// If true, it is KNOWN that the application will map at least one
// window with the given string as its WM class or WM name hint.
//
// Source: https://specifications.freedesktop.org/startup-notification-spec/startup-notification-0.1.txt
if ( services . isEmpty ( ) ) {
services = KServiceTypeTrader : : self ( ) - > query ( QStringLiteral ( " Application " ) , QStringLiteral ( " exist Exec and ('%1' =~ StartupWMClass) " ) . arg ( appId ) ) ;
sortServicesByMenuId ( services , appId ) ;
}
if ( services . isEmpty ( ) & & ! xWindowsWMClassName . isEmpty ( ) ) {
services = KServiceTypeTrader : : self ( ) - > query ( QStringLiteral ( " Application " ) , QStringLiteral ( " exist Exec and ('%1' =~ StartupWMClass) " ) . arg ( xWindowsWMClassName ) ) ;
sortServicesByMenuId ( services , xWindowsWMClassName ) ;
}
// Evaluate rewrite rules from config.
if ( services . isEmpty ( ) ) {
KConfigGroup rewriteRulesGroup ( rulesConfig , QStringLiteral ( " Rewrite Rules " ) ) ;
if ( rewriteRulesGroup . hasGroup ( appId ) ) {
KConfigGroup rewriteGroup ( & rewriteRulesGroup , appId ) ;
const QStringList & rules = rewriteGroup . groupList ( ) ;
for ( const QString & rule : rules ) {
KConfigGroup ruleGroup ( & rewriteGroup , rule ) ;
const QString propertyConfig = ruleGroup . readEntry ( QStringLiteral ( " Property " ) , QString ( ) ) ;
QString matchProperty ;
if ( propertyConfig = = QLatin1String ( " ClassClass " ) ) {
matchProperty = appId ;
} else if ( propertyConfig = = QLatin1String ( " ClassName " ) ) {
matchProperty = xWindowsWMClassName ;
}
if ( matchProperty . isEmpty ( ) ) {
continue ;
}
const QString serviceSearchIdentifier = ruleGroup . readEntry ( QStringLiteral ( " Identifier " ) , QString ( ) ) ;
if ( serviceSearchIdentifier . isEmpty ( ) ) {
continue ;
}
QRegularExpression regExp ( ruleGroup . readEntry ( QStringLiteral ( " Match " ) ) ) ;
const auto match = regExp . match ( matchProperty ) ;
if ( match . hasMatch ( ) ) {
const QString actualMatch = match . captured ( QStringLiteral ( " match " ) ) ;
if ( actualMatch . isEmpty ( ) ) {
continue ;
}
QString rewrittenString = ruleGroup . readEntry ( QStringLiteral ( " Target " ) ) . arg ( actualMatch ) ;
// If no "Target" is provided, instead assume the matched property (appId/xWindowsWMClassName).
if ( rewrittenString . isEmpty ( ) ) {
rewrittenString = matchProperty ;
}
services = KServiceTypeTrader : : self ( ) - > query ( QStringLiteral ( " Application " ) , QStringLiteral ( " exist Exec and ('%1' =~ %2) " ) . arg ( rewrittenString , serviceSearchIdentifier ) ) ;
sortServicesByMenuId ( services , serviceSearchIdentifier ) ;
if ( ! services . isEmpty ( ) ) {
break ;
}
}
}
}
}
// The appId looks like a path.
if ( services . isEmpty ( ) & & appId . startsWith ( QStringLiteral ( " / " ) ) ) {
// Check if it's a path to a .desktop file.
if ( KDesktopFile : : isDesktopFile ( appId ) & & QFile : : exists ( appId ) ) {
return QUrl : : fromLocalFile ( appId ) ;
}
// Check if the appId passes as a .desktop file path if we add the extension.
const QString appIdPlusExtension ( appId + QStringLiteral ( " .desktop " ) ) ;
if ( KDesktopFile : : isDesktopFile ( appIdPlusExtension ) & & QFile : : exists ( appIdPlusExtension ) ) {
return QUrl : : fromLocalFile ( appIdPlusExtension ) ;
}
}
// Try matching mapped name against DesktopEntryName.
if ( ! mapped . isEmpty ( ) & & services . isEmpty ( ) ) {
services = KServiceTypeTrader : : self ( ) - > query ( QStringLiteral ( " Application " ) , QStringLiteral ( " exist Exec and ('%1' =~ DesktopEntryName) and (not exist NoDisplay or not NoDisplay) " ) . arg ( mapped ) ) ;
sortServicesByMenuId ( services , mapped ) ;
}
// Try matching mapped name against 'Name'.
if ( ! mapped . isEmpty ( ) & & services . isEmpty ( ) ) {
services = KServiceTypeTrader : : self ( ) - > query ( QStringLiteral ( " Application " ) , QStringLiteral ( " exist Exec and ('%1' =~ Name) and (not exist NoDisplay or not NoDisplay) " ) . arg ( mapped ) ) ;
sortServicesByMenuId ( services , mapped ) ;
}
// Try matching appId against DesktopEntryName.
if ( services . isEmpty ( ) ) {
services = KServiceTypeTrader : : self ( ) - > query ( QStringLiteral ( " Application " ) , QStringLiteral ( " exist Exec and ('%1' =~ DesktopEntryName) and (not exist NoDisplay or not NoDisplay) " ) . arg ( appId ) ) ;
sortServicesByMenuId ( services , appId ) ;
}
// Try matching appId against 'Name'.
// This has a shaky chance of success as appId is untranslated, but 'Name' may be localized.
if ( services . isEmpty ( ) ) {
services = KServiceTypeTrader : : self ( ) - > query ( QStringLiteral ( " Application " ) , QStringLiteral ( " exist Exec and ('%1' =~ Name) and (not exist NoDisplay or not NoDisplay) " ) . arg ( appId ) ) ;
sortServicesByMenuId ( services , appId ) ;
}
// Check rules configuration for whether we want to hide this task.
// Some window tasks update from bogus to useful metadata early during startup.
// This config key allows listing the bogus metadata, and the matching window
// tasks are hidden until they perform a metadate update that stops them from
// matching.
QStringList skipTaskbar = set . readEntry ( " SkipTaskbar " , QStringList ( ) ) ;
if ( skipTaskbar . contains ( appId ) ) {
QUrlQuery query ( url ) ;
query . addQueryItem ( QStringLiteral ( " skipTaskbar " ) , QStringLiteral ( " true " ) ) ;
url . setQuery ( query ) ;
} else if ( skipTaskbar . contains ( mapped ) ) {
QUrlQuery query ( url ) ;
query . addQueryItem ( QStringLiteral ( " skipTaskbar " ) , QStringLiteral ( " true " ) ) ;
url . setQuery ( query ) ;
}
}
// Ok, absolute *last* chance, try matching via pid (but only if we have not already tried this!) ...
if ( services . isEmpty ( ) & & ! triedPid ) {
services = servicesFromPid ( pid , rulesConfig ) ;
}
}
// Try to improve on a possible from-binary fallback.
// If no services were found or we got a fake-service back from getServicesViaPid()
// we attempt to improve on this by adding a loosely matched reverse-domain-name
// DesktopEntryName. Namely anything that is '*.appId.desktop' would qualify here.
//
// Illustrative example of a case where the above heuristics would fail to produce
// a reasonable result:
// - org.kde.dragonplayer.desktop
// - binary is 'dragon'
// - qapp appname and thus appId is 'dragonplayer'
// - appId cannot directly match the desktop file because of RDN
// - appId also cannot match the binary because of name mismatch
// - in the following code *.appId can match org.kde.dragonplayer though
if ( services . isEmpty ( ) | | services . at ( 0 ) - > desktopEntryName ( ) . isEmpty ( ) ) {
auto matchingServices = KServiceTypeTrader : : self ( ) - > query ( QStringLiteral ( " Application " ) ,
QStringLiteral ( " exist Exec and ('%1' ~~ DesktopEntryName) " ) . arg ( appId ) ) ;
QMutableListIterator < KService : : Ptr > it ( matchingServices ) ;
while ( it . hasNext ( ) ) {
auto service = it . next ( ) ;
if ( ! service - > desktopEntryName ( ) . endsWith ( " . " + appId ) ) {
it . remove ( ) ;
}
}
// Exactly one match is expected, otherwise we discard the results as to reduce
// the likelihood of false-positive mappings. Since we essentially eliminate the
// uniqueness that RDN is meant to bring to the table we could potentially end
// up with more than one match here.
if ( matchingServices . length ( ) = = 1 ) {
services = matchingServices ;
}
}
if ( ! services . isEmpty ( ) ) {
const QString & menuId = services . at ( 0 ) - > menuId ( ) ;
// applications: URLs are used to refer to applications by their KService::menuId
// (i.e. .desktop file name) rather than the absolute path to a .desktop file.
if ( ! menuId . isEmpty ( ) ) {
url . setUrl ( QStringLiteral ( " applications: " ) + menuId ) ;
return url ;
}
QString path = services . at ( 0 ) - > entryPath ( ) ;
if ( path . isEmpty ( ) ) {
path = services . at ( 0 ) - > exec ( ) ;
}
if ( ! path . isEmpty ( ) ) {
QString query = url . query ( ) ;
url = QUrl : : fromLocalFile ( path ) ;
url . setQuery ( query ) ;
return url ;
}
}
return url ;
}
KService : : List servicesFromPid ( quint32 pid , KSharedConfig : : Ptr rulesConfig )
{
if ( pid = = 0 ) {
return KService : : List ( ) ;
}
if ( ! rulesConfig ) {
return KService : : List ( ) ;
}
KSysGuard : : Processes procs ;
procs . updateOrAddProcess ( pid ) ;
KSysGuard : : Process * proc = procs . getProcess ( pid ) ;
const QString & cmdLine = proc ? proc - > command ( ) . simplified ( ) : QString ( ) ; // proc->command has a trailing space???
if ( cmdLine . isEmpty ( ) ) {
return KService : : List ( ) ;
}
return servicesFromCmdLine ( cmdLine , proc - > name ( ) , rulesConfig ) ;
}
KService : : List servicesFromCmdLine ( const QString & _cmdLine , const QString & processName ,
KSharedConfig : : Ptr rulesConfig )
{
QString cmdLine = _cmdLine ;
KService : : List services ;
if ( ! rulesConfig ) {
return services ;
}
const int firstSpace = cmdLine . indexOf ( ' ' ) ;
int slash = 0 ;
services = KServiceTypeTrader : : self ( ) - > query ( QStringLiteral ( " Application " ) , QStringLiteral ( " exist Exec and ('%1' =~ Exec) " ) . arg ( cmdLine ) ) ;
if ( services . isEmpty ( ) ) {
// Could not find with complete command line, so strip out the path part ...
slash = cmdLine . lastIndexOf ( ' / ' , firstSpace ) ;
if ( slash > 0 ) {
services = KServiceTypeTrader : : self ( ) - > query ( QStringLiteral ( " Application " ) , QStringLiteral ( " exist Exec and ('%1' =~ Exec) " ) . arg ( cmdLine . mid ( slash + 1 ) ) ) ;
}
}
if ( services . isEmpty ( ) & & firstSpace > 0 ) {
// Could not find with arguments, so try without ...
cmdLine = cmdLine . left ( firstSpace ) ;
services = KServiceTypeTrader : : self ( ) - > query ( QStringLiteral ( " Application " ) , QStringLiteral ( " exist Exec and ('%1' =~ Exec) " ) . arg ( cmdLine ) ) ;
if ( services . isEmpty ( ) ) {
slash = cmdLine . lastIndexOf ( ' / ' ) ;
if ( slash > 0 ) {
services = KServiceTypeTrader : : self ( ) - > query ( QStringLiteral ( " Application " ) , QStringLiteral ( " exist Exec and ('%1' =~ Exec) " ) . arg ( cmdLine . mid ( slash + 1 ) ) ) ;
}
}
}
if ( services . isEmpty ( ) ) {
KConfigGroup set ( rulesConfig , " Settings " ) ;
const QStringList & runtimes = set . readEntry ( " TryIgnoreRuntimes " , QStringList ( ) ) ;
bool ignore = runtimes . contains ( cmdLine ) ;
if ( ! ignore & & slash > 0 ) {
ignore = runtimes . contains ( cmdLine . mid ( slash + 1 ) ) ;
}
if ( ignore ) {
return servicesFromCmdLine ( _cmdLine . mid ( firstSpace + 1 ) , processName , rulesConfig ) ;
}
}
if ( services . isEmpty ( ) & & ! processName . isEmpty ( ) & & ! QStandardPaths : : findExecutable ( cmdLine ) . isEmpty ( ) ) {
// cmdLine now exists without arguments if there were any.
services < < QExplicitlySharedDataPointer < KService > ( new KService ( processName , cmdLine , QString ( ) ) ) ;
}
return services ;
}
QString defaultApplication ( const QUrl & url )
{
if ( url . scheme ( ) ! = QLatin1String ( " preferred " ) ) {
return QString ( ) ;
}
const QString & application = url . host ( ) ;
if ( application . isEmpty ( ) ) {
return QString ( ) ;
}
if ( application . compare ( QLatin1String ( " mailer " ) , Qt : : CaseInsensitive ) = = 0 ) {
KEMailSettings settings ;
// In KToolInvocation, the default is kmail; but let's be friendlier.
QString command = settings . getSetting ( KEMailSettings : : ClientProgram ) ;
if ( command . isEmpty ( ) ) {
if ( KService : : Ptr kontact = KService : : serviceByStorageId ( QStringLiteral ( " kontact " ) ) ) {
return kontact - > storageId ( ) ;
} else if ( KService : : Ptr kmail = KService : : serviceByStorageId ( QStringLiteral ( " kmail " ) ) ) {
return kmail - > storageId ( ) ;
}
}
if ( ! command . isEmpty ( ) ) {
if ( settings . getSetting ( KEMailSettings : : ClientTerminal ) = = QLatin1String ( " true " ) ) {
KConfigGroup confGroup ( KSharedConfig : : openConfig ( ) , " General " ) ;
const QString preferredTerminal = confGroup . readPathEntry ( " TerminalApplication " ,
QStringLiteral ( " konsole " ) ) ;
command = preferredTerminal + QLatin1String ( " -e " ) + command ;
}
return command ;
}
} else if ( application . compare ( QLatin1String ( " browser " ) , Qt : : CaseInsensitive ) = = 0 ) {
KConfigGroup config ( KSharedConfig : : openConfig ( ) , " General " ) ;
QString browserApp = config . readPathEntry ( " BrowserApplication " , QString ( ) ) ;
if ( browserApp . isEmpty ( ) ) {
const KService : : Ptr htmlApp = KMimeTypeTrader : : self ( ) - > preferredService ( QStringLiteral ( " text/html " ) ) ;
if ( htmlApp ) {
browserApp = htmlApp - > storageId ( ) ;
}
} else if ( browserApp . startsWith ( ' ! ' ) ) {
browserApp = browserApp . mid ( 1 ) ;
}
return browserApp ;
} else if ( application . compare ( QLatin1String ( " terminal " ) , Qt : : CaseInsensitive ) = = 0 ) {
KConfigGroup confGroup ( KSharedConfig : : openConfig ( ) , " General " ) ;
return confGroup . readPathEntry ( " TerminalApplication " , QStringLiteral ( " konsole " ) ) ;
} else if ( application . compare ( QLatin1String ( " filemanager " ) , Qt : : CaseInsensitive ) = = 0 ) {
KService : : Ptr service = KMimeTypeTrader : : self ( ) - > preferredService ( QStringLiteral ( " inode/directory " ) ) ;
if ( service ) {
return service - > storageId ( ) ;
}
} else if ( KService : : Ptr service = KMimeTypeTrader : : self ( ) - > preferredService ( application ) ) {
return service - > storageId ( ) ;
} else {
// Try the files in share/apps/kcm_componentchooser/*.desktop.
QStringList directories = QStandardPaths : : locateAll ( QStandardPaths : : GenericDataLocation , QStringLiteral ( " kcm_componentchooser " ) , QStandardPaths : : LocateDirectory ) ;
QStringList services ;
foreach ( const QString & directory , directories ) {
QDir dir ( directory ) ;
foreach ( const QString & f , dir . entryList ( QStringList ( " *.desktop " ) ) )
services + = dir . absoluteFilePath ( f ) ;
}
foreach ( const QString & service , services ) {
KConfig config ( service , KConfig : : SimpleConfig ) ;
KConfigGroup cg = config . group ( QByteArray ( ) ) ;
const QString type = cg . readEntry ( " valueName " , QString ( ) ) ;
if ( type . compare ( application , Qt : : CaseInsensitive ) = = 0 ) {
KConfig store ( cg . readPathEntry ( " storeInFile " , QStringLiteral ( " null " ) ) ) ;
KConfigGroup storeCg ( & store , cg . readEntry ( " valueSection " , QString ( ) ) ) ;
const QString exec = storeCg . readPathEntry ( cg . readEntry ( " valueName " , " kcm_componenchooser_null " ) ,
cg . readEntry ( " defaultImplementation " , QString ( ) ) ) ;
if ( ! exec . isEmpty ( ) ) {
return exec ;
}
break ;
}
}
}
return QString ( " " ) ;
}
}
}