diff --git a/libtaskmanager/taskmanagerrulesrc b/libtaskmanager/taskmanagerrulesrc --- a/libtaskmanager/taskmanagerrulesrc +++ b/libtaskmanager/taskmanagerrulesrc @@ -3,41 +3,8 @@ Google-chrome=Google Chrome Google-chrome-stable=Google Chrome Systemsettings=System Settings -libreoffice-base=LibreOffice Base -libreoffice-calc=LibreOffice Calc -libreoffice-draw=LibreOffice Draw -libreoffice-writer=LibreOffice Writer -libreoffice-math=LibreOffice Math -libreoffice-impress=LibreOffice Impress -libreoffice-startcenter=LibreOffice oracle-ide-boot-Launcher=Oracle SQL Developer Dragon=dragonplayer + [Settings] ManualOnly=Wine - -[Rewrite Rules][google-chrome][1] -Property=ClassName -Identifier=StartupWMClass -Match=(?<=crx_)(?'match'[a-z]+) - -# Google changed the class to be "Google-chrome" around version 54 -[Rewrite Rules][Google-chrome][1] -Property=ClassName -Identifier=StartupWMClass -Match=(?<=crx_)(?'match'[a-z]+) - -[Rewrite Rules][chromium][1] -Property=ClassName -Identifier=StartupWMClass -Match=(?<=crx_)(?'match'[a-z]+) - -[Rewrite Rules][chromium-browser][1] -Property=ClassName -Identifier=StartupWMClass -Match=(?<=crx_)(?'match'[a-z]+) - -# Chromium changed the class too -[Rewrite Rules][Chromium][1] -Property=ClassName -Identifier=StartupWMClass -Match=(?<=crx_)(?'match'[a-z]+) diff --git a/libtaskmanager/xwindowtasksmodel.cpp b/libtaskmanager/xwindowtasksmodel.cpp --- a/libtaskmanager/xwindowtasksmodel.cpp +++ b/libtaskmanager/xwindowtasksmodel.cpp @@ -517,6 +517,7 @@ 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()); @@ -531,15 +532,15 @@ services = servicesFromPid(pid); } - // If the user has manually set a mapping, respect this first... - QString mapped(grp.readEntry(classClass + "::" + className, QString())); + if (!classClass.isEmpty()) { + // Evaluate any mapping rules that map to a specific .desktop file. + QString mapped(grp.readEntry(classClass + "::" + className, QString())); - if (mapped.endsWith(QLatin1String(".desktop"))) { - url = QUrl(mapped); - return url; - } + if (mapped.endsWith(QLatin1String(".desktop"))) { + url = QUrl(mapped); + return url; + } - if (!classClass.isEmpty()) { if (mapped.isEmpty()) { mapped = grp.readEntry(classClass, QString()); @@ -557,75 +558,95 @@ return url; } - KConfigGroup rewriteRulesGroup(rulesConfig, QStringLiteral("Rewrite Rules")); - if (rewriteRulesGroup.hasGroup(classClass)) { - KConfigGroup rewriteGroup(&rewriteRulesGroup, classClass); - - const QStringList &rules = rewriteGroup.groupList(); - for (const QString &rule : rules) { - KConfigGroup ruleGroup(&rewriteGroup, rule); + // Try matching both WM_CLASS instance and general class 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.empty()) { + services = KServiceTypeTrader::self()->query(QStringLiteral("Application"), QStringLiteral("exist Exec and ('%1' =~ StartupWMClass)").arg(classClass)); + } - const QString propertyConfig = ruleGroup.readEntry(QStringLiteral("Property"), QString()); + if (services.empty()) { + services = KServiceTypeTrader::self()->query(QStringLiteral("Application"), QStringLiteral("exist Exec and ('%1' =~ StartupWMClass)").arg(className)); + } - QString matchProperty; - if (propertyConfig == QLatin1String("ClassClass")) { - matchProperty = classClass; - } else if (propertyConfig == QLatin1String("ClassName")) { - matchProperty = className; - } + // Evaluate rewrite rules from config. + if (services.empty()) { + KConfigGroup rewriteRulesGroup(rulesConfig, QStringLiteral("Rewrite Rules")); + if (rewriteRulesGroup.hasGroup(classClass)) { + KConfigGroup rewriteGroup(&rewriteRulesGroup, classClass); - if (matchProperty.isEmpty()) { - continue; - } + const QStringList &rules = rewriteGroup.groupList(); + for (const QString &rule : rules) { + KConfigGroup ruleGroup(&rewriteGroup, rule); - const QString serviceSearchIdentifier = ruleGroup.readEntry(QStringLiteral("Identifier"), QString()); - if (serviceSearchIdentifier.isEmpty()) { - continue; - } + const QString propertyConfig = ruleGroup.readEntry(QStringLiteral("Property"), QString()); - QRegularExpression regExp(ruleGroup.readEntry(QStringLiteral("Match"))); - const auto match = regExp.match(matchProperty); + QString matchProperty; + if (propertyConfig == QLatin1String("ClassClass")) { + matchProperty = classClass; + } else if (propertyConfig == QLatin1String("ClassName")) { + matchProperty = className; + } - if (match.hasMatch()) { - const QString actualMatch = match.captured(QStringLiteral("match")); - if (actualMatch.isEmpty()) { + if (matchProperty.isEmpty()) { continue; } - QString rewrittenString = ruleGroup.readEntry(QStringLiteral("Target")).arg(actualMatch); - // If no "Target" is provided, instead assume the matched property (ClassClass/ClassName). - if (rewrittenString.isEmpty()) { - rewrittenString = matchProperty; + const QString serviceSearchIdentifier = ruleGroup.readEntry(QStringLiteral("Identifier"), QString()); + if (serviceSearchIdentifier.isEmpty()) { + continue; } - services = KServiceTypeTrader::self()->query(QStringLiteral("Application"), QStringLiteral("exist Exec and ('%1' =~ %2)").arg(rewrittenString, serviceSearchIdentifier)); + 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; + } - if (!services.isEmpty()) { - break; + QString rewrittenString = ruleGroup.readEntry(QStringLiteral("Target")).arg(actualMatch); + // If no "Target" is provided, instead assume the matched property (ClassClass/ClassName). + if (rewrittenString.isEmpty()) { + rewrittenString = matchProperty; + } + + services = KServiceTypeTrader::self()->query(QStringLiteral("Application"), QStringLiteral("exist Exec and ('%1' =~ %2)").arg(rewrittenString, serviceSearchIdentifier)); + + if (!services.isEmpty()) { + break; + } } } } } + // Try matching mapped name against DesktopEntryName. if (!mapped.isEmpty() && services.empty()) { services = KServiceTypeTrader::self()->query(QStringLiteral("Application"), QStringLiteral("exist Exec and ('%1' =~ DesktopEntryName)").arg(mapped)); } + // Try matching mapped name against 'Name'. if (!mapped.isEmpty() && services.empty()) { services = KServiceTypeTrader::self()->query(QStringLiteral("Application"), QStringLiteral("exist Exec and ('%1' =~ Name)").arg(mapped)); } - // To match other docks (docky, unity, etc.) attempt to match on DesktopEntryName first ... + // Try matching WM_CLASS general class against DesktopEntryName. if (services.empty()) { services = KServiceTypeTrader::self()->query(QStringLiteral("Application"), QStringLiteral("exist Exec and ('%1' =~ DesktopEntryName)").arg(classClass)); } - // Try StartupWMClass. - if (services.empty()) { - services = KServiceTypeTrader::self()->query(QStringLiteral("Application"), QStringLiteral("exist Exec and ('%1' =~ StartupWMClass)").arg(classClass)); - } - - // Try 'Name' - unfortunately this can be translated, so has a good chance of failing! (As it does for KDE's own "System Settings" (even in English!!)) + // Try matching WM_CLASS general class against 'Name'. + // This has a shaky chance of success as WM_CLASS is untranslated, but 'Name' may be localized. if (services.empty()) { services = KServiceTypeTrader::self()->query(QStringLiteral("Application"), QStringLiteral("exist Exec and ('%1' =~ Name)").arg(classClass)); }