Commit c7866f60 authored by Tomaz  Canabrava's avatar Tomaz Canabrava
Browse files

USe KCommandBar to allow easy access to the plugin contents

parent ff776d59
......@@ -7,6 +7,10 @@
#include "quickcommandsmodel.h"
#include "quickcommandswidget.h"
#include <KActionCollection>
#include <KCommandBar>
#include <QMainWindow>
#include "MainWindow.h"
#include <KLocalizedString>
......@@ -16,7 +20,7 @@ K_PLUGIN_CLASS_WITH_JSON(QuickCommandsPlugin, "konsole_quickcommands.json")
struct QuickCommandsPlugin::Private {
QuickCommandsModel model;
QAction *showQuickAccess = nullptr;
QMap<Konsole::MainWindow *, QuickCommandsWidget *> widgetForWindow;
QMap<Konsole::MainWindow *, QDockWidget *> dockForWindow;
};
......@@ -25,6 +29,7 @@ QuickCommandsPlugin::QuickCommandsPlugin(QObject *object, const QVariantList &ar
: Konsole::IKonsolePlugin(object, args)
, priv(std::make_unique<Private>())
{
priv->showQuickAccess = new QAction();
setName(QStringLiteral("QuickCommands"));
}
......@@ -48,8 +53,39 @@ void QuickCommandsPlugin::createWidgetsForMainWindow(Konsole::MainWindow *mainWi
void QuickCommandsPlugin::activeViewChanged(Konsole::SessionController *controller, Konsole::MainWindow *mainWindow)
{
if (mainWindow)
priv->showQuickAccess->deleteLater();
priv->showQuickAccess = new QAction(i18n("Show Quick Access for SSH Actions"));
priv->showQuickAccess->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_G));
controller->view()->addAction(priv->showQuickAccess);
Konsole::TerminalDisplay *terminalDisplay = controller->view();
connect(priv->showQuickAccess, &QAction::triggered, this, [this, terminalDisplay, controller] {
KCommandBar bar(terminalDisplay->topLevelWidget());
QList<QAction *> actions;
for (int i = 0; i < priv->model.rowCount(); i++) {
QModelIndex folder = priv->model.index(i, 0);
for (int e = 0; e < priv->model.rowCount(folder); e++) {
QModelIndex idx = priv->model.index(e, 0, folder);
QAction *act = new QAction(idx.data().toString());
connect(act, &QAction::triggered, this, [this, idx, controller] {
const auto item = priv->model.itemFromIndex(idx);
const auto data = item->data(QuickCommandsModel::QuickCommandRole).value<QuickCommandData>();
controller->session()->sendTextToTerminal(data.command, QLatin1Char('\r'));
});
actions.append(act);
}
}
QVector<KCommandBar::ActionGroup> groups;
groups.push_back(KCommandBar::ActionGroup{i18n("Quick Commands"), actions});
bar.setActions(groups);
bar.exec();
});
if (mainWindow) {
priv->widgetForWindow[mainWindow]->setCurrentController(controller);
}
}
QList<QAction *> QuickCommandsPlugin::menuBarActions(Konsole::MainWindow *mainWindow) const
......
......@@ -8,8 +8,6 @@ SOURCES
sshmanagermodel.cpp
sshmanagerfiltermodel.cpp
sshtreeview.cpp
sshquickaccesswidget.cpp
sshquickaccesswidget.h
${EXTRA_SSHPLUGIN_SRCS}
INSTALL_NAMESPACE
"konsoleplugins"
......
......@@ -10,15 +10,24 @@
#include "sshmanagermodel.h"
#include "sshmanagerpluginwidget.h"
#include "ProcessInfo.h"
#include "konsoledebug.h"
#include "session/SessionController.h"
#include <QDockWidget>
#include <QListView>
#include <QMainWindow>
#include <QMenuBar>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include <QTimer>
#include <KActionCollection>
#include <KCommandBar>
#include <KLocalizedString>
#include <KMessageBox>
#include "MainWindow.h"
#include "sshquickaccesswidget.h"
#include "terminalDisplay/TerminalDisplay.h"
K_PLUGIN_CLASS_WITH_JSON(SSHManagerPlugin, "konsole_sshmanager.json")
......@@ -28,7 +37,6 @@ struct SSHManagerPluginPrivate {
QMap<Konsole::MainWindow *, SSHManagerTreeWidget *> widgetForWindow;
QMap<Konsole::MainWindow *, QDockWidget *> dockForWindow;
SSHQuickAccessWidget *quickAccess = nullptr;
QAction *showQuickAccess = nullptr;
};
......@@ -36,13 +44,6 @@ SSHManagerPlugin::SSHManagerPlugin(QObject *object, const QVariantList &args)
: Konsole::IKonsolePlugin(object, args)
, d(std::make_unique<SSHManagerPluginPrivate>())
{
d->quickAccess = new SSHQuickAccessWidget(&d->model);
connect(d->quickAccess, &QObject::destroyed, this, [this] {
// quick access can be destroyed if a terminal display is destroyed.
// so, just create a new one.
d->quickAccess = new SSHQuickAccessWidget(&d->model);
});
d->showQuickAccess = new QAction();
setName(QStringLiteral("SshManager"));
......@@ -50,7 +51,6 @@ SSHManagerPlugin::SSHManagerPlugin(QObject *object, const QVariantList &args)
SSHManagerPlugin::~SSHManagerPlugin()
{
disconnect(d->quickAccess);
}
void SSHManagerPlugin::createWidgetsForMainWindow(Konsole::MainWindow *mainWindow)
......@@ -95,20 +95,31 @@ void SSHManagerPlugin::activeViewChanged(Konsole::SessionController *controller,
auto terminalDisplay = controller->view();
d->quickAccess->hide();
d->quickAccess->setParent(terminalDisplay);
d->quickAccess->setSessionController(controller);
d->showQuickAccess->deleteLater();
d->showQuickAccess = new QAction(i18n("Show Quick Access for SSH Actions"));
d->showQuickAccess->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_H));
terminalDisplay->addAction(d->showQuickAccess);
connect(d->showQuickAccess, &QAction::triggered, this, [this, terminalDisplay] {
d->quickAccess->show();
d->quickAccess->setFocus();
terminalDisplay->installEventFilter(d->quickAccess);
connect(d->showQuickAccess, &QAction::triggered, this, [this, terminalDisplay, controller] {
KCommandBar bar(terminalDisplay->topLevelWidget());
QList<QAction *> actions;
for (int i = 0; i < d->model.rowCount(); i++) {
QModelIndex folder = d->model.index(i, 0);
for (int e = 0; e < d->model.rowCount(folder); e++) {
QModelIndex idx = d->model.index(e, 0, folder);
QAction *act = new QAction(idx.data().toString());
connect(act, &QAction::triggered, this, [this, idx, controller] {
requestConnection(nullptr, &d->model, controller, idx);
});
actions.append(act);
}
}
QVector<KCommandBar::ActionGroup> groups;
groups.push_back(KCommandBar::ActionGroup{i18n("SSH Entries"), actions});
bar.setActions(groups);
bar.exec();
});
if (mainWindow) {
......@@ -116,4 +127,66 @@ void SSHManagerPlugin::activeViewChanged(Konsole::SessionController *controller,
}
}
void SSHManagerPlugin::requestConnection(QSortFilterProxyModel *filterModel,
QStandardItemModel *model,
Konsole::SessionController *controller,
const QModelIndex &idx)
{
if (!controller) {
return;
}
auto sourceIdx = filterModel ? filterModel->mapToSource(idx) : idx;
if (sourceIdx.parent() == model->invisibleRootItem()->index()) {
return;
}
Konsole::ProcessInfo *info = controller->session()->getProcessInfo();
bool ok = false;
QString processName = info->name(&ok);
if (!ok) {
KMessageBox::error(nullptr, i18n("Could not get the process name, assume that we can't request a connection"), i18n("Error issuing SSH Command"));
return;
}
if (!QVector<QString>({QStringLiteral("fish"),
QStringLiteral("bash"),
QStringLiteral("dash"),
QStringLiteral("sh"),
QStringLiteral("csh"),
QStringLiteral("ksh"),
QStringLiteral("zsh")})
.contains(processName)) {
KMessageBox::error(nullptr, i18n("Can't issue SSH command outside the shell application (eg, bash, zsh, sh)"), i18n("Error issuing SSH Command"));
return;
}
auto item = model->itemFromIndex(sourceIdx);
auto data = item->data(SSHManagerModel::SSHRole).value<SSHConfigurationData>();
QString sshCommand = QStringLiteral("ssh ");
if (!data.useSshConfig) {
if (data.sshKey.length()) {
sshCommand += QStringLiteral("-i %1 ").arg(data.sshKey);
}
if (data.port.length()) {
sshCommand += QStringLiteral("-p %1 ").arg(data.port);
}
if (!data.username.isEmpty()) {
sshCommand += data.username + QLatin1Char('@');
}
}
if (!data.host.isEmpty()) {
sshCommand += data.host;
}
controller->session()->sendTextToTerminal(sshCommand, QLatin1Char('\r'));
if (controller->session()->views().count()) {
controller->session()->views().at(0)->setFocus();
}
}
#include "sshmanagerplugin.moc"
......@@ -18,6 +18,9 @@ class SessionController;
class MainWindow;
}
class QSortFilterProxyModel;
class QStandardItemModel;
struct SSHManagerPluginPrivate;
class SSHManagerPlugin : public Konsole::IKonsolePlugin
......@@ -31,6 +34,9 @@ public:
void activeViewChanged(Konsole::SessionController *controller, Konsole::MainWindow *mainWindow) override;
QList<QAction *> menuBarActions(Konsole::MainWindow *mainWindow) const override;
static void
requestConnection(QSortFilterProxyModel *filterModel, QStandardItemModel *model, Konsole::SessionController *controller, const QModelIndex &idx);
private:
std::unique_ptr<SSHManagerPluginPrivate> d;
};
......
......@@ -18,6 +18,7 @@
#include "profile/ProfileModel.h"
#include "sshmanagerfiltermodel.h"
#include "sshmanagerplugin.h"
#include "ui_sshwidget.h"
#include <KLocalizedString>
......@@ -118,7 +119,10 @@ SSHManagerTreeWidget::SSHManagerTreeWidget(QWidget *parent)
menu->popup(ui->treeView->viewport()->mapToGlobal(pos));
});
connect(ui->treeView, &QTreeView::doubleClicked, this, &SSHManagerTreeWidget::connectRequested);
connect(ui->treeView, &QTreeView::doubleClicked, this, [this](const QModelIndex &idx) {
SSHManagerPlugin::requestConnection(d->filterModel, d->model, d->controller, idx);
});
connect(ui->treeView, &SshTreeView::mouseButtonClicked, this, &SSHManagerTreeWidget::handleTreeClick);
ui->treeView->setModel(d->filterModel);
......@@ -441,66 +445,7 @@ void SSHManagerTreeWidget::handleTreeClick(Qt::MouseButton btn, const QModelInde
}
Q_EMIT requestNewTab();
connectRequested(idx);
}
}
void SSHManagerTreeWidget::connectRequested(const QModelIndex &idx)
{
if (!d->controller) {
return;
}
auto sourceIdx = d->filterModel->mapToSource(idx);
if (sourceIdx.parent() == d->model->invisibleRootItem()->index()) {
return;
}
Konsole::ProcessInfo *info = d->controller->session()->getProcessInfo();
bool ok = false;
QString processName = info->name(&ok);
if (!ok) {
KMessageBox::error(this, i18n("Could not get the process name, assume that we can't request a connection"), i18n("Error issuing SSH Command"));
return;
}
if (!QVector<QString>({QStringLiteral("fish"),
QStringLiteral("bash"),
QStringLiteral("dash"),
QStringLiteral("sh"),
QStringLiteral("csh"),
QStringLiteral("ksh"),
QStringLiteral("zsh")})
.contains(processName)) {
KMessageBox::error(this, i18n("Can't issue SSH command outside the shell application (eg, bash, zsh, sh)"), i18n("Error issuing SSH Command"));
return;
}
auto item = d->model->itemFromIndex(sourceIdx);
auto data = item->data(SSHManagerModel::SSHRole).value<SSHConfigurationData>();
QString sshCommand = QStringLiteral("ssh ");
if (!data.useSshConfig) {
if (data.sshKey.length()) {
sshCommand += QStringLiteral("-i %1 ").arg(data.sshKey);
}
if (data.port.length()) {
sshCommand += QStringLiteral("-p %1 ").arg(data.port);
}
if (!data.username.isEmpty()) {
sshCommand += data.username + QLatin1Char('@');
}
}
if (!data.host.isEmpty()) {
sshCommand += data.host;
}
d->controller->session()->sendTextToTerminal(sshCommand, QLatin1Char('\r'));
if (d->controller->session()->views().count()) {
d->controller->session()->views().at(0)->setFocus();
SSHManagerPlugin::requestConnection(d->filterModel, d->model, d->controller, sourceIdx);
}
}
......
......@@ -62,7 +62,6 @@ public:
void setModel(SSHManagerModel *model);
void triggerDelete();
void setCurrentController(Konsole::SessionController *controller);
void connectRequested(const QModelIndex &idx);
void handleImportedData(bool isImported);
protected:
......
/* This file was part of the KDE libraries
SPDX-FileCopyrightText: 2021 Tomaz Canabrava <tcanabrava@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "sshquickaccesswidget.h"
#include "sshmanagerfiltermodel.h"
#include <session/SessionController.h>
#include <terminalDisplay/TerminalDisplay.h>
#include <QAbstractItemModel>
#include <QBoxLayout>
#include <QDebug>
#include <QFocusEvent>
#include <QItemSelectionModel>
#include <QKeyEvent>
#include <QLineEdit>
#include <QMouseEvent>
#include <QShowEvent>
#include <QTreeView>
struct SSHQuickAccessWidget::Private {
QAbstractItemModel *model = nullptr;
SSHManagerFilterModel *filterModel = nullptr;
Konsole::SessionController *controller = nullptr;
QTreeView *view = nullptr;
QLineEdit *filterLine = nullptr;
};
SSHQuickAccessWidget::SSHQuickAccessWidget(QAbstractItemModel *model, QWidget *parent)
: QWidget(parent)
, d(std::make_unique<SSHQuickAccessWidget::Private>())
{
d->filterModel = new SSHManagerFilterModel(this);
d->filterModel->setSourceModel(model);
d->filterLine = new QLineEdit(this);
d->filterLine->setPlaceholderText(tr("Filter"));
auto *view = new QTreeView(this);
view->setHeaderHidden(true);
view->setModel(d->filterModel);
d->view = view;
auto *layout = new QBoxLayout(QBoxLayout::Direction::TopToBottom);
layout->addWidget(d->filterLine);
layout->addWidget(view);
setLayout(layout);
layout->setSpacing(0);
d->model = model;
connect(d->filterLine, &QLineEdit::textChanged, this, [this] {
d->filterModel->setFilterRegularExpression(d->filterLine->text());
});
d->filterLine->installEventFilter(this);
}
SSHQuickAccessWidget::~SSHQuickAccessWidget() = default;
void SSHQuickAccessWidget::setSessionController(Konsole::SessionController *controller)
{
d->controller = controller;
}
void SSHQuickAccessWidget::focusInEvent(QFocusEvent *ev)
{
d->view->setFocus();
}
void SSHQuickAccessWidget::mousePressEvent(QMouseEvent *event)
{
}
void SSHQuickAccessWidget::keyPressEvent(QKeyEvent *ev)
{
if (ev->key() == Qt::Key_Escape) {
hide();
d->controller->view()->setFocus();
}
}
void SSHQuickAccessWidget::showEvent(QShowEvent *ev)
{
if (!parentWidget()) {
return;
}
auto rect = parentWidget()->geometry();
int eigth = rect.width() / 8;
rect.adjust(eigth, eigth, -eigth, -eigth);
setGeometry(eigth, rect.topLeft().y(), rect.width(), rect.height());
for (int i = 0; i < d->view->model()->rowCount(); i++) {
QModelIndex idx = d->view->model()->index(i, 0);
d->view->expand(idx);
}
}
void SSHQuickAccessWidget::selectNext()
{
constexpr QItemSelectionModel::SelectionFlag flag = QItemSelectionModel::SelectionFlag::ClearAndSelect;
auto *slModel = d->view->selectionModel();
auto selection = slModel->selectedIndexes();
if (selection.empty()) {
QModelIndex firstFolder = d->filterModel->index(0, 0);
slModel->select(firstFolder, flag);
return;
}
const QModelIndex curr = selection.first();
const QModelIndex parent = curr.parent();
const int currCount = d->filterModel->rowCount(curr);
const int parentCount = d->filterModel->rowCount(parent);
// we are currently on a folder, select the children.
if (currCount != 0) {
QModelIndex next = d->filterModel->index(0, 0, curr);
slModel->select(next, flag);
return;
}
// test to see if this is not the last possible child.
if (curr.row() != parentCount - 1) {
QModelIndex next = d->filterModel->index(curr.row() + 1, 0, curr.parent());
slModel->select(next, flag);
return;
}
// Last possible child. we need to go to the next parent, *or* wrap.
if (parent.row() != d->filterModel->rowCount() - 1) {
QModelIndex nextFolder = d->filterModel->index(parent.row() + 1, 0);
slModel->select(nextFolder, flag);
return;
}
// Wrap
QModelIndex next = d->filterModel->index(0, 0);
slModel->select(next, flag);
}
void SSHQuickAccessWidget::selectPrevious()
{
constexpr QItemSelectionModel::SelectionFlag flag = QItemSelectionModel::SelectionFlag::ClearAndSelect;
auto *slModel = d->view->selectionModel();
auto selection = slModel->selectedIndexes();
auto lambdaLastFolder = [this, slModel] {
const QModelIndex lastFolder = d->filterModel->index(d->filterModel->rowCount() - 1, 0);
const QModelIndex lastEntry = d->filterModel->index(d->filterModel->rowCount(lastFolder) - 1, 0, lastFolder);
if (lastEntry.isValid()) {
slModel->select(lastEntry, flag);
} else {
slModel->select(lastFolder, flag);
}
return;
};
if (selection.empty()) {
lambdaLastFolder();
return;
}
const QModelIndex curr = selection.first();
const QModelIndex parent = curr.parent();
const int currCount = d->filterModel->rowCount(curr);
const int parentCount = d->filterModel->rowCount(parent);
// we are currently on a folder, select the last element of the previous folder, or the previous folder
// if it's empty.
if (currCount != 0) {
if (curr.row() == 0) {
lambdaLastFolder();
return;
}
QModelIndex prevFolder = d->filterModel->index(curr.row() - 1, 0);
QModelIndex lastEntry = d->filterModel->index(d->filterModel->rowCount(prevFolder) - 1, 0, prevFolder);
if (lastEntry.isValid()) {
slModel->select(lastEntry, flag);
} else {
slModel->select(prevFolder, flag);
}
return;
}
// test to see if this is not the last possible child.
if (curr.row() != 0) {
QModelIndex next = d->filterModel->index(curr.row() - 1, 0, curr.parent());
slModel->select(next, flag);
return;
}
// Last possible child. we need to go to the next parent, *or* wrap.
if (curr.row() == 0 && !curr.parent().isValid()) {
lambdaLastFolder();
return;
}
slModel->select(parent, flag);
}
bool SSHQuickAccessWidget::eventFilter(QObject *watched, QEvent *event)
{
if (watched == d->filterLine) {
if (event->type() == QEvent::KeyPress) {
auto keyEvent = dynamic_cast<QKeyEvent *>(event);
switch (keyEvent->key()) {
case Qt::Key_Up:
selectPrevious();
return true;
case Qt::Key_Down:
selectNext();
return true;
default:
return false;
}
return true;
}
return QWidget::eventFilter(watched, event);
}
if (watched == parentWidget()) {
if (event->type() == QEvent::MouseButtonPress) {
hide();
d->controller->view()->setFocus();