diff --git a/CMakeLists.txt b/CMakeLists.txt
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,7 +19,7 @@
PURPOSE "Used by the HTML-based GUI ksysguard library"
)
-find_package(KF5 REQUIRED COMPONENTS CoreAddons Config I18n WindowSystem Completion Auth WidgetsAddons IconThemes ConfigWidgets Service)
+find_package(KF5 REQUIRED COMPONENTS CoreAddons Config I18n WindowSystem Completion Auth WidgetsAddons IconThemes ConfigWidgets Service Plasma GlobalAccel KIO)
find_package(KF5 OPTIONAL_COMPONENTS Plasma)
set_package_properties(KF5Plasma PROPERTIES
URL "https://cgit.kde.org/plasma-framework.git/"
diff --git a/processui/CMakeLists.txt b/processui/CMakeLists.txt
--- a/processui/CMakeLists.txt
+++ b/processui/CMakeLists.txt
@@ -44,6 +44,9 @@
KF5::ConfigWidgets
KF5::WidgetsAddons
KF5::IconThemes
+ KF5::GlobalAccel
+ KF5::Service
+ KF5::KIOWidgets
)
target_include_directories(processui
PUBLIC
diff --git a/processui/ProcessWidgetUI.ui b/processui/ProcessWidgetUI.ui
--- a/processui/ProcessWidgetUI.ui
+++ b/processui/ProcessWidgetUI.ui
@@ -6,20 +6,29 @@
0
0
- 490
+ 498
472
-
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
0
-
-
-
+
- false
+ true
@@ -41,13 +50,16 @@
&End Process...
-
+
+ Qt::ToolButtonTextBesideIcon
+
+
false
-
+
false
-
+
false
@@ -68,7 +80,7 @@
Quick search
-
+
true
@@ -135,6 +147,16 @@
+ -
+
+
+ Tools
+
+
+
+
+
+
-
diff --git a/processui/ksysguardprocesslist.cpp b/processui/ksysguardprocesslist.cpp
--- a/processui/ksysguardprocesslist.cpp
+++ b/processui/ksysguardprocesslist.cpp
@@ -56,6 +56,9 @@
#include
#include
#include
+#include
+#include
+#include
#include "ReniceDlg.h"
#include "ui_ProcessWidgetUI.h"
@@ -68,6 +71,7 @@
//#define DO_MODELCHECK
#ifdef DO_MODELCHECK
#include "modeltest.h"
+class KGlobalAccel;
#endif
class ProgressBarItemDelegate : public QStyledItemDelegate
{
@@ -192,7 +196,7 @@
struct KSysGuardProcessListPrivate {
KSysGuardProcessListPrivate(KSysGuardProcessList* q, const QString &hostName)
- : mModel(q, hostName), mFilterModel(q), mUi(new Ui::ProcessWidget()), mProcessContextMenu(nullptr), mUpdateTimer(nullptr)
+ : mModel(q, hostName), mFilterModel(q), mUi(new Ui::ProcessWidget()), mProcessContextMenu(nullptr), mUpdateTimer(nullptr), mToolsMenu(new QMenu(q))
{
mScripting = nullptr;
mNeedToExpandInit = false;
@@ -285,6 +289,8 @@
QAction *sigKill;
QAction *sigUsr1;
QAction *sigUsr2;
+
+ QMenu* mToolsMenu;
};
KSysGuardProcessList::KSysGuardProcessList(QWidget* parent, const QString &hostName)
@@ -357,7 +363,6 @@
d->mUi->treeView->setDragEnabled(true);
d->mUi->treeView->setDragDropMode(QAbstractItemView::DragOnly);
-
//Sort by username by default
d->mUi->treeView->sortByColumn(ProcessModel::HeadingUser, Qt::AscendingOrder);
@@ -376,8 +381,79 @@
retranslateUi();
+ d->mUi->btnKillProcess->setEnabled(false);
d->mUi->btnKillProcess->setIcon(QIcon::fromTheme(QStringLiteral("process-stop")));
- d->mUi->btnKillProcess->setToolTip(i18n("End the selected process. Warning - you may lose unsaved work.
Right click on a process to send other signals.
See What's This for technical information.
To target a specific window to kill, press Ctrl+Alt+Esc at any time."));
+ d->mUi->btnKillProcess->setToolTip(i18n("End the selected process. Warning - you may lose unsaved work.
Right click on a process to send other signals.
See What's This for technical information."));
+
+ auto d1 = d;
+ auto addByDesktopName = [this, d1](const QString& desktopName)
+ {
+ auto d = d1;
+ auto kService = KService::serviceByDesktopName(desktopName);
+ if (kService != nullptr) {
+ auto action = new QAction(QIcon::fromTheme(kService->icon()),
+ kService->name(), nullptr);
+
+ connect(action, &QAction::triggered, this,
+ [kService](bool) {
+ KRun::runService(*kService, { }, nullptr);
+ });
+ d->mToolsMenu->addAction(action);
+ }
+ };
+
+ addByDesktopName(QStringLiteral("org.kde.konsole"));
+
+ // QCoreApplication::applicationFilePath() returns something like "/usr/bin/ksysguard"
+ // when this view is embedded in KSysGuard.
+ // And in this case we do not add the KSysGuard item to the menu.
+ if (!QCoreApplication::applicationFilePath().contains(QStringLiteral("ksysguard"))) {
+ addByDesktopName(QStringLiteral("org.kde.ksysguard"));
+ }
+
+ addByDesktopName(QStringLiteral("org.kde.ksystemlog"));
+ addByDesktopName(QStringLiteral("org.kde.kinfocenter"));
+ addByDesktopName(QStringLiteral("org.kde.filelight"));
+ addByDesktopName(QStringLiteral("sweeper"));
+ addByDesktopName(QStringLiteral("kmag"));
+ addByDesktopName(QStringLiteral("htop"));
+
+ {
+ auto action = new QAction(i18n("Run Command"), this);
+ action->setIcon(QIcon::fromTheme(QStringLiteral("system-run")));
+ connect(action, &QAction::triggered, this, [](){
+ KRun::runCommand(QStringLiteral("krunner"), nullptr);
+ });
+ d->mToolsMenu->addAction(action);
+ }
+
+ {
+ // find shortcut of xkill functionality which is defined in KWin
+ auto killWindowKeys = KGlobalAccel::self()->globalShortcut(QStringLiteral("kwin"), QStringLiteral("Kill Window"));
+ QString killWindowShortcut = i18nc("the keyboard shortcut of the Kill Window function", "not set");
+ if (killWindowKeys.size() > 0) {
+ killWindowShortcut = killWindowKeys[0].toString();
+ }
+ auto killWindowAction = new QAction(QIcon::fromTheme(QStringLiteral("document-close")),
+ i18nc("%1 is a keyboard shortcut", "Kill a window (%1)", killWindowShortcut), this);
+
+ // Alternative to using xkill directly. The KWin method also allows to press Esc to abort.
+ auto killWindowKwinMethod = new QDBusInterface(QStringLiteral("org.kde.KWin"), QStringLiteral("/KWin"), QStringLiteral("org.kde.KWin"));
+ // If KWin is not the window manager, then we disable the entry:
+ if (!killWindowKwinMethod->isValid()) {
+ killWindowAction->setEnabled(false);
+ }
+
+ connect(killWindowAction, &QAction::triggered, this, [this, killWindowKwinMethod](bool) {
+ // with DBus call, always use the async method.
+ // Otherwise it could wait up to 30 seconds in certain situations.
+ killWindowKwinMethod->asyncCall(QStringLiteral("killWindow"));
+ });
+
+ d->mToolsMenu->addAction(killWindowAction);
+ }
+
+ d->mUi->btnTools->setMenu(d->mToolsMenu);
}
KSysGuardProcessList::~KSysGuardProcessList()
@@ -413,8 +489,8 @@
d->mFilterModel.setFilterRegExp(newText.trimmed());
if(isVisible())
expandInit();
- d->mUi->btnKillProcess->setEnabled( d->mUi->treeView->selectionModel()->hasSelection() );
- d->mUi->treeView->scrollTo( d->mUi->treeView->currentIndex());
+ d->mUi->btnKillProcess->setEnabled(d->mUi->treeView->selectionModel()->hasSelection());
+ d->mUi->treeView->scrollTo(d->mUi->treeView->currentIndex());
}
int KSysGuardProcessList::visibleProcessesCount() const {
@@ -453,7 +529,7 @@
if(numSelected == d->mNumItemsSelected)
return;
d->mNumItemsSelected = numSelected;
- d->mUi->btnKillProcess->setEnabled( numSelected != 0 );
+ d->mUi->btnKillProcess->setEnabled(numSelected != 0);
d->renice->setText(i18np("Set Priority...", "Set Priority...", numSelected));
d->kill->setText(i18np("Forcibly Kill Process", "Forcibly Kill Processes", numSelected));