Commit dd11e707 authored by Pablo Rauzy's avatar Pablo Rauzy Committed by Christoph Cullmann
Browse files

simplifying things

parent 42171a43
......@@ -2,12 +2,9 @@ kate_add_plugin(keyboardmacroplugin)
target_compile_definitions(keyboardmacroplugin PRIVATE TRANSLATION_DOMAIN="katekeyboardmacro")
target_link_libraries(keyboardmacroplugin PRIVATE KF5::I18n KF5::TextEditor)
ki18n_wrap_ui(keyboardmacroplugin keyboardmacrowidget.ui)
target_sources(
keyboardmacroplugin
PRIVATE
plugin_katekeyboardmacro.cpp
plugin.qrc
)
{
"KPlugin": {
"Description": "Record and replay keyboard action sequences",
"Description": "Record and run any keyboard action sequence",
"Name": "Keyboard Macro",
"ServiceTypes": [
"KTextEditor/Plugin"
......
/***************************************************************************
plugin_katekeyboardmacro.cpp - description
-------------------
begin : FRE Feb 23 2001
copyright : (C) 2001 by Joseph Wenninger <jowenn@bigfoot.com>
copyright : (C) 2009 Dominik Haumann <dhaumann kde org>
***************************************************************************/
/***************************************************************************
* *
* SPDX-License-Identifier: GPL-2.0-or-later
* *
***************************************************************************/
/*
* SPDX-FileCopyrightText: 2022 Pablo Rauzy <r .at. uzy .dot. me>
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "plugin_katekeyboardmacro.h"
#include "ui_keyboardmacrowidget.h"
#include <ktexteditor/editor.h>
#include <ktexteditor/message.h>
#include <KLineEdit>
#include <KLocalizedString>
#include <KMessageBox>
#include <QAction>
#include <QDialog>
#include <QString>
#include <KActionCollection>
#include <KAuthorized>
#include <KConfigGroup>
#include <KPluginFactory>
#include <KSharedConfig>
#include <KXMLGUIFactory>
......@@ -36,221 +20,142 @@
#include <QApplication>
#include <QClipboard>
#include <iostream>
K_PLUGIN_FACTORY_WITH_JSON(KeyboardMacroPluginFactory, "keyboardmacroplugin.json", registerPlugin<PluginKateKeyboardMacro>();)
PluginKateKeyboardMacro::PluginKateKeyboardMacro(QObject *parent, const QList<QVariant> &)
: KTextEditor::Plugin(parent)
{
// register command
new PluginKateKeyboardMacroCommand(this);
// register commands
m_recCommand = new PluginKateKeyboardMacroRecordCommand(this);
m_runCommand = new PluginKateKeyboardMacroRunCommand(this);
}
PluginKateKeyboardMacro::~PluginKateKeyboardMacro()
{
// cleanup the process the right way (TM)
if (m_pFilterProcess) {
m_pFilterProcess->kill();
m_pFilterProcess->waitForFinished();
delete m_pFilterProcess;
}
delete m_recCommand;
delete m_runCommand;
}
QObject *PluginKateKeyboardMacro::createView(KTextEditor::MainWindow *mainWindow)
QObject *PluginKateKeyboardMacro::createView(KTextEditor::MainWindow *)
{
m_mainWindow = mainWindow;
// create a plugin view
return new PluginViewKateKeyboardMacro(this, mainWindow);
return nullptr;
}
void PluginKateKeyboardMacro::slotFilterReceivedStdout()
bool PluginKateKeyboardMacro::record(KTextEditor::View *)
{
m_strFilterOutput += QString::fromLocal8Bit(m_pFilterProcess->readAllStandardOutput());
}
void PluginKateKeyboardMacro::slotFilterReceivedStderr()
{
const QString block = QString::fromLocal8Bit(m_pFilterProcess->readAllStandardError());
if (mergeOutput) {
m_strFilterOutput += block;
} else {
m_stderrOutput += block;
if (m_recording) {
// stop recording
std::cerr << "stop recording" << std::endl;
m_recording = false;
m_macro = QStringLiteral("foobar");
return true; // if success
}
std::cerr << "start recording" << std::endl;
m_recording = true;
return true;
}
void PluginKateKeyboardMacro::slotFilterProcessExited(int, QProcess::ExitStatus)
bool PluginKateKeyboardMacro::run(KTextEditor::View *view)
{
KTextEditor::View *kv(KTextEditor::Editor::instance()->application()->activeMainWindow()->activeView());
if (!kv) {
return;
}
// Is there any error output to display?
if (!mergeOutput && !m_stderrOutput.isEmpty()) {
QPointer<KTextEditor::Message> message =
new KTextEditor::Message(xi18nc("@info", "<title>Result of:</title><nl /><pre><code>$ %1\n<nl />%2</code></pre>", m_last_command, m_stderrOutput),
KTextEditor::Message::Error);
message->setWordWrap(true);
message->setAutoHide(1000);
kv->document()->postMessage(message);
}
if (newDocument) {
auto v = m_mainWindow->openUrl(QUrl());
if (v && v->document()) {
v->document()->setText(m_strFilterOutput);
}
return;
}
if (copyResult) {
QApplication::clipboard()->setText(m_strFilterOutput);
return;
}
// Do not even try to change the document if no result collected...
if (m_strFilterOutput.isEmpty()) {
return;
if (m_recording) {
return false;
}
KTextEditor::Document::EditingTransaction transaction(kv->document());
KTextEditor::Cursor start = kv->cursorPosition();
if (kv->selection()) {
start = kv->selectionRange().start();
kv->removeSelectionText();
if (m_macro.isEmpty()) {
return false;
}
kv->setCursorPosition(start); // for block selection
kv->insertText(m_strFilterOutput);
view->insertText(m_macro);
return true;
}
static void slipInFilter(KProcess &proc, KTextEditor::View &view, const QString &command)
bool PluginKateKeyboardMacro::isRecording()
{
QString inputText;
return m_recording;
}
if (view.selection()) {
inputText = view.selectionText();
} else {
inputText = view.document()->text();
void PluginKateKeyboardMacro::slotRecord()
{
if (!KTextEditor::Editor::instance()->application()->activeMainWindow()) {
return;
}
proc.clearProgram();
proc.setShellCommand(command);
KTextEditor::View *view(KTextEditor::Editor::instance()->application()->activeMainWindow()->activeView());
if (!view) {
return;
}
proc.start();
QByteArray encoded = inputText.toLocal8Bit();
proc.write(encoded);
proc.closeWriteChannel();
// TODO: Put up a modal dialog to defend the text from further
// keystrokes while the command is out. With a cancel button...
record(view);
}
void PluginKateKeyboardMacro::slotEditFilter()
void PluginKateKeyboardMacro::slotRun()
{
if (!KAuthorized::authorize(QStringLiteral("shell_access"))) {
KMessageBox::error(nullptr,
i18n("You are not allowed to execute arbitrary external applications. If "
"you want to be able to do this, contact your system administrator."),
i18n("Access Restrictions"));
return;
}
if (!KTextEditor::Editor::instance()->application()->activeMainWindow()) {
return;
}
KTextEditor::View *kv(KTextEditor::Editor::instance()->application()->activeMainWindow()->activeView());
if (!kv) {
KTextEditor::View *view(KTextEditor::Editor::instance()->application()->activeMainWindow()->activeView());
if (!view) {
return;
}
QDialog dialog(KTextEditor::Editor::instance()->application()->activeMainWindow()->window());
Ui::KeyboardMacroWidget ui;
ui.setupUi(&dialog);
ui.filterBox->setFocus();
dialog.setWindowTitle(i18n("Text Filter"));
KConfigGroup config(KSharedConfig::openConfig(), "PluginKeyboardMacro");
QStringList items = config.readEntry("Completion list", QStringList());
copyResult = config.readEntry("Copy result", false);
mergeOutput = config.readEntry("Merge output", true);
newDocument = config.readEntry("New Document", false);
ui.filterBox->setMaxCount(10);
ui.filterBox->setHistoryItems(items, true);
ui.filterBox->setMinimumContentsLength(80);
ui.copyResult->setChecked(copyResult);
ui.mergeOutput->setChecked(mergeOutput);
ui.newDoc->setChecked(newDocument);
if (dialog.exec() == QDialog::Accepted) {
copyResult = ui.copyResult->isChecked();
mergeOutput = ui.mergeOutput->isChecked();
newDocument = ui.newDoc->isChecked();
const QString filter = ui.filterBox->currentText();
if (!filter.isEmpty()) {
ui.filterBox->addToHistory(filter);
config.writeEntry("New Document", newDocument);
config.writeEntry("Completion list", ui.filterBox->historyItems());
config.writeEntry("Copy result", copyResult);
config.writeEntry("Merge output", mergeOutput);
m_last_command = filter;
runFilter(kv, filter);
}
}
run(view);
}
void PluginKateKeyboardMacro::runFilter(KTextEditor::View *kv, const QString &filter)
{
m_strFilterOutput.clear();
m_stderrOutput.clear();
if (!m_pFilterProcess) {
m_pFilterProcess = new KProcess;
// BEGIN commands
connect(m_pFilterProcess, &KProcess::readyReadStandardOutput, this, &PluginKateKeyboardMacro::slotFilterReceivedStdout);
connect(m_pFilterProcess, &KProcess::readyReadStandardError, this, &PluginKateKeyboardMacro::slotFilterReceivedStderr);
PluginKateKeyboardMacroRecordCommand::PluginKateKeyboardMacroRecordCommand(PluginKateKeyboardMacro *plugin)
: KTextEditor::Command(QStringList() << QStringLiteral("recmac"), plugin)
, m_plugin(plugin)
{
}
connect(m_pFilterProcess,
static_cast<void (KProcess::*)(int, KProcess::ExitStatus)>(&KProcess::finished),
this,
&PluginKateKeyboardMacro::slotFilterProcessExited);
bool PluginKateKeyboardMacroRecordCommand::exec(KTextEditor::View *view, const QString &, QString &, const KTextEditor::Range &)
{
if (m_plugin->isRecording()) {
// remove from the recording the call to this command…
}
m_pFilterProcess->setOutputChannelMode(mergeOutput ? KProcess::MergedChannels : KProcess::SeparateChannels);
slipInFilter(*m_pFilterProcess, *kv, filter);
bool success = m_plugin->record(view);
if (!success) {
// display fail in toolview
}
return true;
}
// BEGIN Kate::Command methods
bool PluginKateKeyboardMacroRecordCommand::help(KTextEditor::View *, const QString &, QString &msg)
{
msg = i18n(
"<qt><p>Usage: <code>recmac</code></p>"
"<p>Start/stop recording a keyboard macro.</p></qt>");
return true;
}
PluginKateKeyboardMacroCommand::PluginKateKeyboardMacroCommand(PluginKateKeyboardMacro *plugin)
: KTextEditor::Command(QStringList() << QStringLiteral("keyboardmacro"), plugin)
PluginKateKeyboardMacroRunCommand::PluginKateKeyboardMacroRunCommand(PluginKateKeyboardMacro *plugin)
: KTextEditor::Command(QStringList() << QStringLiteral("runmac"), plugin)
, m_plugin(plugin)
{
}
bool PluginKateKeyboardMacroCommand::exec(KTextEditor::View *view, const QString &cmd, QString &msg, const KTextEditor::Range &)
bool PluginKateKeyboardMacroRunCommand::exec(KTextEditor::View *view, const QString &, QString &, const KTextEditor::Range &)
{
QString filter = cmd.section(QLatin1Char(' '), 1).trimmed();
if (filter.isEmpty()) {
msg = i18n("Usage: keyboardmacro COMMAND");
return false;
bool success = m_plugin->run(view);
if (!success) {
// display fail in toolview
}
m_plugin->runFilter(view, filter);
return true;
}
bool PluginKateKeyboardMacroCommand::help(KTextEditor::View *, const QString &, QString &msg)
bool PluginKateKeyboardMacroRunCommand::help(KTextEditor::View *, const QString &, QString &msg)
{
msg = i18n(
"<qt><p>Usage: <code>keyboardmacro COMMAND</code></p>"
"<p>Replace the selection with the output of the specified shell command.</p></qt>");
"<qt><p>Usage: <code>runmac</code></p>"
"<p>Run recorded keyboard macro.</p></qt>");
return true;
}
// END
PluginViewKateKeyboardMacro::PluginViewKateKeyboardMacro(PluginKateKeyboardMacro *plugin, KTextEditor::MainWindow *mainwindow)
......@@ -261,11 +166,17 @@ PluginViewKateKeyboardMacro::PluginViewKateKeyboardMacro(PluginKateKeyboardMacro
KXMLGUIClient::setComponentName(QStringLiteral("keyboardmacro"), i18n("Keyboard Macro"));
setXMLFile(QStringLiteral("ui.rc"));
// create our one and only action
QAction *a = actionCollection()->addAction(QStringLiteral("record_macro"));
a->setText(i18n("&Record Macro..."));
actionCollection()->setDefaultShortcut(a, Qt::CTRL | Qt::ALT | Qt::Key_R);
connect(a, &QAction::triggered, plugin, &PluginKateKeyboardMacro::slotEditFilter);
// create record action
QAction *rec = actionCollection()->addAction(QStringLiteral("record_macro"));
rec->setText(i18n("&Record Macro..."));
actionCollection()->setDefaultShortcut(rec, Qt::CTRL | Qt::SHIFT | Qt::Key_K);
connect(rec, &QAction::triggered, plugin, &PluginKateKeyboardMacro::slotRecord);
// create run action
QAction *exec = actionCollection()->addAction(QStringLiteral("run_macro"));
exec->setText(i18n("&Run Macro"));
actionCollection()->setDefaultShortcut(exec, Qt::CTRL | Qt::ALT | Qt::Key_K);
connect(exec, &QAction::triggered, plugin, &PluginKateKeyboardMacro::slotRun);
// register us at the UI
mainwindow->guiFactory()->addClient(this);
......
/***************************************************************************
plugin_katekeyboardmacro.h - description
-------------------
begin : FRE Feb 23 2001
copyright : (C) 2001 by Joseph Wenninger
email : jowenn@bigfoot.com
***************************************************************************/
/***************************************************************************
* *
* SPDX-License-Identifier: GPL-2.0-or-later
* *
***************************************************************************/
#ifndef PLUGIN_KATEKEYBOARDMACROS_H
#define PLUGIN_KATEKEYBOARDMACROS_H
/*
* SPDX-FileCopyrightText: 2022 Pablo Rauzy <r .at. uzy .dot. me>
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef PLUGIN_KATEKEYBOARDMACRO_H
#define PLUGIN_KATEKEYBOARDMACRO_H
#include <KTextEditor/Application>
#include <KTextEditor/Command>
......@@ -22,8 +13,8 @@
#include <KTextEditor/Plugin>
#include <KTextEditor/View>
#include <KProcess>
#include <QVariantList>
class PluginKateKeyboardMacroRecordCommand;
class PluginKateKeyboardMacroRunCommand;
class PluginKateKeyboardMacro : public KTextEditor::Plugin
{
......@@ -37,36 +28,53 @@ public:
~PluginKateKeyboardMacro() override;
QObject *createView(KTextEditor::MainWindow *mainWindow) override;
virtual QObject *createView(KTextEditor::MainWindow *mainWindow) override;
void runFilter(KTextEditor::View *kv, const QString &filter);
bool record(KTextEditor::View *view);
bool run(KTextEditor::View *view);
bool isRecording();
private:
QString m_strFilterOutput;
QString m_stderrOutput;
QString m_last_command;
KProcess *m_pFilterProcess = nullptr;
QStringList completionList;
bool copyResult = false;
bool mergeOutput = false;
bool newDocument = false;
KTextEditor::MainWindow *m_mainWindow;
bool m_recording = false;
QString m_macro;
PluginKateKeyboardMacroRecordCommand *m_recCommand;
PluginKateKeyboardMacroRunCommand *m_runCommand;
public Q_SLOTS:
void slotEditFilter();
void slotFilterReceivedStdout();
void slotFilterReceivedStderr();
void slotFilterProcessExited(int exitCode, QProcess::ExitStatus exitStatus);
void slotRecord();
void slotRun();
};
class PluginKateKeyboardMacroCommand : public KTextEditor::Command
/**
* recmac command
*/
class PluginKateKeyboardMacroRecordCommand : public KTextEditor::Command
{
Q_OBJECT
public:
PluginKateKeyboardMacroRecordCommand(PluginKateKeyboardMacro *plugin);
// Kate::Command
bool exec(KTextEditor::View *view, const QString &, QString &, const KTextEditor::Range & = KTextEditor::Range::invalid()) override;
bool help(KTextEditor::View *view, const QString &, QString &msg) override;
private:
PluginKateKeyboardMacro *m_plugin;
};
/**
* runmac command
*/
class PluginKateKeyboardMacroRunCommand : public KTextEditor::Command
{
Q_OBJECT
public:
PluginKateKeyboardMacroCommand(PluginKateKeyboardMacro *plugin);
PluginKateKeyboardMacroRunCommand(PluginKateKeyboardMacro *plugin);
// Kate::Command
bool exec(KTextEditor::View *view, const QString &cmd, QString &msg, const KTextEditor::Range &range = KTextEditor::Range::invalid()) override;
bool help(KTextEditor::View *view, const QString &cmd, QString &msg) override;
bool exec(KTextEditor::View *view, const QString &, QString &, const KTextEditor::Range & = KTextEditor::Range::invalid()) override;
bool help(KTextEditor::View *view, const QString &, QString &msg) override;
private:
PluginKateKeyboardMacro *m_plugin;
......
......@@ -5,6 +5,7 @@
<Menu name="tools">
<text>&amp;Tools</text>
<Action name="record_macro" group="tools_snippets"/>
<Action name="run_macro" group="tools_snippets"/>
</Menu>
</MenuBar>
</gui>
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment