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

starting from a clean (only renamed) copy of the textfilter plugin

parent 8cb35e5a
......@@ -33,3 +33,4 @@ ecm_optional_add_subdirectory(textfilter) # Pipe text through some external comm
ecm_optional_add_subdirectory(xmlcheck) # XML Validation plugin
ecm_optional_add_subdirectory(xmltools) # XML completion
ecm_optional_add_subdirectory(cmake-tools)
ecm_optional_add_subdirectory(keyboardmacro)
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
)
#! /bin/sh
$EXTRACTRC *.rc *.ui >> rc.cpp
$XGETTEXT *.cpp -o $podir/katekeyboardmacro.pot
{
"KPlugin": {
"Description": "Record and replay keyboard action sequences",
"Name": "Keyboard Macro",
"ServiceTypes": [
"KTextEditor/Plugin"
]
}
}
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KeyboardMacroWidget</class>
<widget class="QDialog" name="KeyboardMacroWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>370</width>
<height>190</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="lblFilter">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Enter command to &amp;pipe selected text through:</string>
</property>
<property name="buddy">
<cstring>filterBox</cstring>
</property>
</widget>
</item>
<item>
<widget class="KHistoryComboBox" name="filterBox"/>
</item>
<item>
<widget class="QCheckBox" name="newDoc">
<property name="text">
<string>Open results in a new document</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="copyResult">
<property name="toolTip">
<string>Copy the result to clipboard leaving a document unchanged.</string>
</property>
<property name="text">
<string>Copy the result instead of pasting it</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mergeOutput">
<property name="toolTip">
<string>If checked, an output from STDOUT and STDERR will be merged and no errors will be reported.
Otherwise, STDERR will be displayed as a passive message.</string>
</property>
<property name="text">
<string>Merge STDOUT and STDERR</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KComboBox</class>
<extends>QComboBox</extends>
<header>kcombobox.h</header>
</customwidget>
<customwidget>
<class>KHistoryComboBox</class>
<extends>KComboBox</extends>
<header>khistorycombobox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>KeyboardMacroWidget</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>177</x>
<y>136</y>
</hint>
<hint type="destinationlabel">
<x>182</x>
<y>118</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>KeyboardMacroWidget</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>326</x>
<y>141</y>
</hint>
<hint type="destinationlabel">
<x>358</x>
<y>117</y>
</hint>
</hints>
</connection>
</connections>
</ui>
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource prefix="/kxmlgui5/keyboardmacro">
<file>ui.rc</file>
</qresource>
</RCC>
/***************************************************************************
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
* *
***************************************************************************/
#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>
#include <QApplication>
#include <QClipboard>
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);
}
PluginKateKeyboardMacro::~PluginKateKeyboardMacro()
{
// cleanup the process the right way (TM)
if (m_pFilterProcess) {
m_pFilterProcess->kill();
m_pFilterProcess->waitForFinished();
delete m_pFilterProcess;
}
}
QObject *PluginKateKeyboardMacro::createView(KTextEditor::MainWindow *mainWindow)
{
m_mainWindow = mainWindow;
// create a plugin view
return new PluginViewKateKeyboardMacro(this, mainWindow);
}
void PluginKateKeyboardMacro::slotFilterReceivedStdout()
{
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;
}
}
void PluginKateKeyboardMacro::slotFilterProcessExited(int, QProcess::ExitStatus)
{
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;
}
KTextEditor::Document::EditingTransaction transaction(kv->document());
KTextEditor::Cursor start = kv->cursorPosition();
if (kv->selection()) {
start = kv->selectionRange().start();
kv->removeSelectionText();
}
kv->setCursorPosition(start); // for block selection
kv->insertText(m_strFilterOutput);
}
static void slipInFilter(KProcess &proc, KTextEditor::View &view, const QString &command)
{
QString inputText;
if (view.selection()) {
inputText = view.selectionText();
} else {
inputText = view.document()->text();
}
proc.clearProgram();
proc.setShellCommand(command);
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...
}
void PluginKateKeyboardMacro::slotEditFilter()
{
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) {
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);
}
}
}
void PluginKateKeyboardMacro::runFilter(KTextEditor::View *kv, const QString &filter)
{
m_strFilterOutput.clear();
m_stderrOutput.clear();
if (!m_pFilterProcess) {
m_pFilterProcess = new KProcess;
connect(m_pFilterProcess, &KProcess::readyReadStandardOutput, this, &PluginKateKeyboardMacro::slotFilterReceivedStdout);
connect(m_pFilterProcess, &KProcess::readyReadStandardError, this, &PluginKateKeyboardMacro::slotFilterReceivedStderr);
connect(m_pFilterProcess,
static_cast<void (KProcess::*)(int, KProcess::ExitStatus)>(&KProcess::finished),
this,
&PluginKateKeyboardMacro::slotFilterProcessExited);
}
m_pFilterProcess->setOutputChannelMode(mergeOutput ? KProcess::MergedChannels : KProcess::SeparateChannels);
slipInFilter(*m_pFilterProcess, *kv, filter);
}
// BEGIN Kate::Command methods
PluginKateKeyboardMacroCommand::PluginKateKeyboardMacroCommand(PluginKateKeyboardMacro *plugin)
: KTextEditor::Command(QStringList() << QStringLiteral("keyboardmacro"), plugin)
, m_plugin(plugin)
{
}
bool PluginKateKeyboardMacroCommand::exec(KTextEditor::View *view, const QString &cmd, QString &msg, const KTextEditor::Range &)
{
QString filter = cmd.section(QLatin1Char(' '), 1).trimmed();
if (filter.isEmpty()) {
msg = i18n("Usage: keyboardmacro COMMAND");
return false;
}
m_plugin->runFilter(view, filter);
return true;
}
bool PluginKateKeyboardMacroCommand::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>");
return true;
}
// END
PluginViewKateKeyboardMacro::PluginViewKateKeyboardMacro(PluginKateKeyboardMacro *plugin, KTextEditor::MainWindow *mainwindow)
: QObject(mainwindow)
, m_mainWindow(mainwindow)
{
// setup right xml gui data
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);
// register us at the UI
mainwindow->guiFactory()->addClient(this);
}
PluginViewKateKeyboardMacro::~PluginViewKateKeyboardMacro()
{
// remove us from the UI again
m_mainWindow->guiFactory()->removeClient(this);
}
// required for KeyboardMacroPluginFactory vtable
#include "plugin_katekeyboardmacro.moc"
/***************************************************************************
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
#include <KTextEditor/Application>
#include <KTextEditor/Command>
#include <KTextEditor/Document>
#include <KTextEditor/MainWindow>
#include <KTextEditor/Plugin>
#include <KTextEditor/View>
#include <KProcess>
#include <QVariantList>
class PluginKateKeyboardMacro : public KTextEditor::Plugin
{
Q_OBJECT
public:
/**
* Plugin constructor.
*/
explicit PluginKateKeyboardMacro(QObject *parent = nullptr, const QList<QVariant> & = QList<QVariant>());
~PluginKateKeyboardMacro() override;
QObject *createView(KTextEditor::MainWindow *mainWindow) override;
void runFilter(KTextEditor::View *kv, const QString &filter);
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;
public Q_SLOTS:
void slotEditFilter();
void slotFilterReceivedStdout();
void slotFilterReceivedStderr();
void slotFilterProcessExited(int exitCode, QProcess::ExitStatus exitStatus);
};
class PluginKateKeyboardMacroCommand : public KTextEditor::Command
{
Q_OBJECT
public:
PluginKateKeyboardMacroCommand(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;
private:
PluginKateKeyboardMacro *m_plugin;
};
/**
* Plugin view to merge the actions into the UI
*/
class PluginViewKateKeyboardMacro : public QObject, public KXMLGUIClient
{
Q_OBJECT
public:
/**
* Construct plugin view
* @param plugin our plugin
* @param mainwindows the mainwindow for this view
*/
explicit PluginViewKateKeyboardMacro(PluginKateKeyboardMacro *plugin, KTextEditor::MainWindow *mainwindow);
/**
* Our Destructor
*/
~PluginViewKateKeyboardMacro() override;
private:
/**
* the main window we belong to
*/
KTextEditor::MainWindow *m_mainWindow;
};
#endif
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE gui SYSTEM "kpartgui.dtd">
<gui name="katekeyboardmacro" library="libkatekeyboardmacroplugin" version="5" translationDomain="katekeyboardmacro">
<MenuBar>
<Menu name="tools">
<text>&amp;Tools</text>
<Action name="record_macro" group="tools_snippets"/>
</Menu>
</MenuBar>
</gui>
<!-- kate: space-indent on; indent-width 4; replace-tabs on; -->
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