diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ project(plasma-workspace) -set(PROJECT_VERSION "5.8.3") +set(PROJECT_VERSION "5.8.90") set(PROJECT_VERSION_MAJOR 5) cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) @@ -26,8 +26,13 @@ find_package(KF5 REQUIRED COMPONENTS PlasmaQuick) find_package(KF5 REQUIRED COMPONENTS SysGuard) find_package(KF5 REQUIRED COMPONENTS Package) -# Baloo has a different version scheme than KF5 for now -find_package(KF5 5.1 REQUIRED COMPONENTS Baloo) + +find_package(KF5Baloo) +set_package_properties(KF5Baloo PROPERTIES DESCRIPTION "File Searching" + TYPE RECOMMENDED + PURPOSE "Needed for the File Search runner." + ) + find_package(KF5TextEditor) find_package(KWinDBusInterface CONFIG REQUIRED) diff --git a/applets/clipboard/contents/ui/ClipboardPage.qml b/applets/clipboard/contents/ui/ClipboardPage.qml --- a/applets/clipboard/contents/ui/ClipboardPage.qml +++ b/applets/clipboard/contents/ui/ClipboardPage.qml @@ -50,12 +50,10 @@ break; } case Qt.Key_Escape: { - if (filter.text == "") { - plasmoid.expanded = false; - } else { + if (filter.text != "") { filter.text = ""; + event.accepted = true; } - event.accepted = true; break; } default: { // forward key to filter diff --git a/applets/systemtray/package/contents/ui/ConfigEntries.qml b/applets/systemtray/package/contents/ui/ConfigEntries.qml --- a/applets/systemtray/package/contents/ui/ConfigEntries.qml +++ b/applets/systemtray/package/contents/ui/ConfigEntries.qml @@ -75,7 +75,7 @@ "index": i, "taskId": item.Id, "name": item.Title, - "iconName": plasmoid.nativeInterface.resolveIcon(item.IconName, item.IconThemePath), + "iconName": item.IconName, "icon": item.Icon }); } diff --git a/applets/systemtray/package/contents/ui/PlasmoidPopupsContainer.qml b/applets/systemtray/package/contents/ui/PlasmoidPopupsContainer.qml --- a/applets/systemtray/package/contents/ui/PlasmoidPopupsContainer.qml +++ b/applets/systemtray/package/contents/ui/PlasmoidPopupsContainer.qml @@ -27,7 +27,6 @@ id: mainStack clip: true focus: true - Keys.forwardTo: [currentItem] Layout.minimumWidth: units.gridUnit * 12 Layout.minimumHeight: units.gridUnit * 12 diff --git a/applets/systemtray/package/contents/ui/items/StatusNotifierItem.qml b/applets/systemtray/package/contents/ui/items/StatusNotifierItem.qml --- a/applets/systemtray/package/contents/ui/items/StatusNotifierItem.qml +++ b/applets/systemtray/package/contents/ui/items/StatusNotifierItem.qml @@ -28,7 +28,7 @@ text: Title mainText: ToolTipTitle != "" ? ToolTipTitle : Title subText: ToolTipSubTitle - icon: ToolTipIcon != "" ? ToolTipIcon : plasmoid.nativeInterface.resolveIcon(IconName != "" ? IconName : Icon, IconThemePath) + icon: ToolTipIcon != "" ? ToolTipIcon : Icon ? Icon : IconName textFormat: Text.AutoText category: Category @@ -48,7 +48,7 @@ PlasmaCore.IconItem { id: iconItem - source: plasmoid.nativeInterface.resolveIcon(IconName != "" ? IconName : Icon, IconThemePath) + source: Icon ? Icon : IconName width: Math.min(parent.width, parent.height) height: width active: taskIcon.containsMouse diff --git a/applets/systemtray/package/contents/ui/main.qml b/applets/systemtray/package/contents/ui/main.qml --- a/applets/systemtray/package/contents/ui/main.qml +++ b/applets/systemtray/package/contents/ui/main.qml @@ -21,6 +21,8 @@ import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.plasmoid 2.0 +import org.kde.draganddrop 2.0 as DnD + import "items" MouseArea { @@ -230,6 +232,46 @@ location: plasmoid.location } + DnD.DropArea { + anchors.fill: parent + + preventStealing: true; + + /** Extracts the name of the system tray applet in the drag data if present + * otherwise returns null*/ + function systemTrayAppletName(event) { + if (event.mimeData.formats.indexOf("text/x-plasmoidservicename") < 0) { + return null; + } + var plasmoidId = event.mimeData.getDataAsByteArray("text/x-plasmoidservicename"); + + if (!plasmoid.nativeInterface.isSystemTrayApplet(plasmoidId)) { + return null; + } + return plasmoidId; + } + + onDragEnter: { + if (!systemTrayAppletName(event)) { + event.ignore(); + } + } + + onDrop: { + var plasmoidId = systemTrayAppletName(event); + if (!plasmoidId) { + event.ignore(); + return; + } + + if (plasmoid.configuration.extraItems.indexOf(plasmoidId) < 0) { + var extraItems = plasmoid.configuration.extraItems; + extraItems.push(plasmoidId); + plasmoid.configuration.extraItems = extraItems; + } + } + } + //Main Layout Flow { id: tasksRow @@ -286,6 +328,7 @@ flags: Qt.WindowStaysOnTopHint location: plasmoid.location hideOnWindowDeactivate: expandedRepresentation.hideOnWindowDeactivate + onVisibleChanged: { if (!visible) { plasmoid.status = PlasmaCore.Types.PassiveStatus; @@ -298,6 +341,11 @@ } mainItem: ExpandedRepresentation { id: expandedRepresentation + + Keys.onEscapePressed: { + root.expanded = false; + } + activeApplet: root.activeApplet LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft diff --git a/applets/systemtray/systemtray.h b/applets/systemtray/systemtray.h --- a/applets/systemtray/systemtray.h +++ b/applets/systemtray/systemtray.h @@ -60,12 +60,6 @@ //Invokable utilities /** - * returns either a simple icon name or a custom path if the app is - * using a custom theme - */ - Q_INVOKABLE QVariant resolveIcon(const QVariant &variant, const QString &iconThemePath); - - /** * Given an AppletInterface pointer, shows a proper context menu for it */ Q_INVOKABLE void showPlasmoidMenu(QQuickItem *appletInterface, int x, int y); @@ -103,6 +97,8 @@ */ Q_INVOKABLE void reorderItemAfter(QQuickItem* after, QQuickItem* before); + Q_INVOKABLE bool isSystemTrayApplet(const QString &appletId); + private Q_SLOTS: void serviceNameFetchFinished(QDBusPendingCallWatcher* watcher, const QDBusConnection &connection); void serviceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner); diff --git a/applets/systemtray/systemtray.cpp b/applets/systemtray/systemtray.cpp --- a/applets/systemtray/systemtray.cpp +++ b/applets/systemtray/systemtray.cpp @@ -37,37 +37,11 @@ #include #include -#include -#include #include #include #include -/* - * An app may also load icons from their own directories, so we need a new iconloader that takes this into account - * This is wrapped into a subclass of iconengine so the iconloader lifespan matches the icon object - */ -class AppIconEngine : public KIconEngine -{ -public: - AppIconEngine(const QString &variant, const QString &path, const QString &appName); - ~AppIconEngine(); -private: - KIconLoader* m_loader; -}; - -AppIconEngine::AppIconEngine(const QString &variant, const QString &path, const QString &appName) : - KIconEngine(variant, m_loader = new KIconLoader(appName, QStringList())) -{ - m_loader->addAppDir(appName, path); -} - -AppIconEngine::~AppIconEngine() -{ - delete m_loader; -} - class PlasmoidModel: public QStandardItemModel { public: @@ -99,32 +73,32 @@ { Containment::init(); - for (const auto &info: Plasma::PluginLoader::self()->listAppletInfo(QString())) { - if (!info.isValid() || info.property(QStringLiteral("X-Plasma-NotificationArea")) != "true") { + for (const auto &info: Plasma::PluginLoader::self()->listAppletMetaData(QString())) { + if (!info.isValid() || info.value(QStringLiteral("X-Plasma-NotificationArea")) != "true") { continue; } - m_systrayApplets[info.pluginName()] = info; + m_systrayApplets[info.pluginId()] = KPluginInfo(info); - if (info.isPluginEnabledByDefault()) { - m_defaultPlasmoids += info.pluginName(); + if (info.isEnabledByDefault()) { + m_defaultPlasmoids += info.pluginId(); } - const QString dbusactivation = info.property(QStringLiteral("X-Plasma-DBusActivationService")).toString(); + const QString dbusactivation = info.value(QStringLiteral("X-Plasma-DBusActivationService")); if (!dbusactivation.isEmpty()) { - qCDebug(SYSTEM_TRAY) << "ST Found DBus-able Applet: " << info.pluginName() << dbusactivation; - m_dbusActivatableTasks[info.pluginName()] = dbusactivation; + qCDebug(SYSTEM_TRAY) << "ST Found DBus-able Applet: " << info.pluginId() << dbusactivation; + m_dbusActivatableTasks[info.pluginId()] = dbusactivation; } } } void SystemTray::newTask(const QString &task) { foreach (Plasma::Applet *applet, applets()) { - if (!applet->pluginInfo().isValid()) { + if (!applet->pluginMetaData().isValid()) { continue; } //only allow one instance per applet - if (task == applet->pluginInfo().pluginName()) { + if (task == applet->pluginMetaData().pluginId()) { //Applet::destroy doesn't delete the applet from Containment::applets in the same event //potentially a dbus activated service being restarted can be added in this time. if (!applet->destroyed()) { @@ -156,7 +130,7 @@ void SystemTray::cleanupTask(const QString &task) { foreach (Plasma::Applet *applet, applets()) { - if (!applet->pluginInfo().isValid() || task == applet->pluginInfo().pluginName()) { + if (!applet->pluginMetaData().isValid() || task == applet->pluginMetaData().pluginId()) { //we are *not* cleaning the config here, because since is one //of those automatically loaded/unloaded by dbus, we want to recycle //the config the next time it's loaded, in case the user configured something here @@ -169,32 +143,6 @@ } } -QVariant SystemTray::resolveIcon(const QVariant &variant, const QString &iconThemePath) -{ - if (variant.canConvert()) { - if (!iconThemePath.isEmpty()) { - const QString path = iconThemePath; - if (!path.isEmpty()) { - // FIXME: If last part of path is not "icons", this won't work! - auto tokens = path.splitRef('/', QString::SkipEmptyParts); - if (tokens.length() >= 3 && tokens.takeLast() == QLatin1String("icons")) { - const QString appName = tokens.takeLast().toString(); - - return QVariant(QIcon(new AppIconEngine(variant.toString(), path, appName))); - } else { - qCWarning(SYSTEM_TRAY) << "Wrong IconThemePath" << path << ": too short or does not end with 'icons'"; - } - } - - //return just the string hoping that IconItem will know how to interpret it anyways as either a normal icon or a SVG from the theme - return variant; - } - } - - // Most importantly QIcons. Nothing to do for those. - return variant; -} - void SystemTray::showPlasmoidMenu(QQuickItem *appletInterface, int x, int y) { if (!appletInterface) { @@ -255,11 +203,11 @@ } Plasma::Applet *applet = appletInterface->property("_plasma_applet").value(); - if (!applet || !applet->pluginInfo().isValid()) { + if (!applet || !applet->pluginMetaData().isValid()) { return "UnknownCategory"; } - const QString cat = applet->pluginInfo().property(QStringLiteral("X-Plasma-NotificationAreaCategory")).toString(); + const QString cat = applet->pluginMetaData().value(QStringLiteral("X-Plasma-NotificationAreaCategory")); if (cat.isEmpty()) { return "UnknownCategory"; @@ -368,6 +316,11 @@ after->setVisible(true); } +bool SystemTray::isSystemTrayApplet(const QString &appletId) +{ + return m_systrayApplets.contains(appletId); +} + void SystemTray::restoreContents(KConfigGroup &group) { Q_UNUSED(group); @@ -385,11 +338,11 @@ foreach (Plasma::Applet *applet, applets()) { //Here it should always be valid. //for some reason it not always is. - if (!applet->pluginInfo().isValid()) { + if (!applet->pluginMetaData().isValid()) { applet->config().parent().deleteGroup(); applet->deleteLater(); } else { - const QString task = applet->pluginInfo().pluginName(); + const QString task = applet->pluginMetaData().pluginId(); if (!m_allowedPlasmoids.contains(task)) { //in those cases we do delete the applet config completely //as they were explicitly disabled by the user diff --git a/appmenu/appmenu.desktop b/appmenu/appmenu.desktop --- a/appmenu/appmenu.desktop +++ b/appmenu/appmenu.desktop @@ -2,6 +2,7 @@ Type=Service Name=Application menus daemon Name[ar]=عفريت قوائم التطبيقات +Name[ast]=Degorriu de menús d'aplicaciones Name[bs]=Demon za aplikacijske menije Name[ca]=Dimoni de menús d'aplicació Name[ca@valencia]=Dimoni de menús d'aplicació diff --git a/components/shellprivate/widgetexplorer/widgetexplorer.h b/components/shellprivate/widgetexplorer/widgetexplorer.h --- a/components/shellprivate/widgetexplorer/widgetexplorer.h +++ b/components/shellprivate/widgetexplorer/widgetexplorer.h @@ -25,6 +25,7 @@ #include #include +#include #include "plasmaappletitemmodel_p.h" @@ -50,10 +51,11 @@ void separatorChanged(); }; -class WidgetExplorer : public QObject +class WidgetExplorer : public QObject, public QQmlParserStatus { Q_OBJECT + Q_INTERFACES(QQmlParserStatus) /** * Model that lists all applets @@ -66,6 +68,11 @@ Q_PROPERTY(QObject * filterModel READ filterModel CONSTANT) /** + * Whether to show special filters such as "Running" and "Uninstallable" in the filterModel. + */ + Q_PROPERTY(bool showSpecialFilters READ showSpecialFilters WRITE setShowSpecialFilters NOTIFY showSpecialFiltersChanged) + + /** * Actions for adding widgets, like download plasma widgets, download google gadgets, install from local file */ Q_PROPERTY(QList widgetsMenuActions READ widgetsMenuActions NOTIFY widgetsMenuActionsChanged) @@ -125,14 +132,20 @@ QObject *widgetsModel() const; QObject *filterModel() const; + bool showSpecialFilters() const; + void setShowSpecialFilters(bool show); + QList widgetsMenuActions(); QList extraActions() const; /** * Uninstall a plasmoid with a given plugin name. only user-installed ones are uninstallable */ Q_INVOKABLE void uninstall(const QString &pluginName); + void classBegin(); + void componentComplete(); + Q_SIGNALS: void widgetsMenuActionsChanged(); void extraActionsChanged(); @@ -150,6 +163,9 @@ void openWidgetFile(); void downloadWidgets(const QString &type); +Q_SIGNALS: + void showSpecialFiltersChanged() const; + protected Q_SLOTS: void immutabilityChanged(Plasma::Types::ImmutabilityType); diff --git a/components/shellprivate/widgetexplorer/widgetexplorer.cpp b/components/shellprivate/widgetexplorer/widgetexplorer.cpp --- a/components/shellprivate/widgetexplorer/widgetexplorer.cpp +++ b/components/shellprivate/widgetexplorer/widgetexplorer.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -104,37 +105,41 @@ PlasmaAppletItemModel itemModel; KCategorizedItemsViewModels::DefaultFilterModel filterModel; + bool showSpecialFilters = true; DefaultItemFilterProxyModel filterItemModel; QPointer newStuffDialog; QScopedPointer activitiesConsumer; }; void WidgetExplorerPrivate::initFilters() { - filterModel.addFilter(i18n("All Widgets"), - KCategorizedItemsViewModels::Filter(), QIcon::fromTheme(QStringLiteral("plasma"))); + filterModel.clear(); - // Filters: Special - filterModel.addFilter(i18n("Running"), - KCategorizedItemsViewModels::Filter(QStringLiteral("running"), true), - QIcon::fromTheme(QStringLiteral("dialog-ok"))); + if (showSpecialFilters) { + filterModel.addFilter(i18n("All Widgets"), + KCategorizedItemsViewModels::Filter(), QIcon::fromTheme(QStringLiteral("plasma"))); - filterModel.addFilter(i18n("Uninstallable"), - KCategorizedItemsViewModels::Filter(QStringLiteral("local"), true), - QIcon::fromTheme(QStringLiteral("list-remove"))); + // Filters: Special + filterModel.addFilter(i18n("Running"), + KCategorizedItemsViewModels::Filter(QStringLiteral("running"), true), + QIcon::fromTheme(QStringLiteral("dialog-ok"))); - filterModel.addSeparator(i18n("Categories:")); + filterModel.addFilter(i18n("Uninstallable"), + KCategorizedItemsViewModels::Filter(QStringLiteral("local"), true), + QIcon::fromTheme(QStringLiteral("list-remove"))); + + filterModel.addSeparator(i18n("Categories:")); + } typedef QPair catPair; QMap categories; QSet existingCategories = itemModel.categories(); //foreach (const QString &category, Plasma::Applet::listCategories(application)) { QStringList cats; - const QList list = KPackage::PackageLoader::self()->listPackages(QStringLiteral("Plasma/Applet"), QStringLiteral("plasma/plasmoids")); + const QList list = PluginLoader::self()->listAppletInfo(QString()); - for (auto& data : list) { - const KPluginInfo info(data); + for (auto& info : list) { if (!info.isValid()) { continue; } @@ -164,6 +169,15 @@ } +void WidgetExplorer::classBegin() +{ +} + +void WidgetExplorer::componentComplete() +{ + setApplication(); + d->initRunningApplets(); +} QObject *WidgetExplorer::widgetsModel() const { @@ -175,6 +189,20 @@ return &d->filterModel; } +bool WidgetExplorer::showSpecialFilters() const +{ + return d->showSpecialFilters; +} + +void WidgetExplorer::setShowSpecialFilters(bool show) +{ + if (d->showSpecialFilters != show) { + d->showSpecialFilters = show; + d->initFilters(); + emit showSpecialFiltersChanged(); + } +} + QList WidgetExplorer::widgetsMenuActions() { QList actionList; @@ -249,14 +277,14 @@ QObject::connect(containment, SIGNAL(appletRemoved(Plasma::Applet*)), q, SLOT(appletRemoved(Plasma::Applet*))); foreach (Applet *applet, containment->applets()) { - if (applet->pluginInfo().isValid()) { + if (applet->pluginMetaData().isValid()) { Containment *childContainment = applet->property("containment").value(); if (childContainment) { addContainment(childContainment); } - runningApplets[applet->pluginInfo().pluginName()]++; + runningApplets[applet->pluginMetaData().pluginId()]++; } else { - qDebug() << "Invalid plugininfo. :("; + qDebug() << "Invalid plugin metadata. :("; } } } @@ -268,10 +296,10 @@ void WidgetExplorerPrivate::appletAdded(Plasma::Applet *applet) { - if (!applet->pluginInfo().isValid()) { + if (!applet->pluginMetaData().isValid()) { return; } - QString name = applet->pluginInfo().pluginName(); + QString name = applet->pluginMetaData().pluginId(); runningApplets[name]++; appletNames.insert(applet, name); @@ -302,10 +330,6 @@ : QObject(parent), d(new WidgetExplorerPrivate(this)) { - //FIXME: delay - setApplication(); - d->initRunningApplets(); - d->filterItemModel.setSortCaseSensitivity(Qt::CaseInsensitive); d->filterItemModel.setDynamicSortFilter(true); d->filterItemModel.setSourceModel(&d->itemModel); @@ -471,9 +495,9 @@ const auto &applets = c->applets(); foreach (Applet *applet, applets) { - const auto &appletInfo = applet->pluginInfo(); + const auto &appletInfo = applet->pluginMetaData(); - if (appletInfo.isValid() && appletInfo.pluginName() == pluginName) { + if (appletInfo.isValid() && appletInfo.pluginId() == pluginName) { applet->destroy(); } } diff --git a/dataengines/statusnotifieritem/statusnotifieritemsource.cpp b/dataengines/statusnotifieritem/statusnotifieritemsource.cpp --- a/dataengines/statusnotifieritem/statusnotifieritemsource.cpp +++ b/dataengines/statusnotifieritem/statusnotifieritemsource.cpp @@ -240,14 +240,19 @@ if (!m_customIconLoader) { m_customIconLoader = new KIconLoader(QString(), QStringList(), this); } + // FIXME: If last part of path is not "icons", this won't work! + QString appName; + auto tokens = path.splitRef('/', QString::SkipEmptyParts); + if (tokens.length() >= 3 && tokens.takeLast() == QLatin1String("icons")) + appName = tokens.takeLast().toString(); //icons may be either in the root directory of the passed path or in a appdir format //i.e hicolor/32x32/iconname.png - m_customIconLoader->reconfigure(QString(), QStringList(path)); + m_customIconLoader->reconfigure(appName, QStringList(path)); //add app dir requires an app name, though this is completely unused in this context - m_customIconLoader->addAppDir(QStringLiteral("unused"), path); + m_customIconLoader->addAppDir(appName.size() ? appName : QStringLiteral("unused"), path); } setData(QStringLiteral("IconThemePath"), path); diff --git a/dataengines/systemmonitor/systemmonitor.h b/dataengines/systemmonitor/systemmonitor.h --- a/dataengines/systemmonitor/systemmonitor.h +++ b/dataengines/systemmonitor/systemmonitor.h @@ -24,6 +24,7 @@ #include #include +#include class QTimer; @@ -52,7 +53,7 @@ void updateMonitorsList(); private: - QStringList m_sensors; + QVector m_sensors; QTimer* m_timer; int m_waitingFor; }; diff --git a/dataengines/systemmonitor/systemmonitor.cpp b/dataengines/systemmonitor/systemmonitor.cpp --- a/dataengines/systemmonitor/systemmonitor.cpp +++ b/dataengines/systemmonitor/systemmonitor.cpp @@ -50,7 +50,7 @@ QStringList SystemMonitorEngine::sources() const { - return m_sensors; + return m_sensors.toList(); } bool SystemMonitorEngine::sourceRequestEvent(const QString &name) @@ -118,10 +118,10 @@ return; } - const QString sensorName = newSensorInfo[0]; - const QString min = newSensorInfo[1]; - const QString max = newSensorInfo[2]; - const QString unit = newSensorInfo[3]; + const QString& sensorName = newSensorInfo[0]; + const QString& min = newSensorInfo[1]; + const QString& max = newSensorInfo[2]; + const QString& unit = newSensorInfo[3]; if (it != sources.constEnd()) { it.value()->setData(QStringLiteral("name"), sensorName); @@ -139,14 +139,15 @@ int count = 0; foreach (const QByteArray &sens, answer) { - const QStringList newSensorInfo = QString::fromUtf8(sens).split('\t'); + const QString sensStr{QString::fromUtf8(sens)}; + const QVector newSensorInfo = sensStr.splitRef('\t'); if (newSensorInfo.count() < 2) { continue; } if(newSensorInfo.at(1) == QLatin1String("logfile")) continue; // logfile data type not currently supported - const QString newSensor = newSensorInfo[0]; + const QString newSensor = newSensorInfo[0].toString(); sensors.insert(newSensor); m_sensors.append(newSensor); { @@ -160,7 +161,7 @@ } DataEngine::Data d; d.insert(QStringLiteral("value"), QVariant()); - d.insert(QStringLiteral("type"), newSensorInfo[1]); + d.insert(QStringLiteral("type"), newSensorInfo[1].toString()); setData(newSensor, d); KSGRD::SensorMgr->sendRequest( QStringLiteral("localhost"), QStringLiteral("%1?").arg(newSensor), (KSGRD::SensorClient*)this, -(count + 2)); ++count; diff --git a/doc/config_update_tool/extract_config.py b/doc/config_update_tool/extract_config.py --- a/doc/config_update_tool/extract_config.py +++ b/doc/config_update_tool/extract_config.py @@ -24,7 +24,9 @@ root = xdg_data_dir + "/plasma/plasmoids" -for plasmoid in os.listdir(root): +plasmoids = os.listdir(root) +plasmoids.sort() +for plasmoid in plasmoids: configPath = "/contents/config/main.xml" path = root + "/" + plasmoid + configPath try: diff --git a/doc/kcontrol/screenlocker/index.docbook b/doc/kcontrol/screenlocker/index.docbook --- a/doc/kcontrol/screenlocker/index.docbook +++ b/doc/kcontrol/screenlocker/index.docbook @@ -12,9 +12,9 @@ &Mike.McBride; &Mike.McBride.mail; - -2015-05-20 -Plasma 5.3 + +2016-09-19 +Plasma 5.8 KDE @@ -33,7 +33,7 @@ You can enter any positive number of minutes in this box. Below that is a spinbox labeled Require password -after locking. If it is checked, when you click a key +after locking. When you press a key or click a mouse button to end the screen locker after the time in the spinbox and return to your work, you must enter a password. The password used is the same password you used to login to your machine. @@ -46,8 +46,11 @@ Check Lock screen on resume if you want a password protected system when waking up from suspension. -If you would like to change the background in locked status click on the image button right -to Custom Background to open a menu with actions to load an image from -file or clear the currently used image. +If you would like to change the background in locked status switch to the Wallpaper tab. + +You can select a Plain Color as wallpaper type. + +Alternatively use a single image or a slideshow with images from a folder +or load new wallpapers from the Internet. diff --git a/drkonqi/bugzillaintegration/reportassistantpages_base.cpp b/drkonqi/bugzillaintegration/reportassistantpages_base.cpp --- a/drkonqi/bugzillaintegration/reportassistantpages_base.cpp +++ b/drkonqi/bugzillaintegration/reportassistantpages_base.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -237,10 +238,11 @@ subject= subject.arg(crashedApp->datetime().toString(QStringLiteral("yyyy-MM-dd"))); KToolInvocation::invokeMailer(reportAddress, QLatin1String(""), QLatin1String("") , subject, report); } else { + QUrl url(reportAddress); if (QUrl(reportAddress).isRelative()) { //Scheme is missing - reportAddress = QString::fromLatin1("http://%1").arg(reportAddress); + url = QUrl(QString::fromLatin1("http://%1").arg(reportAddress)); } - KToolInvocation::invokeBrowser(reportAddress); + QDesktopServices::openUrl(url); } //Show a copy of the bug reported diff --git a/drkonqi/bugzillaintegration/reportassistantpages_bugzilla.cpp b/drkonqi/bugzillaintegration/reportassistantpages_bugzilla.cpp --- a/drkonqi/bugzillaintegration/reportassistantpages_bugzilla.cpp +++ b/drkonqi/bugzillaintegration/reportassistantpages_bugzilla.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -759,7 +760,7 @@ void BugzillaSendPage::finishClicked() { if (ui.m_launchPageOnFinish->isChecked() && !reportUrl.isEmpty()) { - KToolInvocation::invokeBrowser(reportUrl); + QDesktopServices::openUrl(QUrl(reportUrl)); } if (ui.m_restartAppOnFinish->isChecked()) { DrKonqi::crashedApplication()->restart(); diff --git a/freespacenotifier/freespacenotifier.desktop b/freespacenotifier/freespacenotifier.desktop --- a/freespacenotifier/freespacenotifier.desktop +++ b/freespacenotifier/freespacenotifier.desktop @@ -70,6 +70,7 @@ Name[zh_TW]=剩餘空間通知 Comment=Warns when running out of space on your home folder Comment[ar]=يحذّرك عندما تنفذ مساحة مجلد المنزل +Comment[ast]=Alvierte cuando tea escosándose l'espaciu de la to carpeta Home Comment[bg]=Предупреждение при твърде малко пространство в домашната директория Comment[bn]=ব্যক্তিগত ফোল্ডারে জায়গা কমে গেলে সতর্ক করে Comment[bs]=Upozorava kada vam ponestaje prostora u ličnom direktoriju diff --git a/kioslave/remote/CMakeLists.txt b/kioslave/remote/CMakeLists.txt --- a/kioslave/remote/CMakeLists.txt +++ b/kioslave/remote/CMakeLists.txt @@ -1,7 +1,6 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kio_remote\") add_subdirectory( kdedmodule ) -add_subdirectory( tests ) set(kio_remote_SRCS kio_remote.cpp @@ -14,7 +13,7 @@ DEFAULT_SEVERITY Info) add_library(kio_remote MODULE ${kio_remote_SRCS}) -target_link_libraries(kio_remote KF5::KIOCore KF5::KDELibs4Support) +target_link_libraries(kio_remote KF5::KIOCore KF5::I18n) install(TARGETS kio_remote DESTINATION ${KDE_INSTALL_PLUGINDIR} ) install( FILES remote.protocol DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) diff --git a/kioslave/remote/kdedmodule/CMakeLists.txt b/kioslave/remote/kdedmodule/CMakeLists.txt --- a/kioslave/remote/kdedmodule/CMakeLists.txt +++ b/kioslave/remote/kdedmodule/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(remotedirnotify MODULE remotedirnotify.cpp remotedirnotifymodule.cpp ../debug.cpp) kcoreaddons_desktop_to_json(remotedirnotify remotedirnotify.desktop) -target_link_libraries(remotedirnotify KF5::DBusAddons KF5::KIOCore KF5::KDELibs4Support) +target_link_libraries(remotedirnotify KF5::DBusAddons KF5::KIOCore) install(TARGETS remotedirnotify DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf5/kded ) diff --git a/kioslave/remote/kdedmodule/remotedirnotify.h b/kioslave/remote/kdedmodule/remotedirnotify.h --- a/kioslave/remote/kdedmodule/remotedirnotify.h +++ b/kioslave/remote/kdedmodule/remotedirnotify.h @@ -19,7 +19,7 @@ #ifndef REMOTEDIRNOTIFY_H #define REMOTEDIRNOTIFY_H -#include +#include #include class RemoteDirNotify : public QObject @@ -35,9 +35,9 @@ void FilesChanged (const QStringList &fileList); private: - KUrl toRemoteURL(const KUrl &url); - KUrl::List toRemoteURLList(const KUrl::List &list); - KUrl m_baseURL; + QUrl toRemoteURL(const QUrl &url); + QList toRemoteURLList(const QStringList &list); + QUrl m_baseURL; }; #endif diff --git a/kioslave/remote/kdedmodule/remotedirnotify.cpp b/kioslave/remote/kdedmodule/remotedirnotify.cpp --- a/kioslave/remote/kdedmodule/remotedirnotify.cpp +++ b/kioslave/remote/kdedmodule/remotedirnotify.cpp @@ -19,19 +19,15 @@ #include "remotedirnotify.h" #include "../debug.h" -#include -#include -#include #include #include +#include #include RemoteDirNotify::RemoteDirNotify() { - KGlobal::dirs()->addResourceType("remote_entries", "data", "remoteview"); - - const QString path = KGlobal::dirs()->saveLocation("remote_entries"); + const QString path = QStringLiteral("%1/remoteview").arg(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)); m_baseURL.setPath(path); QDBusConnection::sessionBus().connect(QString(), QString(), QStringLiteral("org.kde.KDirNotify"), @@ -42,48 +38,42 @@ QStringLiteral("FilesChanged"), this, SLOT(FilesChanged(QStringList))); } -KUrl RemoteDirNotify::toRemoteURL(const KUrl &url) +QUrl RemoteDirNotify::toRemoteURL(const QUrl &url) { qCDebug(KIOREMOTE_LOG) << "RemoteDirNotify::toRemoteURL(" << url << ")"; if ( m_baseURL.isParentOf(url) ) { - QString path = KUrl::relativePath(m_baseURL.path(), - url.path()); - KUrl result("remote:/"+path); - result.cleanPath(); + QString path = QDir(m_baseURL.path()).relativeFilePath(url.path()); + QUrl result; + result.setScheme(QStringLiteral("remote")); + result.setPath(path); + result.setPath(QDir::cleanPath(result.path())); qCDebug(KIOREMOTE_LOG) << "result => " << result; return result; } - qCDebug(KIOREMOTE_LOG) << "result => KUrl()"; - return KUrl(); + qCDebug(KIOREMOTE_LOG) << "result => QUrl()"; + return QUrl(); } -KUrl::List RemoteDirNotify::toRemoteURLList(const KUrl::List &list) +QList RemoteDirNotify::toRemoteURLList(const QStringList &list) { - KUrl::List new_list; - - KUrl::List::const_iterator it = list.begin(); - KUrl::List::const_iterator end = list.end(); - - for (; it!=end; ++it) - { - KUrl url = toRemoteURL(*it); - - if (url.isValid()) - { - new_list.append(url); - } - } - - return new_list; + QList urls; + for (const QString &file : list) { + QUrl url = toRemoteURL(QUrl::fromLocalFile(file)); + if (url.isValid()) { + urls.append(url); + } + } + + return urls; } void RemoteDirNotify::FilesAdded(const QString &directory) { qCDebug(KIOREMOTE_LOG) << "RemoteDirNotify::FilesAdded"; - QUrl new_dir = toRemoteURL(directory); + QUrl new_dir = toRemoteURL(QUrl::fromLocalFile(directory)); if (new_dir.isValid()) { @@ -96,16 +86,16 @@ // have a file:/ based UDS_URL so that they are executed correctly. // Hence, FilesRemoved and FilesChanged does nothing... We're forced to use // FilesAdded to re-list the modified directory. -inline void evil_hack(const KUrl::List &list) +inline void evil_hack(const QList &list) { - KUrl::List notified; + QList notified; - KUrl::List::const_iterator it = list.begin(); - KUrl::List::const_iterator end = list.end(); + QList::const_iterator it = list.begin(); + QList::const_iterator end = list.end(); for (; it!=end; ++it) { - QUrl url = (*it).upUrl(); + QUrl url = KIO::upUrl(*it); if (!notified.contains(url)) { @@ -120,7 +110,7 @@ { qCDebug(KIOREMOTE_LOG) << "RemoteDirNotify::FilesRemoved"; - KUrl::List new_list = toRemoteURLList(fileList); + QList new_list = toRemoteURLList(fileList); if (!new_list.isEmpty()) { @@ -134,7 +124,7 @@ { qCDebug(KIOREMOTE_LOG) << "RemoteDirNotify::FilesChanged"; - KUrl::List new_list = toRemoteURLList(fileList); + QList new_list = toRemoteURLList(fileList); if (!new_list.isEmpty()) { diff --git a/kioslave/remote/kdedmodule/remotedirnotifymodule.cpp b/kioslave/remote/kdedmodule/remotedirnotifymodule.cpp --- a/kioslave/remote/kdedmodule/remotedirnotifymodule.cpp +++ b/kioslave/remote/kdedmodule/remotedirnotifymodule.cpp @@ -18,10 +18,6 @@ #include "remotedirnotifymodule.h" -#include -#include -#include - #include //#include diff --git a/kioslave/remote/kio_remote.cpp b/kioslave/remote/kio_remote.cpp --- a/kioslave/remote/kio_remote.cpp +++ b/kioslave/remote/kio_remote.cpp @@ -61,14 +61,15 @@ int second_slash_idx = url.path().indexOf( '/', 1 ); const QString root_dirname = url.path().mid( 1, second_slash_idx-1 ); - KUrl target = m_impl.findBaseURL( root_dirname ); + QUrl target = m_impl.findBaseURL( root_dirname ); qCDebug(KIOREMOTE_LOG) << "possible redirection target : " << target; if( target.isValid() ) { if ( second_slash_idx < 0 ) { second_slash_idx = url.path().size(); } - target.addPath( url.path().remove(0, second_slash_idx) ); + target = target.adjusted(QUrl::StripTrailingSlash); + target.setPath(target.path() + '/' + ( url.path().remove(0, second_slash_idx) )); qCDebug(KIOREMOTE_LOG) << "complete redirection target : " << target; redirection(target); finished(); @@ -149,15 +150,16 @@ } else { - KUrl target = m_impl.findBaseURL( root_dirname ); + QUrl target = m_impl.findBaseURL( root_dirname ); qCDebug(KIOREMOTE_LOG) << "possible redirection target : " << target; if ( target.isValid() ) { if ( second_slash_idx < 0 ) { second_slash_idx = url.path().size(); } qCDebug(KIOREMOTE_LOG) << "complete redirection target : " << target; - target.addPath( url.path().remove( 0, second_slash_idx ) ); + target = target.adjusted(QUrl::StripTrailingSlash); + target.setPath(target.path() + '/' + ( url.path().remove( 0, second_slash_idx ) )); redirection( target ); finished(); return; @@ -190,7 +192,7 @@ if (!file.isEmpty()) { - KUrl desktop; + QUrl desktop; desktop.setPath(file); redirection(desktop); diff --git a/kioslave/remote/remoteimpl.h b/kioslave/remote/remoteimpl.h --- a/kioslave/remote/remoteimpl.h +++ b/kioslave/remote/remoteimpl.h @@ -22,7 +22,7 @@ #include #include -#include +#include @@ -33,12 +33,12 @@ void createTopLevelEntry(KIO::UDSEntry &entry) const; bool createWizardEntry(KIO::UDSEntry &entry) const; - bool isWizardURL(const KUrl &url) const; + bool isWizardURL(const QUrl &url) const; bool statNetworkFolder(KIO::UDSEntry &entry, const QString &filename) const; void listRoot(KIO::UDSEntryList& list) const; - KUrl findBaseURL(const QString &filename) const; + QUrl findBaseURL(const QString &filename) const; QString findDesktopFile(const QString &filename) const; bool deleteNetworkFolder(const QString &filename) const; diff --git a/kioslave/remote/remoteimpl.cpp b/kioslave/remote/remoteimpl.cpp --- a/kioslave/remote/remoteimpl.cpp +++ b/kioslave/remote/remoteimpl.cpp @@ -20,13 +20,10 @@ #include "remoteimpl.h" #include "debug.h" -#include -#include #include #include -#include -#include #include +#include #include #include @@ -38,9 +35,7 @@ RemoteImpl::RemoteImpl() { - KGlobal::dirs()->addResourceType("remote_entries", "data", "remoteview"); - - const QString path = KGlobal::dirs()->saveLocation("remote_entries"); + const QString path = QStringLiteral("%1/remoteview").arg(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)); QDir dir = path; if (!dir.exists()) @@ -55,7 +50,7 @@ qCDebug(KIOREMOTE_LOG) << "RemoteImpl::listRoot"; QStringList names_found; - const QStringList dirList = KGlobal::dirs()->resourceDirs("remote_entries"); + const QStringList dirList = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("remoteview"), QStandardPaths::LocateDirectory); QStringList::ConstIterator dirpath = dirList.constBegin(); const QStringList::ConstIterator end = dirList.constEnd(); @@ -90,7 +85,7 @@ { qCDebug(KIOREMOTE_LOG) << "RemoteImpl::findDirectory"; - const QStringList dirList = KGlobal::dirs()->resourceDirs("remote_entries"); + const QStringList dirList = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("remoteview"), QStandardPaths::LocateDirectory); QStringList::ConstIterator dirpath = dirList.constBegin(); const QStringList::ConstIterator end = dirList.constEnd(); @@ -134,18 +129,18 @@ return QString(); } -KUrl RemoteImpl::findBaseURL(const QString &filename) const +QUrl RemoteImpl::findBaseURL(const QString &filename) const { qCDebug(KIOREMOTE_LOG) << "RemoteImpl::findBaseURL"; const QString file = findDesktopFile(filename); if (!file.isEmpty()) { KDesktopFile desktop( file ); - return desktop.readUrl(); + return QUrl::fromLocalFile(desktop.readUrl()); } - return KUrl(); + return QUrl(); } @@ -161,16 +156,15 @@ entry.insert( KIO::UDSEntry::UDS_GROUP, QString::fromLatin1("root")); } -static KUrl findWizardRealURL() +static QUrl findWizardRealURL() { - KUrl url; + QUrl url; KService::Ptr service = KService::serviceByDesktopName(WIZARD_SERVICE); if (service && service->isValid()) { - url.setPath( KStandardDirs::locate("apps", - service->entryPath()) - ); + url.setPath(QStandardPaths::locate(QStandardPaths::ApplicationsLocation, + QStringLiteral("%1.desktop").arg(WIZARD_SERVICE))); } return url; @@ -180,7 +174,7 @@ { entry.clear(); - KUrl url = findWizardRealURL(); + QUrl url = findWizardRealURL(); if (!url.isValid()) { @@ -198,9 +192,9 @@ return true; } -bool RemoteImpl::isWizardURL(const KUrl &url) const +bool RemoteImpl::isWizardURL(const QUrl &url) const { - return url==KUrl(WIZARD_URL); + return url==QUrl(WIZARD_URL); } @@ -210,7 +204,11 @@ { qCDebug(KIOREMOTE_LOG) << "RemoteImpl::createEntry"; - KDesktopFile desktop(directory+file); + QString dir = directory; + if (!dir.endsWith(QLatin1Char('/'))) { + dir += QLatin1Char('/'); + } + KDesktopFile desktop(dir + file); qCDebug(KIOREMOTE_LOG) << "path = " << directory << file; diff --git a/kioslave/remote/tests/CMakeLists.txt b/kioslave/remote/tests/CMakeLists.txt deleted file mode 100644 --- a/kioslave/remote/tests/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) - -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) -########### next target ############### -set(testremote_SRCS testremote.cpp ${kio_remote_SRCS} ) - -add_executable(testremote ${testremote_SRCS}) -ecm_mark_as_test(testremote) - -target_link_libraries(testremote KF5::KDELibs4Support KF5::KIOCore ) - - - - - diff --git a/kioslave/remote/tests/testremote.h b/kioslave/remote/tests/testremote.h deleted file mode 100644 --- a/kioslave/remote/tests/testremote.h +++ /dev/null @@ -1,34 +0,0 @@ -/* This file is part of the KDE project - Copyright (c) 2004 Kévin Ottens - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#ifndef TESTREMOTE_H -#define TESTREMOTE_H - -class TestRemote -{ -public: - TestRemote() {} - void setup(); - void runAll(); - - // tests - -}; - -#endif diff --git a/kioslave/remote/tests/testremote.cpp b/kioslave/remote/tests/testremote.cpp deleted file mode 100644 --- a/kioslave/remote/tests/testremote.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* This file is part of the KDE project - Copyright (c) 2004 Kévin Ottens - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#include "kio_remote.h" -#include "testremote.h" - -#include -#include -#include - -#include - -int main(int argc, char *argv[]) -{ - //KApplication::disableAutoDcopRegistration(); - KCmdLineArgs::init(argc,argv,"testremote", 0, KLocalizedString(), 0); - KApplication app; - - TestRemote test; - test.setup(); - test.runAll(); - kDebug() << "All tests OK."; - return 0; // success. The exit(1) in check() is what happens in case of failure. -} - -void TestRemote::setup() -{ - -} - -void TestRemote::runAll() -{ - -} - diff --git a/klipper/clipcommandprocess.h b/klipper/clipcommandprocess.h --- a/klipper/clipcommandprocess.h +++ b/klipper/clipcommandprocess.h @@ -25,7 +25,7 @@ class ClipAction; class History; -class ClipCommand; +struct ClipCommand; class HistoryItem; class ClipCommandProcess : public KProcess diff --git a/krunner/CMakeLists.txt b/krunner/CMakeLists.txt --- a/krunner/CMakeLists.txt +++ b/krunner/CMakeLists.txt @@ -37,3 +37,5 @@ INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/KRunnerAppDBusInterfaceConfig.cmake DESTINATION ${CMAKECONFIG_INSTALL_DIR}) + +add_subdirectory(update) \ No newline at end of file diff --git a/krunner/update/CMakeLists.txt b/krunner/update/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/krunner/update/CMakeLists.txt @@ -0,0 +1,12 @@ +set(krunnerplugins_SRCS + main.cpp + ) + +add_executable(krunnerplugins ${krunnerplugins_SRCS}) + +target_link_libraries(krunnerplugins KF5::CoreAddons KF5::Service KF5::Runner KF5::ConfigCore) + +install(TARGETS krunnerplugins DESTINATION ${LIB_INSTALL_DIR}/kconf_update_bin/) +install(FILES krunnerplugins.upd DESTINATION ${KCONF_UPDATE_INSTALL_DIR}) + + diff --git a/krunner/update/krunnerplugins.upd b/krunner/update/krunnerplugins.upd new file mode 100644 --- /dev/null +++ b/krunner/update/krunnerplugins.upd @@ -0,0 +1,3 @@ +Version=5 +Id=5.9KRunnerPlugins +Script=krunnerplugins diff --git a/krunner/update/main.cpp b/krunner/update/main.cpp new file mode 100644 --- /dev/null +++ b/krunner/update/main.cpp @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2016 by Marco Martin * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +void migrateEnabledPlugins() +{ + KSharedConfig::Ptr config = KSharedConfig::openConfig("krunnerrc"); + KConfigGroup runnerManagerGroup(config, "PlasmaRunnerManager"); + KConfigGroup pluginsGroup(config, "Plugins"); + + QStringList enabledCategories = runnerManagerGroup.readEntry("enabledCategories", QStringList()); + + if (enabledCategories.isEmpty()) { + return; + } + + Plasma::RunnerManager *manager = new Plasma::RunnerManager(); + manager->reloadConfiguration(); + for (Plasma::AbstractRunner *runner : manager->runners()) { + pluginsGroup.writeEntry(runner->metadata().pluginName() + QStringLiteral("Enabled"), false); + for (auto category : runner->categories()) { + if (enabledCategories.contains(category)) { + pluginsGroup.writeEntry(runner->metadata().pluginName() + QStringLiteral("Enabled"), true); + break; + } + } + } + + runnerManagerGroup.deleteEntry("enabledCategories"); +} + +int main(int argc, char **argv) +{ + + QCoreApplication app(argc, argv); + + migrateEnabledPlugins(); + + return 0; +} diff --git a/krunner/view.cpp b/krunner/view.cpp --- a/krunner/view.cpp +++ b/krunner/view.cpp @@ -239,6 +239,9 @@ return false; } m_plasmaShellSurface = m_plasmaShell->createSurface(s, this); + m_plasmaShellSurface->setRole(PlasmaShellSurface::Role::Panel); + m_plasmaShellSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::WindowsGoBelow); + m_plasmaShellSurface->setPanelTakesFocus(true); break; } case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed: diff --git a/ksmserver/client.cpp b/ksmserver/client.cpp --- a/ksmserver/client.cpp +++ b/ksmserver/client.cpp @@ -89,7 +89,7 @@ char* ret = NULL; if (!ret) { if (my_addr->isEmpty()) { -// qWarning("Can't get own host name. Your system is severely misconfigured\n"); +// qCWarning(KSMSERVER, "Can't get own host name. Your system is severely misconfigured\n"); /* Faking our IP address, the 0 below is "unknown" address format (1 would be IP, 2 would be DEC-NET format) */ diff --git a/ksmserver/logout-greeter/CMakeLists.txt b/ksmserver/logout-greeter/CMakeLists.txt --- a/ksmserver/logout-greeter/CMakeLists.txt +++ b/ksmserver/logout-greeter/CMakeLists.txt @@ -1,4 +1,4 @@ -set(KSMSERVER_LOGOUT_GREETER_SRCS main.cpp ../shutdowndlg.cpp) +set(KSMSERVER_LOGOUT_GREETER_SRCS main.cpp ../shutdowndlg.cpp ../ksmserver_debug.cpp) add_executable(ksmserver-logout-greeter ${KSMSERVER_LOGOUT_GREETER_SRCS}) target_link_libraries(ksmserver-logout-greeter PW::KWorkspace diff --git a/ksmserver/main.cpp b/ksmserver/main.cpp --- a/ksmserver/main.cpp +++ b/ksmserver/main.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include "server.h" #include @@ -89,7 +90,7 @@ dpy = XOpenDisplay(0); // open default display if (!dpy) { - qCritical() << "Cannot connect to the X server"; + qCCritical(KSMSERVER) << "Cannot connect to the X server"; return; } @@ -288,7 +289,7 @@ // registerService( QStringLiteral( "org.kde.ksmserver" ), // QDBusConnectionInterface::DontQueueService ) ) // { -// qWarning("Could not register with D-BUS. Aborting."); +// qCWarning(KSMSERVER, "Could not register with D-BUS. Aborting."); // return 1; // } @@ -301,7 +302,7 @@ * does nothing on this platform, as here the default is reversed) */ if (!only_local) { - qWarning("--nolocal is not supported on your platform. Sorry."); + qCWarning(KSMSERVER, "--nolocal is not supported on your platform. Sorry."); } only_local = false; #endif diff --git a/ksmserver/server.cpp b/ksmserver/server.cpp --- a/ksmserver/server.cpp +++ b/ksmserver/server.cpp @@ -457,7 +457,7 @@ QString iceAuth = QStandardPaths::findExecutable(QStringLiteral("iceauth")); if (iceAuth.isEmpty()) { - qWarning("KSMServer: could not find iceauth"); + qCWarning(KSMSERVER, "KSMServer: could not find iceauth"); return 0; } @@ -487,7 +487,7 @@ QString iceAuth = QStandardPaths::findExecutable(QStringLiteral("iceauth")); if (iceAuth.isEmpty()) { - qWarning("KSMServer: could not find iceauth"); + qCWarning(KSMSERVER, "KSMServer: could not find iceauth"); return; } @@ -504,7 +504,7 @@ static int Xio_ErrorHandler( Display * ) { - qWarning("ksmserver: Fatal IO error: client killed"); + qCWarning(KSMSERVER, "ksmserver: Fatal IO error: client killed"); // Don't do anything that might require the X connection if (the_server) @@ -650,14 +650,14 @@ (SmPointer) this, HostBasedAuthProc, 256, errormsg ) ) { - qWarning("KSMServer: could not register XSM protocol"); + qCWarning(KSMSERVER, "KSMServer: could not register XSM protocol"); } if (!IceListenForConnections (&numTransports, &listenObjs, 256, errormsg)) { - qWarning("KSMServer: Error listening for connections: %s", errormsg); - qWarning("KSMServer: Aborting."); + qCWarning(KSMSERVER, "KSMServer: Error listening for connections: %s", errormsg); + qCWarning(KSMSERVER, "KSMServer: Aborting."); exit(1); } @@ -681,8 +681,8 @@ f = ::fopen(fName.data(), "w+"); if (!f) { - qWarning("KSMServer: cannot open %s: %s", fName.data(), strerror(errno)); - qWarning("KSMServer: Aborting."); + qCWarning(KSMSERVER, "KSMServer: cannot open %s: %s", fName.data(), strerror(errno)); + qCWarning(KSMSERVER, "KSMServer: Aborting."); exit(1); } char* session_manager = IceComposeNetworkIdList(numTransports, listenObjs); diff --git a/ksmserver/shutdown.cpp b/ksmserver/shutdown.cpp --- a/ksmserver/shutdown.cpp +++ b/ksmserver/shutdown.cpp @@ -129,7 +129,7 @@ void KSMServer::shutdown( KWorkSpace::ShutdownConfirm confirm, KWorkSpace::ShutdownType sdtype, KWorkSpace::ShutdownMode sdmode ) { - qDebug() << "Shutdown called with confirm " << confirm + qCDebug(KSMSERVER) << "Shutdown called with confirm " << confirm << " type " << sdtype << " and mode " << sdmode; pendingShutdown.stop(); if( dialogActive ) @@ -176,7 +176,7 @@ if (sdmode == KWorkSpace::ShutdownModeDefault) sdmode = KWorkSpace::ShutdownModeInteractive; - qDebug() << "After modifications confirm is " << confirm + qCDebug(KSMSERVER) << "After modifications confirm is " << confirm << " type is " << sdtype << " and mode " << sdmode; QString bopt; if ( !logoutConfirmed ) { @@ -270,7 +270,7 @@ QStringLiteral( "restorePreviousLogout" ) ) == QStringLiteral( "restorePreviousLogout" ) ); - qDebug() << "saveSession is " << saveSession; + qCDebug(KSMSERVER) << "saveSession is " << saveSession; if ( saveSession ) sessionGroup = QStringLiteral( "Session: " ) + QString::fromLocal8Bit( SESSION_PREVIOUS_LOGOUT ); @@ -317,7 +317,7 @@ SmsSaveYourself( c->connection(), saveType, true, SmInteractStyleAny, false ); } - qDebug() << "clients should be empty, " << clients.isEmpty(); + qCDebug(KSMSERVER) << "clients should be empty, " << clients.isEmpty(); if ( clients.isEmpty() ) completeShutdownOrCheckpoint(); dialogActive = false; @@ -532,7 +532,7 @@ void KSMServer::completeShutdownOrCheckpoint() { - qDebug() << "completeShutdownOrCheckpoint called"; + qCDebug(KSMSERVER) << "completeShutdownOrCheckpoint called"; if ( state != Shutdown && state != Checkpoint && state != ClosingSubSession ) return; @@ -564,7 +564,7 @@ else discardSession(); - qDebug() << "state is " << state; + qCDebug(KSMSERVER) << "state is " << state; if ( state == Shutdown ) { KNotification *n = KNotification::event(QStringLiteral("exitkde"), QString(), QPixmap(), 0l, KNotification::DefaultEvent); // Plasma says good bye connect(n, &KNotification::closed, this, &KSMServer::startKilling); @@ -605,7 +605,7 @@ foreach( KSMClient* c, clients ) { if( isWM( c )) // kill the WM as the last one in order to reduce flicker continue; - qCDebug(KSMSERVER) << "completeShutdown: client " << c->program() << "(" << c->clientId() << ")"; + qCDebug(KSMSERVER) << "startKilling: client " << c->program() << "(" << c->clientId() << ")"; SmsDie( c->connection() ); } @@ -675,15 +675,15 @@ void KSMServer::timeoutQuit() { foreach( KSMClient* c, clients ) { - qWarning() << "SmsDie timeout, client " << c->program() << "(" << c->clientId() << ")" ; + qCWarning(KSMSERVER) << "SmsDie timeout, client " << c->program() << "(" << c->clientId() << ")" ; } killWM(); } void KSMServer::timeoutWMQuit() { if( state == KillingWM ) { - qWarning() << "SmsDie WM timeout" ; + qCWarning(KSMSERVER) << "SmsDie WM timeout" ; } killingCompleted(); } diff --git a/ksmserver/shutdowndlg.cpp b/ksmserver/shutdowndlg.cpp --- a/ksmserver/shutdowndlg.cpp +++ b/ksmserver/shutdowndlg.cpp @@ -24,7 +24,7 @@ ******************************************************************/ #include "shutdowndlg.h" -//include +#include "ksmserver_debug.h" #include #include @@ -172,12 +172,12 @@ //qCDebug(KSMSERVER) << "Using QML theme" << fileName; setSource(QUrl::fromLocalFile(fileName)); } else { - qWarning() << "Couldn't find a theme for the Shutdown dialog" << fileName; + qCWarning(KSMSERVER) << "Couldn't find a theme for the Shutdown dialog" << fileName; return; } if(!errors().isEmpty()) { - qWarning() << errors(); + qCWarning(KSMSERVER) << errors(); } connect(rootObject(), SIGNAL(logoutRequested()), SLOT(slotLogout())); diff --git a/ksmserver/startup.cpp b/ksmserver/startup.cpp --- a/ksmserver/startup.cpp +++ b/ksmserver/startup.cpp @@ -110,16 +110,16 @@ QString soundFilename = notifyConfig.readEntry(QStringLiteral("Sound")); if (soundFilename.isEmpty()) { - qWarning() << "Audio notification requested, but no sound file provided in notifyrc file, aborting audio notification"; + qCWarning(KSMSERVER) << "Audio notification requested, but no sound file provided in notifyrc file, aborting audio notification"; return; } QUrl soundURL = QUrl(soundFilename); // this CTOR accepts both absolute paths (/usr/share/sounds/blabla.ogg and blabla.ogg) w/o screwing the scheme if (soundURL.isRelative() && !soundURL.toString().startsWith('/')) { // QUrl considers url.scheme.isEmpty() == url.isRelative() soundURL = QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("sounds/") + soundFilename)); if (soundURL.isEmpty()) { - qWarning() << "Audio notification requested, but sound file from notifyrc file was not found, aborting audio notification"; + qCWarning(KSMSERVER) << "Audio notification requested, but sound file from notifyrc file was not found, aborting audio notification"; return; } } @@ -222,7 +222,7 @@ } if( wmProcess->state() == QProcess::NotRunning ) { // wm failed to launch for some reason, go with kwin instead - qWarning() << "Window manager" << wm << "failed to launch"; + qCWarning(KSMSERVER) << "Window manager" << wm << "failed to launch"; if( wm == QStringLiteral( KWIN_BIN ) ) return; // uhoh, kwin itself failed qCDebug(KSMSERVER) << "Launching KWin"; @@ -265,7 +265,7 @@ QStringLiteral( "org.kde.KCMInit" ), QDBusConnection::sessionBus(), this ); if( !kcminitSignals->isValid()) { - qWarning() << "kcminit not running? If we are running with mobile profile or in another platform other than X11 this is normal."; + qCWarning(KSMSERVER) << "kcminit not running? If we are running with mobile profile or in another platform other than X11 this is normal."; delete kcminitSignals; kcminitSignals = 0; QTimer::singleShot(0, this, &KSMServer::kcmPhase1Done); @@ -611,7 +611,7 @@ autoStart2(); break; default: - qWarning() << "Unknown resume startup state" ; + qCWarning(KSMSERVER) << "Unknown resume startup state" ; break; } } diff --git a/ksmserver/switchuserdialog.cpp b/ksmserver/switchuserdialog.cpp --- a/ksmserver/switchuserdialog.cpp +++ b/ksmserver/switchuserdialog.cpp @@ -21,6 +21,7 @@ #include "switchuserdialog.h" #include +#include #include #include @@ -91,15 +92,15 @@ if (QFile::exists(fileName)) { setSource(QUrl::fromLocalFile(fileName)); } else { - qWarning() << "Couldn't find a theme for the Switch User dialog" << fileName; + qCWarning(KSMSERVER) << "Couldn't find a theme for the Switch User dialog" << fileName; return; } setPosition(screen()->virtualGeometry().center().x() - width() / 2, screen()->virtualGeometry().center().y() - height() / 2); if (!errors().isEmpty()) { - qWarning() << errors(); + qCWarning(KSMSERVER) << errors(); } connect(rootObject(), SIGNAL(dismissed()), this, SIGNAL(dismissed())); diff --git a/kuiserver/main.cpp b/kuiserver/main.cpp --- a/kuiserver/main.cpp +++ b/kuiserver/main.cpp @@ -33,8 +33,6 @@ extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv) { - QLoggingCategory::setFilterRules(QStringLiteral("kuiserver.debug = true")); - QApplication app(argc, argv); app.setApplicationName(QStringLiteral("kuiserver")); app.setApplicationVersion(QStringLiteral("2.0")); diff --git a/libtaskmanager/abstracttasksmodel.h b/libtaskmanager/abstracttasksmodel.h --- a/libtaskmanager/abstracttasksmodel.h +++ b/libtaskmanager/abstracttasksmodel.h @@ -83,6 +83,7 @@ IsDemandingAttention, /**< Task is demanding attention. */ SkipTaskbar, /**< Task should not be shown in a 'task bar' user interface. */ SkipPager, /**< Task should not to be shown in a 'pager' user interface. */ + AppPid, /**< Application Process ID */ }; Q_ENUM(AdditionalRoles) diff --git a/libtaskmanager/autotests/launchertasksmodeltest.cpp b/libtaskmanager/autotests/launchertasksmodeltest.cpp --- a/libtaskmanager/autotests/launchertasksmodeltest.cpp +++ b/libtaskmanager/autotests/launchertasksmodeltest.cpp @@ -46,6 +46,7 @@ void LauncherTasksModelTest::initTestCase() { + qApp->setProperty("org.kde.KActivities.core.disableAutostart", true); m_urlStrings << QLatin1String("file:///usr/share/applications/org.kde.dolphin.desktop"); m_urlStrings << QLatin1String("file:///usr/share/applications/org.kde.konsole.desktop"); } diff --git a/libtaskmanager/launchertasksmodel.h b/libtaskmanager/launchertasksmodel.h --- a/libtaskmanager/launchertasksmodel.h +++ b/libtaskmanager/launchertasksmodel.h @@ -62,7 +62,8 @@ int rowCount(const QModelIndex &parent = QModelIndex()) const override; /** - * The list of launcher URLs serialized to strings. + * The list of launcher URLs serialized to strings along with + * the activities they belong to. * * @see setLauncherList * @returns the list of launcher URLs serialized to strings. @@ -107,6 +108,43 @@ bool requestRemoveLauncher(const QUrl &url); /** + * Request adding a launcher with the given URL to current activity. + * + * If this URL is already in the list, the request will fail. URLs are + * compared for equality after removing the query string used to hold + * metadata. + * + * @see launcherUrlsMatch + * @param url A launcher URL. + * @returns @c true if a launcher was added. + */ + bool requestAddLauncherToActivity(const QUrl &url, const QString &activity); + + /** + * Request removing the launcher with the given URL from the current activity. + * + * If this URL is already in the list, the request will fail. URLs are + * compared for equality after removing the query string used to hold + * metadata. + * + * @see launcherUrlsMatch + * @param url A launcher URL. + * @returns @c true if the launcher was removed. + */ + bool requestRemoveLauncherFromActivity(const QUrl &url, const QString &activity); + + /** + * Return the list of activities the launcher belongs to. + * If there is no launcher with that url, the list will be empty, + * while if the launcher is on all activities, it will contain a + * null uuid. + * + * URLs are compared for equality after removing the query string used + * to hold metadata. + */ + QStringList launcherActivities(const QUrl &url) const; + + /** * Return the position of the launcher with the given URL. * * URLs are compared for equality after removing the query string used diff --git a/libtaskmanager/launchertasksmodel.cpp b/libtaskmanager/launchertasksmodel.cpp --- a/libtaskmanager/launchertasksmodel.cpp +++ b/libtaskmanager/launchertasksmodel.cpp @@ -27,28 +27,59 @@ #include #include +#include + #include #include #include +#include +#include #if HAVE_X11 #include #endif +#include "launchertasksmodel_p.h" + namespace TaskManager { +typedef QSet ActivitiesSet; + +template +inline bool isOnAllActivities(const ActivitiesCollection &activities) +{ + return activities.isEmpty() || activities.contains(NULL_UUID); +} + + class LauncherTasksModel::Private { public: Private(LauncherTasksModel *q); - QList launchers; + + KActivities::Consumer activitiesConsumer; + + QList launchersOrder; + + QHash activitiesForLauncher; + inline void setActivitiesForLauncher(const QUrl &url, const ActivitiesSet &activities) { + if (activities.size() == activitiesConsumer.activities().size()) { + activitiesForLauncher[url] = { NULL_UUID }; + } else { + activitiesForLauncher[url] = activities; + } + } + QHash appDataCache; QTimer sycocaChangeTimer; void init(); AppData appData(const QUrl &url); + bool requestAddLauncherToActivities(const QUrl &_url, const QStringList &activities); + bool requestRemoveLauncherFromActivities(const QUrl &_url, const QStringList &activities); + private: LauncherTasksModel *q; }; @@ -65,14 +96,14 @@ QObject::connect(&sycocaChangeTimer, &QTimer::timeout, q, [this]() { - if (!launchers.count()) { + if (!launchersOrder.count()) { return; } appDataCache.clear(); // Emit changes of all roles satisfied from app data cache. - q->dataChanged(q->index(0, 0), q->index(launchers.count() - 1, 0), + q->dataChanged(q->index(0, 0), q->index(launchersOrder.count() - 1, 0), QVector{Qt::DisplayRole, Qt::DecorationRole, AbstractTasksModel::AppId, AbstractTasksModel::AppName, AbstractTasksModel::GenericName, AbstractTasksModel::LauncherUrl}); @@ -93,14 +124,163 @@ AppData LauncherTasksModel::Private::appData(const QUrl &url) { - if (!appDataCache.contains(url)) { - const AppData &data = appDataFromUrl(url, QIcon::fromTheme(QLatin1String("unknown"))); - appDataCache.insert(url, data); + const auto &it = appDataCache.constFind(url); - return data; + if (it != appDataCache.constEnd()) { + return *it; } - return appDataCache.value(url); + const AppData &data = appDataFromUrl(url, QIcon::fromTheme(QLatin1String("unknown"))); + + appDataCache.insert(url, data); + + return data; +} + +bool LauncherTasksModel::Private::requestAddLauncherToActivities(const QUrl &_url, const QStringList &_activities) +{ + // isValid() for the passed-in URL might return true if it was + // constructed in TolerantMode, but we want to reject invalid URLs. + QUrl url(_url.toString(), QUrl::StrictMode); + const auto activities = ActivitiesSet::fromList(_activities); + + if (url.isEmpty() || !url.isValid()) { + return false; + } + + // Merge duplicates + int row = -1; + foreach(const QUrl &launcher, launchersOrder) { + ++row; + + if (launcherUrlsMatch(url, launcher, IgnoreQueryItems)) { + ActivitiesSet newActivities; + + if (!activitiesForLauncher.contains(url)) { + // If we don't have the activities assigned to this url + // for some reason + newActivities = activities; + + } else { + if (isOnAllActivities(activities)) { + // If the new list is empty, or has a null uuid, this + // launcher should be on all activities + newActivities = ActivitiesSet { NULL_UUID }; + + } else if (isOnAllActivities(activitiesForLauncher[url])) { + // If we have been on all activities before, and we have + // been asked to be on a specific one, lets make an + // exception - we will set the activities to exactly + // what we have been asked + newActivities = activities; + + + } else { + newActivities += activities; + newActivities += activitiesForLauncher[url]; + + } + } + + if (newActivities != activitiesForLauncher[url]) { + setActivitiesForLauncher(url, newActivities); + + emit q->dataChanged( + q->index(row, 0), + q->index(row, 0)); + + emit q->launcherListChanged(); + return true; + + } + + return false; + } + } + + // This is a new one + const auto count = launchersOrder.count(); + q->beginInsertRows(QModelIndex(), count, count); + setActivitiesForLauncher(url, activities); + launchersOrder.append(url); + q->endInsertRows(); + + emit q->launcherListChanged(); + + return true; +} + +bool LauncherTasksModel::Private::requestRemoveLauncherFromActivities(const QUrl &url, const QStringList &activities) +{ + for (int row = 0; row < launchersOrder.count(); ++row) { + const QUrl &launcher = launchersOrder.at(row); + + if (launcherUrlsMatch(url, launcher, IgnoreQueryItems) + || launcherUrlsMatch(url, appData(launcher).url, IgnoreQueryItems)) { + + const auto currentActivities = activitiesForLauncher[url]; + ActivitiesSet newActivities; + + bool remove = false; + bool update = false; + + if (isOnAllActivities(currentActivities)) { + // We are currently on all activities. + // Should we go away, or just remove from the current one? + + if (isOnAllActivities(activities)) { + remove = true; + + } else { + for (const auto& activity: activitiesConsumer.activities()) { + if (!activities.contains(activity)) { + newActivities << activity; + } else { + update = true; + } + } + } + + } else { + // We weren't on all activities, just remove those that + // we were on + + for (const auto& activity: currentActivities) { + if (!activities.contains(activity)) { + newActivities << activity; + } + } + + if (newActivities.isEmpty()) { + remove = true; + } else { + update = true; + } + } + + if (remove) { + q->beginRemoveRows(QModelIndex(), row, row); + launchersOrder.removeAt(row); + activitiesForLauncher.remove(url); + appDataCache.remove(launcher); + q->endRemoveRows(); + + } else if (update) { + setActivitiesForLauncher(url, newActivities); + + emit q->dataChanged( + q->index(row, 0), + q->index(row, 0)); + } + + if (remove || update) { + emit q->launcherListChanged(); + return true; + } + } + } + + return false; } LauncherTasksModel::LauncherTasksModel(QObject *parent) @@ -116,13 +296,12 @@ QVariant LauncherTasksModel::data(const QModelIndex &index, int role) const { - if (!index.isValid() || index.row() >= d->launchers.count()) { + if (!index.isValid() || index.row() >= d->launchersOrder.count()) { return QVariant(); } - const QUrl &url = d->launchers.at(index.row()); + const QUrl &url = d->launchersOrder.at(index.row()); const AppData &data = d->appData(url); - if (role == Qt::DisplayRole) { return data.name; } else if (role == Qt::DecorationRole) { @@ -151,121 +330,195 @@ return true; } else if (role == IsOnAllVirtualDesktops) { return true; + } else if (role == Activities) { + return QStringList(d->activitiesForLauncher[url].toList()); } return QVariant(); } int LauncherTasksModel::rowCount(const QModelIndex &parent) const { - return parent.isValid() ? 0 : d->launchers.count(); + return parent.isValid() ? 0 : d->launchersOrder.count(); } QStringList LauncherTasksModel::launcherList() const { - return QUrl::toStringList(d->launchers); + // Serializing the launchers + QStringList result; + + for (const auto &launcher: d->launchersOrder) { + const auto &activities = d->activitiesForLauncher[launcher]; + + QString serializedLauncher; + if (isOnAllActivities(activities)) { + serializedLauncher = launcher.toString(); + + } else { + serializedLauncher = + "[" + d->activitiesForLauncher[launcher].toList().join(",") + "]\n" + + launcher.toString(); + } + + result << serializedLauncher; + } + + return result; } -void LauncherTasksModel::setLauncherList(const QStringList &launchers) +void LauncherTasksModel::setLauncherList(const QStringList &serializedLaunchers) { - const QList &_urls = QUrl::fromStringList(launchers, QUrl::StrictMode); - QList urls; + // Clearing everything + QList newLaunchersOrder; + QHash newActivitiesForLauncher; + + // Loading the activity to launchers map + QHash> launchersForActivitiesCandidates; + for (const auto& serializedLauncher: serializedLaunchers) { + QStringList _activities; + QUrl url; + + std::tie(url, _activities) = + deserializeLauncher(serializedLauncher); + + auto activities = ActivitiesSet::fromList(_activities); + + // Is url is not valid, ignore it + if (!url.isValid()) continue; - // Reject invalid urls and duplicates. - foreach(const QUrl &url, _urls) { - if (url.isValid()) { - bool dupe = false; + // If we have a null uuid, it means we are on all activities + // and we should contain only the null uuid + if (isOnAllActivities(activities)) { + activities = { NULL_UUID }; - foreach(const QUrl &addedUrl, urls) { - dupe = launcherUrlsMatch(url, addedUrl, IgnoreQueryItems); - if (dupe) break; + } else { + // Filter out invalid activities + const auto allActivities = d->activitiesConsumer.activities(); + ActivitiesSet validActivities; + for (const auto& activity: activities) { + if (allActivities.contains(activity)) { + validActivities << activity; + } } - if (!dupe) { - urls.append(url); + if (validActivities.isEmpty()) { + // If all activities that had this launcher are + // removed, we are killing the launcher as well + continue; } + + activities = validActivities; + } + + // Is the url a duplicate? + const auto location = + std::find_if(newLaunchersOrder.begin(), newLaunchersOrder.end(), + [&url] (const QUrl &item) { + return launcherUrlsMatch(url, item, IgnoreQueryItems); + }); + + + if (location != newLaunchersOrder.end()) { + // It is a duplicate + url = *location; + + } else { + // It is not a duplicate, we need to add it + // to the list of registered launchers + newLaunchersOrder << url; + } + + + if (!newActivitiesForLauncher.contains(url)) { + // This is the first time we got this url + newActivitiesForLauncher[url] = activities; + + } else if (newActivitiesForLauncher[url].contains(NULL_UUID)) { + // Do nothing, we are already on all activities + + } else if (activities.contains(NULL_UUID)) { + newActivitiesForLauncher[url] = { NULL_UUID }; + + } else { + // We are not on all activities, append the new ones + newActivitiesForLauncher[url] += activities; + } } - if (d->launchers != urls) { + if (newActivitiesForLauncher != d->activitiesForLauncher) { // Common case optimization: If the list changed but its size // did not (e.g. due to reordering by a user of this model), // just clear the caches and announce new data instead of // resetting. - if (d->launchers.count() == urls.count()) { - d->launchers.clear(); + if (newLaunchersOrder.count() == d->launchersOrder.count()) { d->appDataCache.clear(); - d->launchers = urls; + std::swap(newLaunchersOrder, d->launchersOrder); + std::swap(newActivitiesForLauncher, d->activitiesForLauncher); + + emit dataChanged( + index(0, 0), + index(d->launchersOrder.count() - 1, 0)); - emit dataChanged(index(0, 0), index(d->launchers.count() - 1, 0)); } else { beginResetModel(); - d->launchers.clear(); - d->appDataCache.clear(); + std::swap(newLaunchersOrder, d->launchersOrder); + std::swap(newActivitiesForLauncher, d->activitiesForLauncher); - d->launchers = urls; + d->appDataCache.clear(); endResetModel(); + } emit launcherListChanged(); } } -bool LauncherTasksModel::requestAddLauncher(const QUrl &_url) +bool LauncherTasksModel::requestAddLauncher(const QUrl &url) { - // isValid() for the passed-in URL might return true if it was - // constructed in TolerantMode, but we want to reject invalid URLs. - QUrl url(_url.toString(), QUrl::StrictMode); - - if (url.isEmpty() || !url.isValid()) { - return false; - } - - // Reject duplicates. - foreach(const QUrl &launcher, d->launchers) { - if (launcherUrlsMatch(url, launcher, IgnoreQueryItems)) { - return false; - } - } + return d->requestAddLauncherToActivities(url, { NULL_UUID }); +} - const int count = d->launchers.count(); - beginInsertRows(QModelIndex(), count, count); - d->launchers.append(url); - endInsertRows(); +bool LauncherTasksModel::requestRemoveLauncher(const QUrl &url) +{ + return d->requestRemoveLauncherFromActivities(url, { NULL_UUID }); +} - emit launcherListChanged(); +bool LauncherTasksModel::requestAddLauncherToActivity(const QUrl &url, const QString &activity) +{ + return d->requestAddLauncherToActivities(url, { activity }); +} - return true; +bool LauncherTasksModel::requestRemoveLauncherFromActivity(const QUrl &url, const QString &activity) +{ + return d->requestRemoveLauncherFromActivities(url, { activity }); } -bool LauncherTasksModel::requestRemoveLauncher(const QUrl &url) +QStringList LauncherTasksModel::launcherActivities(const QUrl &_url) const { - for (int i = 0; i < d->launchers.count(); ++i) { - const QUrl &launcher = d->launchers.at(i); + const auto position = launcherPosition(_url); - if (launcherUrlsMatch(url, launcher, IgnoreQueryItems) - || launcherUrlsMatch(url, d->appData(launcher).url, IgnoreQueryItems)) { - beginRemoveRows(QModelIndex(), i, i); - d->launchers.removeAt(i); - d->appDataCache.remove(launcher); - endRemoveRows(); + if (position == -1) { + // If we do not have this launcher, return an empty list + return {}; - emit launcherListChanged(); + } else { + const auto url = d->launchersOrder.at(position); - return true; - } + // If the launcher is on all activities, return a null uuid + return d->activitiesForLauncher.contains(url) + ? d->activitiesForLauncher[url].toList() + : QStringList { NULL_UUID }; } - - return false; } int LauncherTasksModel::launcherPosition(const QUrl &url) const { - for (int i = 0; i < d->launchers.count(); ++i) { - if (launcherUrlsMatch(url, d->launchers.at(i), IgnoreQueryItems)) { + for (int i = 0; i < d->launchersOrder.count(); ++i) { + if (launcherUrlsMatch(url, d->launchersOrder.at(i), IgnoreQueryItems)) { return i; } } @@ -281,11 +534,11 @@ void LauncherTasksModel::requestNewInstance(const QModelIndex &index) { if (!index.isValid() || index.model() != this - || index.row() < 0 || index.row() >= d->launchers.count()) { + || index.row() < 0 || index.row() >= d->launchersOrder.count()) { return; } - const QUrl &url = d->launchers.at(index.row()); + const QUrl &url = d->launchersOrder.at(index.row()); quint32 timeStamp = 0; @@ -312,12 +565,12 @@ void LauncherTasksModel::requestOpenUrls(const QModelIndex &index, const QList &urls) { if (!index.isValid() || index.model() != this - || index.row() < 0 || index.row() >= d->launchers.count() + || index.row() < 0 || index.row() >= d->launchersOrder.count() || urls.isEmpty()) { return; } - const QUrl &url = d->launchers.at(index.row()); + const QUrl &url = d->launchersOrder.at(index.row()); quint32 timeStamp = 0; diff --git a/libtaskmanager/launchertasksmodel_p.h b/libtaskmanager/launchertasksmodel_p.h new file mode 100644 --- /dev/null +++ b/libtaskmanager/launchertasksmodel_p.h @@ -0,0 +1,66 @@ +/******************************************************************** +Copyright 2016 Ivan Cukic + +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 . +*********************************************************************/ + +#ifndef LAUNCHERTASKSMODEL_P_H +#define LAUNCHERTASKSMODEL_P_H + +#include +#include +#include + +namespace TaskManager { + +#define NULL_UUID "00000000-0000-0000-0000-000000000000" + +inline static std::pair deserializeLauncher(const QString &serializedLauncher) +{ + QStringList activities; + QUrl url(serializedLauncher, QUrl::StrictMode); + + // The storage format is: [list of activity ids]\nURL + // The activity IDs list can not be empty, it at least needs + // to contain the nulluuid. + // If parsing fails, we are considering the serialized launcher + // to not have the activities array -- to have the old format + if (serializedLauncher.startsWith('[')) { + // It seems we have the activity specifier in the launcher + const auto activitiesBlockEnd = serializedLauncher.indexOf("]\n"); + + if (activitiesBlockEnd != -1) { + activities = serializedLauncher.mid(1, activitiesBlockEnd - 1).split(",", QString::SkipEmptyParts); + + if (!activities.isEmpty()) { + url = QUrl(serializedLauncher.mid(activitiesBlockEnd + 2), QUrl::StrictMode); + } + } + } + + // If the activities array is empty, this means that this launcher + // needs to be on all activities + if (activities.isEmpty()) { + activities = QStringList({ NULL_UUID }); + } + + return std::make_pair(url, activities); +} + +} // namespace TaskManager + +#endif // LAUNCHERTASKSMODEL_P_H diff --git a/libtaskmanager/taskfilterproxymodel.cpp b/libtaskmanager/taskfilterproxymodel.cpp --- a/libtaskmanager/taskfilterproxymodel.cpp +++ b/libtaskmanager/taskfilterproxymodel.cpp @@ -21,6 +21,8 @@ #include "taskfilterproxymodel.h" #include "abstracttasksmodel.h" +#include "launchertasksmodel_p.h" + namespace TaskManager { @@ -287,7 +289,7 @@ if (!activities.isNull()) { const QStringList l = activities.toStringList(); - if (!l.isEmpty() && !l.contains(d->activity)) { + if (!l.isEmpty() && !l.contains(NULL_UUID) && !l.contains(d->activity)) { return false; } } diff --git a/libtaskmanager/tasksmodel.h b/libtaskmanager/tasksmodel.h --- a/libtaskmanager/tasksmodel.h +++ b/libtaskmanager/tasksmodel.h @@ -120,7 +120,8 @@ int launcherCount() const; /** - * The list of launcher URLs serialized to strings. + * The list of launcher URLs serialized to strings along with + * the activities they belong to. * * @see setLauncherList * @returns the list of launcher URLs serialized to strings. @@ -547,6 +548,43 @@ Q_INVOKABLE bool requestRemoveLauncher(const QUrl &url); /** + * Request adding a launcher with the given URL to current activity. + * + * If this URL is already in the list, the request will fail. URLs are + * compared for equality after removing the query string used to hold + * metadata. + * + * @see launcherUrlsMatch + * @param url A launcher URL. + * @returns @c true if a launcher was added. + */ + Q_INVOKABLE bool requestAddLauncherToActivity(const QUrl &url, const QString &activity); + + /** + * Request removing the launcher with the given URL from the current activity. + * + * If this URL is already in the list, the request will fail. URLs are + * compared for equality after removing the query string used to hold + * metadata. + * + * @see launcherUrlsMatch + * @param url A launcher URL. + * @returns @c true if the launcher was removed. + */ + Q_INVOKABLE bool requestRemoveLauncherFromActivity(const QUrl &url, const QString &activity); + + /** + * Return the list of activities the launcher belongs to. + * If there is no launcher with that url, the list will be empty, + * while if the launcher is on all activities, it will contain a + * null uuid. + * + * URLs are compared for equality after removing the query string used + * to hold metadata. + */ + Q_INVOKABLE QStringList launcherActivities(const QUrl &url); + + /** * Return the position of the launcher with the given URL. * * URLs are compared for equality after removing the query string used diff --git a/libtaskmanager/tasksmodel.cpp b/libtaskmanager/tasksmodel.cpp --- a/libtaskmanager/tasksmodel.cpp +++ b/libtaskmanager/tasksmodel.cpp @@ -30,6 +30,8 @@ #include "startuptasksmodel.h" #include "windowtasksmodel.h" +#include "launchertasksmodel_p.h" + #include #include #include @@ -1152,6 +1154,51 @@ return false; } +bool TasksModel::requestAddLauncherToActivity(const QUrl &url, const QString &activity) +{ + d->initLauncherTasksModel(); + + bool added = d->launcherTasksModel->requestAddLauncherToActivity(url, activity); + + // If using manual and launch-in-place sorting with separate launchers, + // we need to trigger a sort map update to move any window tasks to + // their launcher position now. + if (added && d->sortMode == SortManual && (d->launchInPlace || !d->separateLaunchers)) { + d->updateManualSortMap(); + d->forceResort(); + } + + return added; +} + +bool TasksModel::requestRemoveLauncherFromActivity(const QUrl &url, const QString &activity) +{ + if (d->launcherTasksModel) { + bool removed = d->launcherTasksModel->requestRemoveLauncherFromActivity(url, activity); + + // If using manual and launch-in-place sorting with separate launchers, + // we need to trigger a sort map update to move any window tasks no + // longer backed by a launcher out of the launcher area. + if (removed && d->sortMode == SortManual && (d->launchInPlace || !d->separateLaunchers)) { + d->updateManualSortMap(); + d->forceResort(); + } + + return removed; + } + + return false; +} + +QStringList TasksModel::launcherActivities(const QUrl &url) +{ + if (d->launcherTasksModel) { + return d->launcherTasksModel->launcherActivities(url); + } + + return {}; +} + int TasksModel::launcherPosition(const QUrl &url) const { if (d->launcherTasksModel) { @@ -1435,23 +1482,27 @@ return; } - QMap sortedLaunchers; + QMap sortedShownLaunchers; foreach(const QString &launcherUrlStr, launcherList()) { int row = -1; - QUrl launcherUrl(launcherUrlStr); + QStringList activities; + QUrl launcherUrl; - for (int i = 0; i < rowCount(); ++i) { - const QUrl &rowLauncherUrl = index(i, 0).data(AbstractTasksModel::LauncherUrlWithoutIcon).toUrl(); + std::tie(launcherUrl, activities) = deserializeLauncher(launcherUrlStr); + + for (int i = 0; i < d->launcherTasksModel->rowCount(); ++i) { + const QUrl &rowLauncherUrl = + d->launcherTasksModel->index(i, 0).data(AbstractTasksModel::LauncherUrlWithoutIcon).toUrl(); if (launcherUrlsMatch(launcherUrl, rowLauncherUrl, IgnoreQueryItems)) { row = i; break; } } if (row != -1) { - sortedLaunchers.insert(row, launcherUrl); + sortedShownLaunchers.insert(row, launcherUrlStr); } } @@ -1477,7 +1528,7 @@ } } - setLauncherList(QUrl::toStringList(sortedLaunchers.values())); + setLauncherList(sortedShownLaunchers.values()); d->launcherSortingDirty = false; } diff --git a/libtaskmanager/tasktools.h b/libtaskmanager/tasktools.h --- a/libtaskmanager/tasktools.h +++ b/libtaskmanager/tasktools.h @@ -65,6 +65,22 @@ TASKMANAGER_EXPORT AppData appDataFromUrl(const QUrl &url, const QIcon &fallbackIcon = QIcon()); /** + * Fills in and returns an AppData struct based on the given application + * id. + * + * Application ids are .desktop file names sans extension or an absolute + * path to a .desktop file. + * + * NOTE: Unlike appDataFromUrl(), this makes no attempt to procure icon + * data at this time. + * + * @see appDataFromUrl + * @param appId An application id. + * @returns @c AppData filled in based on the given application id. + */ +TASKMANAGER_EXPORT AppData appDataFromAppId(const QString &appId); + +/** * Returns an application id for an URL using the preferred:// scheme. * * Recognized values for the host component of the URL are: diff --git a/libtaskmanager/tasktools.cpp b/libtaskmanager/tasktools.cpp --- a/libtaskmanager/tasktools.cpp +++ b/libtaskmanager/tasktools.cpp @@ -116,6 +116,44 @@ 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(); + 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; +} + QString defaultApplication(const QUrl &url) { if (url.scheme() != QLatin1String("preferred")) { diff --git a/libtaskmanager/waylandtasksmodel.cpp b/libtaskmanager/waylandtasksmodel.cpp --- a/libtaskmanager/waylandtasksmodel.cpp +++ b/libtaskmanager/waylandtasksmodel.cpp @@ -45,11 +45,14 @@ public: Private(WaylandTasksModel *q); QList windows; - QHash serviceCache; + QHash appDataCache; KWayland::Client::PlasmaWindowManagement *windowManagement = nullptr; void initWayland(); void addWindow(KWayland::Client::PlasmaWindow *window); + + AppData appData(KWayland::Client::PlasmaWindow *window); + void dataChanged(KWayland::Client::PlasmaWindow *window, int role); void dataChanged(KWayland::Client::PlasmaWindow *window, const QVector &roles); @@ -116,20 +119,14 @@ windows.append(window); - KService::Ptr service = KService::serviceByStorageId(window->appId()); - - if (service) { - serviceCache.insert(window, service); - } - q->endInsertRows(); auto removeWindow = [window, this] { const int row = windows.indexOf(window); if (row != -1) { q->beginRemoveRows(QModelIndex(), row, row); windows.removeAt(row); - serviceCache.remove(window); + appDataCache.remove(window); q->endRemoveRows(); } }; @@ -147,13 +144,7 @@ QObject::connect(window, &KWayland::Client::PlasmaWindow::appIdChanged, q, [window, this] { - KService::Ptr service = KService::serviceByStorageId(window->appId()); - - if (service) { - serviceCache.insert(window, service); - } else { - serviceCache.remove(window); - } + appDataCache.remove(window); dataChanged(window, QVector{AppId, AppName, GenericName, LauncherUrl, LauncherUrlWithoutIcon}); @@ -237,6 +228,21 @@ ); } +AppData WaylandTasksModel::Private::appData(KWayland::Client::PlasmaWindow *window) +{ + const auto &it = appDataCache.constFind(window); + + if (it != appDataCache.constEnd()) { + return *it; + } + + const AppData &data = appDataFromAppId(window->appId()); + + appDataCache.insert(window, data); + + return data; +} + void WaylandTasksModel::Private::dataChanged(KWayland::Client::PlasmaWindow *window, int role) { QModelIndex idx = q->index(windows.indexOf(window)); @@ -273,19 +279,11 @@ } else if (role == AppId) { return window->appId(); } else if (role == AppName) { - if (d->serviceCache.contains(window)) { - return d->serviceCache.value(window)->name(); - } else { - return window->title(); - } + return d->appData(window).name; } else if (role == GenericName) { - if (d->serviceCache.contains(window)) { - return d->serviceCache.value(window)->genericName(); - } + return d->appData(window).genericName; } else if (role == LauncherUrl || role == LauncherUrlWithoutIcon) { - if (d->serviceCache.contains(window)) { - return QUrl::fromLocalFile(d->serviceCache.value(window)->entryPath()); - } + return d->appData(window).url; } else if (role == IsWindow) { return true; } else if (role == IsActive) { @@ -334,6 +332,8 @@ return window->skipTaskbar(); } else if (role == SkipPager) { // FIXME Implement. + } else if (role == AppPid) { + // FIXME Implement. } return QVariant(); @@ -368,13 +368,15 @@ KWayland::Client::PlasmaWindow* window = d->windows.at(index.row()); - if (d->serviceCache.contains(window)) { - const KService::Ptr service = d->serviceCache.value(window); + if (d->appDataCache.contains(window)) { + const AppData &data = d->appData(window); - new KRun(QUrl::fromLocalFile(service->entryPath()), 0, false); + new KRun(data.url, 0, false); - KActivities::ResourceInstance::notifyAccessed(QUrl("applications:" + service->storageId()), - "org.kde.libtaskmanager"); + if (!data.id.isEmpty()) { + KActivities::ResourceInstance::notifyAccessed(QUrl(QStringLiteral("applications:") + data.id), + QStringLiteral("org.kde.libtaskmanager")); + } } } @@ -386,15 +388,14 @@ return; } - KWayland::Client::PlasmaWindow *window = d->windows.at(index.row()); - - if (d->serviceCache.contains(window)) { - const KService::Ptr service = d->serviceCache.value(window); + const QUrl &url = d->appData(d->windows.at(index.row())).url; + const KService::Ptr service = KService::serviceByDesktopPath(url.toLocalFile()); + if (service) { KRun::runApplication(*service, urls, nullptr, 0); - KActivities::ResourceInstance::notifyAccessed(QUrl("applications:" + service->storageId()), - "org.kde.libtaskmanager"); + KActivities::ResourceInstance::notifyAccessed(QUrl(QStringLiteral("applications:") + service->storageId()), + QStringLiteral("org.kde.libtaskmanager")); } } diff --git a/libtaskmanager/xwindowtasksmodel.cpp b/libtaskmanager/xwindowtasksmodel.cpp --- a/libtaskmanager/xwindowtasksmodel.cpp +++ b/libtaskmanager/xwindowtasksmodel.cpp @@ -22,7 +22,9 @@ #include "xwindowtasksmodel.h" #include "tasktools.h" +#include #include +#include #include #include #include @@ -50,9 +52,9 @@ { static const NET::Properties windowInfoFlags = NET::WMState | NET::XAWMState | NET::WMDesktop | - NET::WMVisibleName | NET::WMGeometry | NET::WMFrameExtents | - NET::WMWindowType; -static const NET::Properties2 windowInfoFlags2 = NET::WM2WindowClass | NET::WM2AllowedActions; + NET::WMVisibleName | NET::WMGeometry | NET::WMFrameExtents | NET::WMWindowType | NET::WMPid; +static const NET::Properties2 windowInfoFlags2 = NET::WM2DesktopFileName | NET::WM2Activities | + NET::WM2WindowClass | NET::WM2AllowedActions; class XWindowTasksModel::Private { @@ -88,7 +90,6 @@ QUrl launcherUrl(WId window, bool encodeFallbackIcon = true); QUrl serviceUrl(int pid, const QString &type, const QStringList &cmdRemovals); KService::List servicesFromPid(int pid); - QStringList activities(WId window); bool demandsAttention(WId window); private: @@ -341,11 +342,11 @@ bool wipeAppDataCache = false; QVector changedRoles; - if (properties & (NET::WMName | NET::WMVisibleName | NET::WM2WindowClass | NET::WMPid) - || properties2 & NET::WM2WindowClass) { + if (properties & (NET::WMName | NET::WMVisibleName | NET::WMPid) + || properties2 & (NET::WM2DesktopFileName | NET::WM2WindowClass)) { wipeInfoCache = true; wipeAppDataCache = true; - changedRoles << Qt::DisplayRole << Qt::DecorationRole << AppId << AppName << GenericName << LauncherUrl; + changedRoles << Qt::DisplayRole << Qt::DecorationRole << AppId << AppName << GenericName << LauncherUrl << AppPid; } if ((properties & NET::WMIcon) && !changedRoles.contains(Qt::DecorationRole)) { @@ -414,41 +415,45 @@ KWindowInfo* XWindowTasksModel::Private::windowInfo(WId window) { - if (!windowInfoCache.contains(window)) { - KWindowInfo *info = new KWindowInfo(window, windowInfoFlags, windowInfoFlags2); - windowInfoCache.insert(window, info); + const auto &it = windowInfoCache.constFind(window); - return info; + if (it != windowInfoCache.constEnd()) { + return *it; } - return windowInfoCache.value(window); + KWindowInfo *info = new KWindowInfo(window, windowInfoFlags, windowInfoFlags2); + windowInfoCache.insert(window, info); + + return info; } AppData XWindowTasksModel::Private::appData(WId window) { - if (!appDataCache.contains(window)) { - const AppData &data = appDataFromUrl(windowUrl(window)); + const auto &it = appDataCache.constFind(window); - // If we weren't able to derive a launcher URL from the window meta data, - // fall back to WM_CLASS Class string as app id. This helps with apps we - // can't map to an URL due to existing outside the regular system - // environment, e.g. wine clients. - if (data.id.isEmpty() && data.url.isEmpty()) { - AppData dataCopy = data; + if (it != appDataCache.constEnd()) { + return *it; + } - dataCopy.id = windowInfo(window)->windowClassClass(); + const AppData &data = appDataFromUrl(windowUrl(window)); - appDataCache.insert(window, dataCopy); + // If we weren't able to derive a launcher URL from the window meta data, + // fall back to WM_CLASS Class string as app id. This helps with apps we + // can't map to an URL due to existing outside the regular system + // environment, e.g. wine clients. + if (data.id.isEmpty() && data.url.isEmpty()) { + AppData dataCopy = data; - return dataCopy; - } + dataCopy.id = windowInfo(window)->windowClassClass(); - appDataCache.insert(window, data); + appDataCache.insert(window, dataCopy); - return data; + return dataCopy; } - return appDataCache.value(window); + appDataCache.insert(window, data); + + return data; } QIcon XWindowTasksModel::Private::icon(WId window) @@ -484,6 +489,25 @@ QUrl url; const KWindowInfo *info = windowInfo(window); + + QString desktopFile = QString::fromUtf8(info->desktopFileName()); + + if (!desktopFile.isEmpty()) { + KService::Ptr service = KService::serviceByStorageId(desktopFile); + + if (service) { + return QUrl::fromLocalFile(service->entryPath()); + } + + if (!desktopFile.endsWith(QLatin1String(".desktop"))) { + desktopFile.append(QLatin1String(".desktop")); + } + + if (KDesktopFile::isDesktopFile(desktopFile) && QFile::exists(desktopFile)) { + return QUrl::fromLocalFile(desktopFile); + } + } + const QString &classClass = info->windowClassClass(); const QString &className = info->windowClassName(); @@ -801,19 +825,6 @@ return services; } -QStringList XWindowTasksModel::Private::activities(WId window) -{ - NETWinInfo ni(QX11Info::connection(), window, QX11Info::appRootWindow(), 0, NET::WM2Activities); - - const QString result(ni.activities()); - - if (!result.isEmpty() && result != QLatin1String("00000000-0000-0000-0000-000000000000")) { - return result.split(','); - } - - return QStringList(); -} - bool XWindowTasksModel::Private::demandsAttention(WId window) { if (windows.contains(window)) { @@ -905,7 +916,7 @@ } else if (role == ScreenGeometry) { return screenGeometry(d->windowInfo(window)->frameGeometry().center()); } else if (role == Activities) { - return d->activities(window); + return d->windowInfo(window)->activities(); } else if (role == IsDemandingAttention) { return d->demandsAttention(window); } else if (role == SkipTaskbar) { @@ -915,6 +926,8 @@ return (info->hasState(NET::SkipTaskbar) || info->windowType(NET::UtilityMask) == NET::Utility); } else if (role == SkipPager) { return d->windowInfo(window)->hasState(NET::SkipPager); + } else if (role == AppPid) { + return d->windowInfo(window)->pid(); } return QVariant(); @@ -964,10 +977,15 @@ return; } - const QUrl &url = d->appData(d->windows.at(index.row())).url; + const AppData &data = d->appData(d->windows.at(index.row())); + + if (data.url.isValid()) { + new KRun(data.url, 0, false, KStartupInfo::createNewStartupIdForTimestamp(QX11Info::appUserTime())); - if (url.isValid()) { - new KRun(url, 0, false, KStartupInfo::createNewStartupIdForTimestamp(QX11Info::appUserTime())); + if (!data.id.isEmpty()) { + KActivities::ResourceInstance::notifyAccessed(QUrl(QStringLiteral("applications:") + data.id), + QStringLiteral("org.kde.libtaskmanager")); + } } } @@ -981,8 +999,12 @@ const QUrl &url = d->appData(d->windows.at(index.row())).url; const KService::Ptr service = KService::serviceByDesktopPath(url.toLocalFile()); + if (service) { KRun::runApplication(*service, urls, nullptr, 0, {}, KStartupInfo::createNewStartupIdForTimestamp(QX11Info::appUserTime())); + + KActivities::ResourceInstance::notifyAccessed(QUrl(QStringLiteral("applications:") + service->storageId()), + QStringLiteral("org.kde.libtaskmanager")); } } diff --git a/lookandfeel/contents/lockscreen/LockScreenUi.qml b/lookandfeel/contents/lockscreen/LockScreenUi.qml --- a/lookandfeel/contents/lockscreen/LockScreenUi.qml +++ b/lookandfeel/contents/lockscreen/LockScreenUi.qml @@ -28,7 +28,6 @@ import "../components" PlasmaCore.ColorScope { - id: lockScreenRoot colorGroup: PlasmaCore.Theme.ComplementaryColorGroup @@ -69,120 +68,184 @@ visible: false } - Clock { - anchors.bottom: parent.verticalCenter - anchors.bottomMargin: units.gridUnit * 13 - anchors.horizontalCenter: parent.horizontalCenter - } + Item { + id: lockScreenRoot - ListModel { - id: users + x: parent.x + y: parent.y + width: parent.width + height: parent.height - Component.onCompleted: { - users.append({name: kscreenlocker_userName, - realName: kscreenlocker_userName, - icon: kscreenlocker_userImage, + Component.onCompleted: PropertyAnimation { id: launchAnimation; target: lockScreenRoot; property: "opacity"; from: 0; to: 1; duration: 1000 } - }) - if (sessionsModel.canStartNewSession) { - users.append({realName: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "New Session"), - name: "__new_session", - iconName: "list-add" - }) + states: [ + State { + name: "onOtherSession" + // for slide out animation + PropertyChanges { target: lockScreenRoot; y: lockScreenRoot.height } + // we also change the opacity just to be sure it's not visible even on unexpected screen dimension changes with possible race conditions + PropertyChanges { target: lockScreenRoot; opacity: 0 } } - } - } + ] + + transitions: + Transition { + // we only animate switchting to another session, because kscreenlocker doesn't get notified when + // coming from another session back and so we wouldn't know when to trigger the animation exactly + from: "" + to: "onOtherSession" + + PropertyAnimation { id: stateChangeAnimation; properties: "y"; duration: 300; easing: Easing.InQuad} + PropertyAnimation { properties: "opacity"; duration: 300} - StackView { - id: mainStack - anchors.fill: parent - focus: true //StackView is an implicit focus scope, so we need to give this focus so the item inside will have it - initialItem: MainBlock { - userListModel: users - notificationMessage: { - var text = "" - if (keystateSource.data["Caps Lock"]["Locked"]) { - text += i18nd("plasma_lookandfeel_org.kde.lookandfeel","Caps Lock is on") - if (root.notification) { - text += " • " + onRunningChanged: { + // after the animation has finished switch session: since we only animate the transition TO state "onOtherSession" + // and not the other way around, we don't have to check the state we transitioned into + if (/* lockScreenRoot.state == "onOtherSession" && */ !running) { + mainStack.currentItem.switchSession() } } - text += root.notification - return text } - onNewSession: { - sessionsModel.startNewSession(false); - } + Clock { + anchors.bottom: parent.verticalCenter + anchors.bottomMargin: units.gridUnit * 13 + anchors.horizontalCenter: parent.horizontalCenter + } + + ListModel { + id: users - onLoginRequest: { - root.notification = "" - authenticator.tryUnlock(password) + Component.onCompleted: { + users.append({name: kscreenlocker_userName, + realName: kscreenlocker_userName, + icon: kscreenlocker_userImage, + + }) } + } - actionItems: [ - ActionButton { - text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Switch User") - iconSource: "system-switch-user" - onClicked: mainStack.push(switchSessionPage) - visible: sessionsModel.count > 1 && sessionsModel.canSwitchUser + StackView { + id: mainStack + anchors.fill: parent + focus: true //StackView is an implicit focus scope, so we need to give this focus so the item inside will have it + + initialItem: MainBlock { + id: mainBlock + + Stack.onStatusChanged: { + // prepare for presenting again to the user + if (Stack.status == Stack.Activating) { + mainPasswordBox.remove(0, mainPasswordBox.length) + mainPasswordBox.focus = true + } + } + userListModel: users + notificationMessage: { + var text = "" + if (keystateSource.data["Caps Lock"]["Locked"]) { + text += i18nd("plasma_lookandfeel_org.kde.lookandfeel","Caps Lock is on") + if (root.notification) { + text += " • " + } + } + text += root.notification + return text + } + + onLoginRequest: { + root.notification = "" + authenticator.tryUnlock(password) } - ] + + actionItems: [ + ActionButton { + text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Switch User") + iconSource: "system-switch-user" + onClicked: mainStack.push(switchSessionPage) + // the current session isn't listed in the model, hence a check for greater than zero, not one + visible: (sessionsModel.count > 0 || sessionsModel.canStartNewSession) && sessionsModel.canSwitchUser + } + ] + } } - } - Component { - id: switchSessionPage - SessionManagementScreen { - userListModel: sessionsModel + Component { + id: switchSessionPage + SessionManagementScreen { + property var switchSession: finalSwitchSession - PlasmaComponents.Button { - Layout.fillWidth: true - text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Switch Session") - onClicked: { + Stack.onStatusChanged: { + if (Stack.status == Stack.Activating) { + focus = true + } + } + + userListModel: sessionsModel + + // initiating animation of lockscreen for session switch + function initSwitchSession() { + lockScreenRoot.state = 'onOtherSession' + } + + // initiating session switch and preparing lockscreen for possible return of user + function finalSwitchSession() { + mainStack.pop({immediate:true}) sessionsModel.switchUser(userListCurrentModelData.vtNumber) - mainStack.pop() + lockScreenRoot.state = '' } - } - actionItems: [ - ActionButton { - iconSource: "go-previous" - text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Back") - onClicked: mainStack.pop() + Keys.onLeftPressed: userList.decrementCurrentIndex() + Keys.onRightPressed: userList.incrementCurrentIndex() + Keys.onEnterPressed: initSwitchSession() + Keys.onReturnPressed: initSwitchSession() + Keys.onEscapePressed: mainStack.pop() + + PlasmaComponents.Button { + Layout.fillWidth: true + // the magic "-1" vtNumber indicates the "New Session" entry + text: userListCurrentModelData.vtNumber === -1 ? i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Start New Session") : i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Switch Session") + onClicked: initSwitchSession() } - ] - } - } - Loader { - active: root.viewVisible - source: "LockOsd.qml" - anchors { - horizontalCenter: parent.horizontalCenter - bottom: parent.bottom + actionItems: [ + ActionButton { + iconSource: "go-previous" + text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Back") + onClicked: mainStack.pop() + } + ] + } } - } - RowLayout { - id: footer - anchors { - bottom: parent.bottom - left: parent.left - right: parent.right - margins: units.smallSpacing + Loader { + active: root.viewVisible + source: "LockOsd.qml" + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + } } - KeyboardLayoutButton { - } + RowLayout { + id: footer + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + margins: units.smallSpacing + } - Item { - Layout.fillWidth: true - } + KeyboardLayoutButton { + } - Battery {} - } + Item { + Layout.fillWidth: true + } + Battery {} + } + } Component.onCompleted: { // version support checks diff --git a/lookandfeel/contents/lockscreen/MainBlock.qml b/lookandfeel/contents/lockscreen/MainBlock.qml --- a/lookandfeel/contents/lockscreen/MainBlock.qml +++ b/lookandfeel/contents/lockscreen/MainBlock.qml @@ -28,22 +28,15 @@ import "../components" SessionManagementScreen { + + property Item mainPasswordBox: passwordBox /* * Login has been requested with the following username and password * If username field is visible, it will be taken from that, otherwise from the "name" property of the currentIndex */ signal loginRequest(string password) - /* - */ - signal newSession() - function startLogin() { - if (userListCurrentIndex == 1) { - newSession() - return; - } - var password = passwordBox.text //this is partly because it looks nicer @@ -64,8 +57,6 @@ onAccepted: startLogin() - visible: userListCurrentIndex == 0 - //if empty and left or right is pressed change selection in user switch //this cannot be in keys.onLeftPressed as then it doesn't reach the password box Keys.onPressed: { @@ -92,7 +83,7 @@ id: loginButton Layout.fillWidth: true - text: userListCurrentIndex == 0 ? i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Unlock") : i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Start New Session") + text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Unlock") onClicked: startLogin() } } diff --git a/plasma-windowed/plasmawindowedcorona.cpp b/plasma-windowed/plasmawindowedcorona.cpp --- a/plasma-windowed/plasmawindowedcorona.cpp +++ b/plasma-windowed/plasmawindowedcorona.cpp @@ -51,7 +51,7 @@ //forbid more instances per applet (todo: activate the correpsponding already loaded applet) for (Plasma::Applet *a : cont->applets()) { - if (a->pluginInfo().pluginName() == applet) { + if (a->pluginMetaData().pluginId() == applet) { return; } } diff --git a/runners/CMakeLists.txt b/runners/CMakeLists.txt --- a/runners/CMakeLists.txt +++ b/runners/CMakeLists.txt @@ -1,5 +1,7 @@ add_subdirectory(activities) -add_subdirectory(baloo) +if(KF5Baloo_FOUND) + add_subdirectory(baloo) +endif() add_subdirectory(bookmarks) add_subdirectory(calculator) add_subdirectory(locations) diff --git a/runners/activities/plasma-runner-activityrunner.desktop b/runners/activities/plasma-runner-activityrunner.desktop --- a/runners/activities/plasma-runner-activityrunner.desktop +++ b/runners/activities/plasma-runner-activityrunner.desktop @@ -60,6 +60,7 @@ Name[zh_TW]=活動 Comment=List and switch between desktop activities Comment[ar]=اسرد أنشطة سطح المكتب وبدّل بينها +Comment[ast]=Llista y camuda actividaes d'escritoriu Comment[bg]=Списък и превключване между дейностите Comment[bs]=Prikaz i prebacivanje između aktivnosti radne površine Comment[ca]=Llista i commuta entre activitats d'escriptori diff --git a/runners/baloo/baloosearchrunner.cpp b/runners/baloo/baloosearchrunner.cpp --- a/runners/baloo/baloosearchrunner.cpp +++ b/runners/baloo/baloosearchrunner.cpp @@ -31,6 +31,7 @@ #include #include +#include #include @@ -86,6 +87,11 @@ QList SearchRunner::match(Plasma::RunnerContext& context, const QString& type, const QString& category) { + Baloo::IndexerConfig config; + if (!config.fileIndexingEnabled()) { + return QList(); + } + if (!context.isValid()) return QList(); diff --git a/runners/baloo/plasma-runner-baloosearch.desktop b/runners/baloo/plasma-runner-baloosearch.desktop --- a/runners/baloo/plasma-runner-baloosearch.desktop +++ b/runners/baloo/plasma-runner-baloosearch.desktop @@ -44,6 +44,7 @@ Name[zh_CN]=桌面搜索 Name[zh_TW]=桌面搜尋 Comment=Searches through files, emails and contacts +Comment[ast]=Guetes pente ficheros, correos y contautos Comment[ca]=Cerca pels fitxers, correus electrònics i contactes Comment[ca@valencia]=Cerca pels fitxers, correus electrònics i contactes Comment[cs]=Prohledává v souborech, emailech a kontaktech diff --git a/runners/bookmarks/bookmarksrunner.cpp b/runners/bookmarks/bookmarksrunner.cpp --- a/runners/bookmarks/bookmarksrunner.cpp +++ b/runners/bookmarks/bookmarksrunner.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -129,7 +130,7 @@ url.setScheme(QStringLiteral("http")); } - KToolInvocation::invokeBrowser(url.url()); + QDesktopServices::openUrl(url); } QMimeData * BookmarksRunner::mimeDataForMatch(const Plasma::QueryMatch &match) diff --git a/runners/calculator/plasma-runner-calculator.desktop b/runners/calculator/plasma-runner-calculator.desktop --- a/runners/calculator/plasma-runner-calculator.desktop +++ b/runners/calculator/plasma-runner-calculator.desktop @@ -82,6 +82,7 @@ Name[zh_TW]=計算機 Comment=Calculate expressions Comment[ar]=احسب التعابير +Comment[ast]=Calcula espresiones Comment[be@latin]=Padlik vyrazaŭ. Comment[bg]=Изчисления Comment[bs]=Računajte izraze diff --git a/runners/kill/plasma-runner-kill.desktop b/runners/kill/plasma-runner-kill.desktop --- a/runners/kill/plasma-runner-kill.desktop +++ b/runners/kill/plasma-runner-kill.desktop @@ -71,6 +71,7 @@ Name[zh_TW]=終端機應用程式 Comment=Stop applications that are currently running Comment[ar]=أوقف التطبيقات التي تعمل حاليًا +Comment[ast]=Para aplicaciones qu'anguaño tean n'execución Comment[bg]=Спиране на работещи в момента програми Comment[bs]=Zaustavlja programe trenutno u pogonu Comment[ca]=Atura aplicacions que estan actualment en execució diff --git a/runners/locations/plasma-runner-locations.desktop b/runners/locations/plasma-runner-locations.desktop --- a/runners/locations/plasma-runner-locations.desktop +++ b/runners/locations/plasma-runner-locations.desktop @@ -78,6 +78,7 @@ Name[zh_TW]=位置 Comment=File and URL opener Comment[ar]=فاتح ملفات ووصلات +Comment[ast]=Abridor de ficheros y URLs Comment[be@latin]=Adčynieńnie fajłaŭ i adrasoŭ Comment[bg]=Отваряне на файлове и адреси Comment[bn_IN]=ফাইল ও URL প্রদর্শক diff --git a/runners/places/plasma-runner-places.desktop b/runners/places/plasma-runner-places.desktop --- a/runners/places/plasma-runner-places.desktop +++ b/runners/places/plasma-runner-places.desktop @@ -80,6 +80,7 @@ Icon=user-home Comment=Open devices and folder bookmarks Comment[ar]=افتح علامات الأجهزة والمجلدات +Comment[ast]=Abre marcadores de preseos y carpetes Comment[bs]=Otvoreni uređaji i mape favorita Comment[ca]=Obre els punts dels dispositius i dels preferits Comment[ca@valencia]=Obri els punts dels dispositius i dels preferits diff --git a/runners/services/plasma-runner-services.desktop b/runners/services/plasma-runner-services.desktop --- a/runners/services/plasma-runner-services.desktop +++ b/runners/services/plasma-runner-services.desktop @@ -93,6 +93,7 @@ Name[zh_TW]=應用程式 Comment=Find applications, control panels and services Comment[ar]=اعثر على التطبيقات، لوحات التحكّم والخدمات +Comment[ast]=Alcuentra aplicaciones, paneles de control y servicios Comment[be@latin]=Pošuk aplikacyjaŭ, panelaŭ kiravańnia j słužbaŭ Comment[bg]=Търсене на програми, панели и услуги Comment[bs]=Tražite programe, kontrolne panele i servise diff --git a/runners/sessions/sessionrunner.cpp b/runners/sessions/sessionrunner.cpp --- a/runners/sessions/sessionrunner.cpp +++ b/runners/sessions/sessionrunner.cpp @@ -150,7 +150,6 @@ } } - qDebug() << "session switching to" << (listAll ? QStringLiteral("all sessions") : term); bool switchUser = listAll || term.compare(i18n("switch user"), Qt::CaseInsensitive) == 0 || term.compare(i18n("new session"), Qt::CaseInsensitive) == 0; diff --git a/runners/shell/plasma-runner-shell.desktop b/runners/shell/plasma-runner-shell.desktop --- a/runners/shell/plasma-runner-shell.desktop +++ b/runners/shell/plasma-runner-shell.desktop @@ -48,7 +48,7 @@ Name[mr]=आदेश ओळ Name[nb]=Kommandolinje Name[nds]=Konsool -Name[nl]=Commandoregel +Name[nl]=Opdrachtregel Name[nn]=Kommandolinje Name[oc]=Linha de comanda Name[or]=ନିର୍ଦ୍ଦେଶନାମା @@ -80,6 +80,7 @@ Name[zh_TW]=命令列 Comment=Executes shell commands Comment[ar]=نفّذ أوامر الصَدفة +Comment[ast]=Executa comandos de shell Comment[be@latin]=Vykanańnie zahadaŭ abałonki. Comment[bg]=Изпълнение на команди на обвивката Comment[bn]=শেল কমাণ্ড চালায় diff --git a/runners/webshortcuts/plasma-runner-webshortcuts.desktop b/runners/webshortcuts/plasma-runner-webshortcuts.desktop --- a/runners/webshortcuts/plasma-runner-webshortcuts.desktop +++ b/runners/webshortcuts/plasma-runner-webshortcuts.desktop @@ -72,6 +72,7 @@ Name[zh_TW]=網頁捷徑 Comment=Allows user to use Konqueror's web shortcuts Comment[ar]=يسمح للمستخدِم باستخدام اختصارات كنكر للوِب +Comment[ast]=Permite al usuariu usar los atayos web de Konqueror Comment[bs]=Dozvoli koristenje Koquerorovih web prečica Comment[ca]=Permet que l'usuari utilitzi les dreceres web del Konqueror Comment[ca@valencia]=Permet que l'usuari utilitze les dreceres web del Konqueror diff --git a/runners/windowedwidgets/plasma-runner-windowedwidgets.desktop b/runners/windowedwidgets/plasma-runner-windowedwidgets.desktop --- a/runners/windowedwidgets/plasma-runner-windowedwidgets.desktop +++ b/runners/windowedwidgets/plasma-runner-windowedwidgets.desktop @@ -63,6 +63,7 @@ Comment=Find Plasma widgets that can be run as standalone windows Comment[ar]=اعثر على ودجات بلازما التي يمكن أن تعمل كنوافذ مستقلّة +Comment[ast]=Alcuentra widgets de Plasma que puean executase como ventanes independientes Comment[bg]=Търсене на джаджи, които могат да се използват като самостоятелни прозорци Comment[bs]=Nađite plazma grafičke kontrole koje mogu da rade kao samostalni prozori Comment[ca]=Cerca estris del Plasma que es poden executar en finestres autònomes diff --git a/runners/windowedwidgets/windowedwidgetsrunner.h b/runners/windowedwidgets/windowedwidgetsrunner.h --- a/runners/windowedwidgets/windowedwidgetsrunner.h +++ b/runners/windowedwidgets/windowedwidgetsrunner.h @@ -48,7 +48,7 @@ protected: - void setupMatch(const KService::Ptr &service, Plasma::QueryMatch &action); + void setupMatch(const KPluginMetaData &md, Plasma::QueryMatch &action); }; #endif diff --git a/runners/windowedwidgets/windowedwidgetsrunner.cpp b/runners/windowedwidgets/windowedwidgetsrunner.cpp --- a/runners/windowedwidgets/windowedwidgetsrunner.cpp +++ b/runners/windowedwidgets/windowedwidgetsrunner.cpp @@ -60,37 +60,33 @@ QList matches; - - foreach (const KPluginInfo &info, Plasma::PluginLoader::self()->listAppletInfo(QString())) { - KService::Ptr service = info.service(); - if (!service->isValid()) { + foreach (const KPluginMetaData &md, Plasma::PluginLoader::self()->listAppletMetaData(QString())) { + if (!md.isValid()) { continue; } - if (((service->name().contains(term, Qt::CaseInsensitive) || - service->genericName().contains(term, Qt::CaseInsensitive) || - service->comment().contains(term, Qt::CaseInsensitive)) || - service->categories().contains(term, Qt::CaseInsensitive) || + if (((md.name().contains(term, Qt::CaseInsensitive) || + md.value(QLatin1String("GenericName")).contains(term, Qt::CaseInsensitive) || + md.description().contains(term, Qt::CaseInsensitive)) || + md.category().contains(term, Qt::CaseInsensitive) || term.startsWith(i18nc("Note this is a KRunner keyword", "mobile applications"))) && - !info.property(QStringLiteral("NoDisplay")).toBool()) { + !md.rawData().value(QStringLiteral("NoDisplay")).toBool()) { - QVariant val = info.property(QStringLiteral("X-Plasma-StandAloneApp")); + QVariant val = md.value(QStringLiteral("X-Plasma-StandAloneApp")); if (!val.isValid() || !val.toBool()) { continue; } Plasma::QueryMatch match(this); - setupMatch(service, match); - if (service->name().compare(term, Qt::CaseInsensitive) == 0) { + setupMatch(md, match); + if (md.name().compare(term, Qt::CaseInsensitive) == 0) { match.setType(Plasma::QueryMatch::ExactMatch); match.setRelevance(1); } else { match.setType(Plasma::QueryMatch::PossibleMatch); match.setRelevance(0.7); } matches << match; - - qDebug() << service; } } @@ -104,27 +100,27 @@ void WindowedWidgetsRunner::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match) { Q_UNUSED(context); - KService::Ptr service = KService::serviceByStorageId(match.data().toString()); - if (service) { - QProcess::startDetached(QStringLiteral("plasmawindowed"), QStringList() << service->property(QStringLiteral("X-KDE-PluginInfo-Name"), QVariant::String).toString()); + KPluginMetaData md(match.data().toString()); + if (md.isValid()) { + QProcess::startDetached(QStringLiteral("plasmawindowed"), QStringList() << md.pluginId()); } } -void WindowedWidgetsRunner::setupMatch(const KService::Ptr &service, Plasma::QueryMatch &match) +void WindowedWidgetsRunner::setupMatch(const KPluginMetaData &md, Plasma::QueryMatch &match) { - const QString name = service->name(); + const QString name = md.pluginId(); match.setText(name); - match.setData(service->storageId()); + match.setData(md.metaDataFileName()); - if (!service->genericName().isEmpty() && service->genericName() != name) { - match.setSubtext(service->genericName()); - } else if (!service->comment().isEmpty()) { - match.setSubtext(service->comment()); + if (!md.name().isEmpty() && md.name() != name) { + match.setSubtext(md.name()); + } else if (!md.description().isEmpty()) { + match.setSubtext(md.description()); } - if (!service->icon().isEmpty()) { - match.setIconName(service->icon()); + if (!md.iconName().isEmpty()) { + match.setIconName(md.iconName()); } } diff --git a/runners/windows/plasma-runner-windows.desktop b/runners/windows/plasma-runner-windows.desktop --- a/runners/windows/plasma-runner-windows.desktop +++ b/runners/windows/plasma-runner-windows.desktop @@ -72,6 +72,7 @@ Name[zh_TW]=視窗 Comment=List windows and desktops and switch them Comment[ar]=اسرد النوافذ وأسطح المكتب وبدّل بينها +Comment[ast]=Llista ventanes y escritorios y camuda ente ellos Comment[bg]=Показване и превключване на прозорци и работни плотове Comment[bs]=Nabrajanje i prebacivanje između prozora i površî Comment[ca]=Llista finestres i escriptoris i canvia entre ells diff --git a/runners/windows/windowsrunner.cpp b/runners/windows/windowsrunner.cpp --- a/runners/windows/windowsrunner.cpp +++ b/runners/windows/windowsrunner.cpp @@ -75,7 +75,7 @@ } foreach (const WId w, KWindowSystem::windows()) { - KWindowInfo info = KWindowSystem::windowInfo(w, NET::WMWindowType | NET::WMDesktop | + KWindowInfo info(w, NET::WMWindowType | NET::WMDesktop | NET::WMState | NET::XAWMState | NET::WMName, NET::WM2WindowClass | NET::WM2WindowRole | NET::WM2AllowedActions); diff --git a/sddm-theme/Background.qml b/sddm-theme/Background.qml --- a/sddm-theme/Background.qml +++ b/sddm-theme/Background.qml @@ -1,45 +1,67 @@ -/*************************************************************************** -* Copyright (c) 2013 Abdurrahman AVCI -* -* Permission is hereby granted, free of charge, to any person -* obtaining a copy of this software and associated documentation -* files (the "Software"), to deal in the Software without restriction, -* including without limitation the rights to use, copy, modify, merge, -* publish, distribute, sublicense, and/or sell copies of the Software, -* and to permit persons to whom the Software is furnished to do so, -* subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR -* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE -* OR OTHER DEALINGS IN THE SOFTWARE. -* -***************************************************************************/ +/* + * Copyright 2016 Boudhayan Gupta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) any later version. + * + * This program 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 General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ import QtQuick 2.2 FocusScope { - property alias source: image.source - property alias fillMode: image.fillMode - property alias status: image.status + id: sceneBackground + + property var sceneBackgroundType + property alias sceneBackgroundColor: sceneColorBackground.color + property alias sceneBackgroundImage: sceneImageBackground.source Rectangle { + id: sceneColorBackground anchors.fill: parent - color: "#1d99f3" } Image { - id: image + id: sceneImageBackground anchors.fill: parent - - clip: true - focus: true - smooth: true + fillMode: Image.PreserveAspectCrop + smooth: true; } + + states: [ + State { + name: "imageBackground" + when: sceneBackgroundType == "image" + PropertyChanges { + target: sceneColorBackground + visible: false + } + PropertyChanges { + target: sceneImageBackground + visible: true + } + }, + State { + name: "colorBackground" + when: sceneBackgroundType != "image" + PropertyChanges { + target: sceneColorBackground + visible: true + } + PropertyChanges { + target: sceneImageBackground + visible: false + } + } + ] } diff --git a/sddm-theme/Main.qml b/sddm-theme/Main.qml --- a/sddm-theme/Main.qml +++ b/sddm-theme/Main.qml @@ -50,9 +50,10 @@ model: screenModel Background { - x: geometry.x; y: geometry.y; width: geometry.width; height:geometry.height - source: config.background - fillMode: Image.PreserveAspectCrop + x: geometry.x; y: geometry.y; width: geometry.width; height: geometry.height + sceneBackgroundType: config.type + sceneBackgroundColor: config.color + sceneBackgroundImage: config.background } } diff --git a/sddm-theme/theme.conf b/sddm-theme/theme.conf --- a/sddm-theme/theme.conf +++ b/sddm-theme/theme.conf @@ -1,2 +1,4 @@ [General] +type=color +color=#1d99f3 background= diff --git a/shell/CMakeLists.txt b/shell/CMakeLists.txt --- a/shell/CMakeLists.txt +++ b/shell/CMakeLists.txt @@ -59,7 +59,6 @@ shellmanager.cpp standaloneappcorona osd.cpp - waylanddialogfilter.cpp coronatesthelper.cpp debug.cpp screenpool.cpp diff --git a/shell/alternativeshelper.cpp b/shell/alternativeshelper.cpp --- a/shell/alternativeshelper.cpp +++ b/shell/alternativeshelper.cpp @@ -38,12 +38,12 @@ QStringList AlternativesHelper::appletProvides() const { - return m_applet->pluginInfo().property(QStringLiteral("X-Plasma-Provides")).toStringList(); + return KPluginMetaData::readStringList(m_applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); } QString AlternativesHelper::currentPlugin() const { - return m_applet->pluginInfo().pluginName(); + return m_applet->pluginMetaData().pluginId(); } QQuickItem *AlternativesHelper::applet() const @@ -53,7 +53,7 @@ void AlternativesHelper::loadAlternative(const QString &plugin) { - if (plugin == m_applet->pluginInfo().pluginName() || m_applet->isContainment()) { + if (plugin == m_applet->pluginMetaData().pluginId() || m_applet->isContainment()) { return; } diff --git a/shell/containmentconfigview.cpp b/shell/containmentconfigview.cpp --- a/shell/containmentconfigview.cpp +++ b/shell/containmentconfigview.cpp @@ -104,7 +104,7 @@ QString ContainmentConfigView::containmentPlugin() const { - return m_containment->pluginInfo().pluginName(); + return m_containment->pluginMetaData().pluginId(); } void ContainmentConfigView::setContainmentPlugin(const QString &plugin) diff --git a/shell/desktopview.cpp b/shell/desktopview.cpp --- a/shell/desktopview.cpp +++ b/shell/desktopview.cpp @@ -217,6 +217,7 @@ switch (pe->surfaceEventType()) { case QPlatformSurfaceEvent::SurfaceCreated: setupWaylandIntegration(); + ensureWindowType(); break; case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed: delete m_shellSurface; diff --git a/shell/panelview.h b/shell/panelview.h --- a/shell/panelview.h +++ b/shell/panelview.h @@ -27,6 +27,7 @@ #include "plasmaquick/configview.h" class ShellCorona; +class PanelConfigView; namespace KWayland { @@ -152,6 +153,8 @@ void setScreenToFollow(QScreen* screen); QScreen* screenToFollow() const; + PanelConfigView *panelConfigView() const; + protected: void resizeEvent(QResizeEvent *ev) override; void showEvent(QShowEvent *event) override; diff --git a/shell/panelview.cpp b/shell/panelview.cpp --- a/shell/panelview.cpp +++ b/shell/panelview.cpp @@ -703,6 +703,11 @@ return m_screenToFollow; } +PanelConfigView *PanelView::panelConfigView() const +{ + return qobject_cast(m_panelConfigView.data()); +} + void PanelView::adaptToScreen() { emit screenToFollowChanged(m_screenToFollow); diff --git a/shell/scripting/containment.cpp b/shell/scripting/containment.cpp --- a/shell/scripting/containment.cpp +++ b/shell/scripting/containment.cpp @@ -248,7 +248,7 @@ int count = 0; foreach (Plasma::Applet *widget, c->d->containment.data()->applets()) { - if (widgetType.isEmpty() || widget->pluginInfo().pluginName() == widgetType) { + if (widgetType.isEmpty() || widget->pluginMetaData().pluginId() == widgetType) { widgets.setProperty(count, env->wrap(widget)); ++count; } @@ -273,7 +273,7 @@ return QString(); } - return d->containment.data()->pluginInfo().pluginName(); + return d->containment.data()->pluginMetaData().pluginId(); } void Containment::remove() diff --git a/shell/scripting/widget.cpp b/shell/scripting/widget.cpp --- a/shell/scripting/widget.cpp +++ b/shell/scripting/widget.cpp @@ -65,7 +65,7 @@ QString Widget::type() const { if (d->applet) { - return d->applet.data()->pluginInfo().pluginName(); + return d->applet.data()->pluginMetaData().pluginId(); } return QString(); diff --git a/shell/shellcorona.h b/shell/shellcorona.h --- a/shell/shellcorona.h +++ b/shell/shellcorona.h @@ -113,10 +113,6 @@ QString defaultContainmentPlugin() const; - -protected: - bool eventFilter(QObject *watched, QEvent *event) override; - public Q_SLOTS: /** * Request saving applicationConfig on disk, it's event compressed, not immediate diff --git a/shell/shellcorona.cpp b/shell/shellcorona.cpp --- a/shell/shellcorona.cpp +++ b/shell/shellcorona.cpp @@ -976,7 +976,9 @@ QRegion r = view->geometry(); foreach (const PanelView *v, m_panelViews) { - if (v->isVisible() && view->screen() == v->screen() && v->visibilityMode() != PanelView::AutoHide) { + if (v->isVisible() && view->screen() == v->screen() && + ((v->panelConfigView() && v->panelConfigView()->visibilityMode() != PanelView::AutoHide ) || + (!v->panelConfigView() && v->visibilityMode() != PanelView::AutoHide))) { //if the panel is being moved around, we still want to calculate it from the edge r -= v->geometryByDistance(0); } @@ -997,7 +999,9 @@ QRect r = view->geometry(); foreach (PanelView *v, m_panelViews) { - if (v->isVisible() && v->screen() == view->screen() && v->visibilityMode() != PanelView::AutoHide) { + if (v->isVisible() && v->screen() == view->screen() && + ((v->panelConfigView() && v->panelConfigView()->visibilityMode() != PanelView::AutoHide ) || + (!v->panelConfigView() && v->visibilityMode() != PanelView::AutoHide))) { switch (v->location()) { case Plasma::Types::LeftEdge: r.setLeft(r.left() + v->thickness()); diff --git a/shell/waylanddialogfilter.h b/shell/waylanddialogfilter.h deleted file mode 100644 --- a/shell/waylanddialogfilter.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2015 Marco Martin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef WAYLANDDIALOGFILTER_H -#define WAYLANDDIALOGFILTER_H - -#include - -#include -#include - -namespace KWayland -{ - namespace Client - { - class PlasmaShellSurface; - } -} - -class ShellCorona; - -class WaylandDialogFilter : public QObject -{ - Q_OBJECT -public: - WaylandDialogFilter(ShellCorona *c, QWindow *parent = 0); - ~WaylandDialogFilter() override; - - static void install(QWindow *dialog, ShellCorona *c); - -protected: - bool eventFilter(QObject *watched, QEvent *event) override; - -private: - void setupWaylandIntegration(ShellCorona *c); - - QPointer m_dialog; - QPointer m_shellSurface; -}; - -#endif diff --git a/shell/waylanddialogfilter.cpp b/shell/waylanddialogfilter.cpp deleted file mode 100644 --- a/shell/waylanddialogfilter.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2015 Marco Martin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "waylanddialogfilter.h" -#include "shellcorona.h" -#include "panelshadows_p.h" - -#include -#include - -#include -#include - - -class DialogShadows : public PanelShadows { -public: - explicit DialogShadows(QObject *parent = 0) - : PanelShadows(parent, QStringLiteral("dialogs/background")) - {} - - static DialogShadows *self(); -}; - -class DialogShadowsSingleton -{ -public: - DialogShadowsSingleton() - { - } - - DialogShadows self; -}; - -Q_GLOBAL_STATIC(DialogShadowsSingleton, privateDialogShadowsSelf) - -DialogShadows *DialogShadows::self() -{ - return &privateDialogShadowsSelf->self; -} - - - -WaylandDialogFilter::WaylandDialogFilter(ShellCorona *c, QWindow *parent) - : QObject(parent), - m_dialog(parent) -{ - parent->installEventFilter(this); - setupWaylandIntegration(c); -} - -WaylandDialogFilter::~WaylandDialogFilter() -{ - DialogShadows::self()->removeWindow(m_dialog); -} - -void WaylandDialogFilter::install(QWindow *dialog, ShellCorona *c) -{ - new WaylandDialogFilter(c, dialog); -} - -bool WaylandDialogFilter::eventFilter(QObject *watched, QEvent *event) -{ - if (event->type() == QEvent::Move) { - QMoveEvent *me = static_cast(event); - if (m_shellSurface) { - m_shellSurface->setPosition(me->pos()); - } - } else if (event->type() == QEvent::Show) { - if (m_dialog == watched) { - Plasma::FrameSvg::EnabledBorders enabledBorders = Plasma::FrameSvg::AllBorders; - Plasma::FrameSvg *background = m_dialog->property("__plasma_frameSvg").value(); - if (background) { - enabledBorders = background->enabledBorders(); - } - DialogShadows::self()->addWindow(m_dialog, enabledBorders); - } - } - return QObject::eventFilter(watched, event); -} - -void WaylandDialogFilter::setupWaylandIntegration(ShellCorona *c) -{ - if (m_shellSurface) { - // already setup - return; - } - if (c) { - using namespace KWayland::Client; - PlasmaShell *interface = c->waylandPlasmaShellInterface(); - if (!interface) { - return; - } - Surface *s = Surface::fromWindow(m_dialog); - if (!s) { - return; - } - m_shellSurface = interface->createSurface(s, this); - } -} - -#include "moc_waylanddialogfilter.cpp" diff --git a/solidautoeject/solidautoeject.desktop b/solidautoeject/solidautoeject.desktop --- a/solidautoeject/solidautoeject.desktop +++ b/solidautoeject/solidautoeject.desktop @@ -6,6 +6,7 @@ X-KDE-Kded-autoload=true Name=Drive Ejector Name[ar]=مُخرِج الأجهزة +Name[ast]=Estrautor de preseos Name[bg]=Изваждане на устройства Name[bs]=Izbacivač jedinica Name[ca]=Expulsor d'unitats @@ -71,6 +72,7 @@ Name[zh_TW]=碟片跳出管理 Comment=Automatically releases drives when their eject button is pushed Comment[ar]=أطلِق الأجهزة آليًا عندما يُضغَط زر الإخراج +Comment[ast]=Estrái unidaes de mou automáticu al primir el botón d'estraición Comment[bg]=Автоматично освобождаване на устройствата при натискане на бутона за изваждане Comment[bs]=Automatsko oslobađanje jedinica kada im se prisne dugme za izbacivanje Comment[ca]=Allibera unitats automàticament quan es prem el seu botó d'expulsió diff --git a/startkde/kcminit/main.cpp b/startkde/kcminit/main.cpp --- a/startkde/kcminit/main.cpp +++ b/startkde/kcminit/main.cpp @@ -256,6 +256,7 @@ startup = ( strcmp( argv[ 0 ], "kcminit_startup" ) == 0 ); // started from startkde? KLocalizedString::setApplicationDomain("kcminit"); + QGuiApplication::setDesktopSettingsAware(false); QGuiApplication app(argc, argv); //gui is needed for several modules KAboutData about(QStringLiteral("kcminit"), i18n("KCMInit"), QString(), i18n("KCMInit - runs startup initialization for Control Modules."), KAboutLicense::GPL); diff --git a/startkde/startkde.cmake b/startkde/startkde.cmake --- a/startkde/startkde.cmake +++ b/startkde/startkde.cmake @@ -3,33 +3,10 @@ # DEFAULT KDE STARTUP SCRIPT ( @PROJECT_VERSION@ ) # -if test "x$1" = x--failsafe; then - KDE_FAILSAFE=1 # General failsafe flag - KWIN_COMPOSE=N # Disable KWin's compositing - QT_XCB_FORCE_SOFTWARE_OPENGL=1 - export KWIN_COMPOSE KDE_FAILSAFE QT_XCB_FORCE_SOFTWARE_OPENGL -fi - # When the X server dies we get a HUP signal from xinit. We must ignore it # because we still need to do some cleanup. trap 'echo GOT SIGHUP' HUP -# we have to unset this for Darwin since it will screw up KDE's dynamic-loading -unset DYLD_FORCE_FLAT_NAMESPACE - -# in case we have been started with full pathname spec without being in PATH -bindir=`echo "$0" | sed -n 's,^\(/.*\)/[^/][^/]*$,\1,p'` -if [ -n "$bindir" ]; then - qbindir=`qtpaths --binaries-dir` - qdbus=$qbindir/qdbus - case $PATH in - $bindir|$bindir:*|*:$bindir|*:$bindir:*) ;; - *) PATH=$bindir:$PATH; export PATH;; - esac -else - qdbus=qdbus -fi - # Check if a KDE session already is running and whether it's possible to connect to X kcheckrunning kcheckrunning_result=$? @@ -102,38 +79,6 @@ EOF } -# Make sure the Oxygen font is installed -# This is necessary for setups where CMAKE_INSTALL_PREFIX -# is not in /usr. fontconfig looks in /usr, ~/.fonts and -# $XDG_DATA_HOME for fonts. In this case, we symlink the -# Oxygen font under ${XDG_DATA_HOME} and make it known to -# fontconfig - -usr_share="/usr/share" -install_share="@KDE_INSTALL_FULL_DATADIR@" - -if [ ! $install_share = $usr_share ]; then - - if [ ${XDG_DATA_HOME} ]; then - fontsDir="${XDG_DATA_HOME}/fonts" - else - fontsDir="${HOME}/.fonts" - fi - - test -d $fontsDir || { - mkdir -p $fontsDir - } - - oxygenDir=$fontsDir/oxygen - prefixDir="@KDE_INSTALL_FULL_DATADIR@/fonts/oxygen" - - # if the oxygen dir doesn't exist, create a symlink to be sure that the - # Oxygen font is available to the user - test -d $oxygenDir || test -d $prefixDir && { - test -h $oxygenDir || ln -s $prefixDir $oxygenDir && fc-cache $oxygenDir - } -fi - kstartupconfig5 returncode=$? if test $returncode -ne 0; then @@ -205,8 +150,7 @@ # For anything else (that doesn't set env vars, or that needs a window manager), # better use the Autostart folder. -# TODO: Use GenericConfigLocation once we depend on Qt 5.4 -scriptpath=`qtpaths --paths ConfigLocation | tr ':' '\n' | sed 's,$,/plasma-workspace,g'` +scriptpath=`qtpaths --locate-dirs GenericConfigLocation plasma-workspace | tr ':' '\n'` # Add /env/ to the directory to locate the scripts to be sourced for prefix in `echo $scriptpath`; do @@ -287,17 +231,6 @@ fi export XDG_DATA_DIRS -# Make sure that D-Bus is running -if $qdbus >/dev/null 2>/dev/null; then - : # ok -else - echo 'startkde: Could not start D-Bus. Can you call qdbus?' 1>&2 - test -n "$ksplash_pid" && kill "$ksplash_pid" 2>/dev/null - xmessage -geometry 500x100 "Could not start D-Bus. Can you call qdbus?" - exit 1 -fi - - # Mark that full KDE session is running (e.g. Konqueror preloading works only # with full KDE running). The KDE_FULL_SESSION property can be detected by # any X client connected to the same X session, even if not launched @@ -359,7 +292,7 @@ exit 1 fi -$qdbus org.kde.KSplash /KSplash org.kde.KSplash.setStage kinit +qdbus org.kde.KSplash /KSplash org.kde.KSplash.setStage kinit & # finally, give the session control to the session manager # see kdebase/ksmserver for the description of the rest of the startup sequence @@ -384,19 +317,21 @@ xmessage -geometry 500x100 "Could not start ksmserver. Check your installation." fi +#Anything after here is logout/shutdown + wait_drkonqi=`kreadconfig5 --file startkderc --group WaitForDrKonqi --key Enabled --default true` if test x"$wait_drkonqi"x = x"true"x ; then # wait for remaining drkonqi instances with timeout (in seconds) wait_drkonqi_timeout=`kreadconfig5 --file startkderc --group WaitForDrKonqi --key Timeout --default 900` wait_drkonqi_counter=0 - while $qdbus | grep "^[^w]*org.kde.drkonqi" > /dev/null ; do + while qdbus | grep "^[^w]*org.kde.drkonqi" > /dev/null ; do sleep 5 wait_drkonqi_counter=$((wait_drkonqi_counter+5)) if test "$wait_drkonqi_counter" -ge "$wait_drkonqi_timeout" ; then # ask remaining drkonqis to die in a graceful way - $qdbus | grep 'org.kde.drkonqi-' | while read address ; do - $qdbus "$address" "/MainApplication" "quit" + qdbus | grep 'org.kde.drkonqi-' | while read address ; do + qdbus "$address" "/MainApplication" "quit" done break fi diff --git a/systemmonitor/kdedksysguard.cpp b/systemmonitor/kdedksysguard.cpp --- a/systemmonitor/kdedksysguard.cpp +++ b/systemmonitor/kdedksysguard.cpp @@ -38,6 +38,7 @@ registerPlugin();) KDEDKSysGuard::KDEDKSysGuard(QObject* parent, const QVariantList&) + : KDEDModule(parent) { QTimer::singleShot(0, this, &KDEDKSysGuard::init); } diff --git a/templates/ion-dataengine/ion-dataengine.kdevtemplate b/templates/ion-dataengine/ion-dataengine.kdevtemplate --- a/templates/ion-dataengine/ion-dataengine.kdevtemplate +++ b/templates/ion-dataengine/ion-dataengine.kdevtemplate @@ -33,13 +33,13 @@ Comment[nl]=Een speciale Plasma sub-dataengine voor de Plasma weer-dataengine, die toegang biedt tot een serviceprovider van weergegevens Comment[nn]=Ein spesiell Plasma underdatamotor for vêrdatamotoren i Plasma, som gjev tilgang til ein leverandør av vêrdata Comment[pl]=Specjalny podsilnik danych Plazmy dla silnika danych pogody Plazmy, zapewniający dostęp do danych usługi pogodowej -Comment[pt]=Um sub-motor de dados especial do Plasma para o motor de dados meteorológicos do Plasma, que fornece o acesso a um fornecedor de serviços de dados meteorológicos +Comment[pt]=Um sub-motor de dados especial do Plasma para o motor de dados meteorológicos, oferecendo o acesso a um fornecedor de serviços de dados meteorológicos Comment[sk]=Špeciálny dátový engine Plasma pre dátový engine Plasma Weather, poskytujúci prístup k jednému poskytovateľovi údajov počasia Comment[sl]=Posebni podatkovni podpogon za vremenski podatkovni pogon Plasme, ki ponuja dostop do enega ponudnika podatkov o vremenu Comment[sv]=Ett särskilt undergränssnitt för Plasmas datagränssnitt för väder, som ger tillgång till en datatjänstleverantör av väder Comment[uk]=Спеціалізований допоміжний рушій даних Плазми для рушія даних прогнозу погоди, який надає доступ до даних одного з надавачів даних прогнозу погоди Comment[x-test]=xxA special Plasma sub-dataengine for the Plasma Weather dataengine, providing access to one weather data service providerxx Comment[zh_CN]=一个特殊的 Plasma 子数据引擎,用于辅助 Plasma 天气数据引擎,提供了对一个天气数据服务提供商的访问功能 -Comment[zh_TW]=一個專為 Plasma 天氣資料引擎所設計的 Plasma 子資料引擎,提供存取一個天氣資料服務提供者的能力 +Comment[zh_TW]=一個特殊的 Plasma 天氣資料引擎的子引擎,提供對一個天氣資料服務提供者的存取 ShowFilesAfterGeneration=%{PROJECTDIR}/src/ion-%{APPNAMELC}.cpp Category=Plasma/Dataengine diff --git a/wallpapers/image/image.cpp b/wallpapers/image/image.cpp --- a/wallpapers/image/image.cpp +++ b/wallpapers/image/image.cpp @@ -228,9 +228,15 @@ void Image::setTargetSize(const QSize &size) { + bool sizeChanged = m_targetSize != size; m_targetSize = size; if (m_mode == SingleImage) { + if (sizeChanged) { + // If screen size was changed, we may want to select a new preferred image + // which has correct aspect ratio for the new screen size. + m_wallpaperPackage.removeDefinition("preferred"); + } setSingleImage(); } }