diff --git a/addons/externaltools/kateexternaltool.cpp b/addons/externaltools/kateexternaltool.cpp index cbee19388..8abb13a3c 100644 --- a/addons/externaltools/kateexternaltool.cpp +++ b/addons/externaltools/kateexternaltool.cpp @@ -1,85 +1,88 @@ /* This file is part of the KDE project * * Copyright 2019 Dominik Haumann * * 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 "kateexternaltool.h" #include #include bool KateExternalTool::checkExec() const { return !QStandardPaths::findExecutable(executable).isEmpty(); } bool KateExternalTool::matchesMimetype(const QString& mt) const { return mimetypes.isEmpty() || mimetypes.contains(mt); } void KateExternalTool::load(const KConfigGroup& cg) { category = cg.readEntry("category", ""); name = cg.readEntry("name", ""); icon = cg.readEntry("icon", ""); executable = cg.readEntry("executable", ""); arguments = cg.readEntry("arguments", ""); input = cg.readEntry("input", ""); workingDir = cg.readEntry("workingDir", ""); mimetypes = cg.readEntry("mimetypes", QStringList()); actionName = cg.readEntry("actionName"); cmdname = cg.readEntry("cmdname"); saveMode = static_cast(cg.readEntry("save", 0)); + outputMode = static_cast(cg.readEntry("output", 0)); includeStderr = cg.readEntry("includeStderr", false); hasexec = checkExec(); } void KateExternalTool::save(KConfigGroup& cg) const { cg.writeEntry("category", category); cg.writeEntry("name", name); cg.writeEntry("icon", icon); cg.writeEntry("executable", executable); cg.writeEntry("arguments", arguments); cg.writeEntry("input", input); cg.writeEntry("workingDir", workingDir); cg.writeEntry("mimetypes", mimetypes); cg.writeEntry("actionName", actionName); cg.writeEntry("cmdname", cmdname); cg.writeEntry("save", static_cast(saveMode)); + cg.writeEntry("output", static_cast(outputMode)); cg.writeEntry("includeStderr", includeStderr); } bool operator==(const KateExternalTool & lhs, const KateExternalTool & rhs) { return lhs.category == rhs.category && lhs.name == rhs.name && lhs.icon == rhs.icon && lhs.executable == rhs.executable && lhs.arguments == rhs.arguments && lhs.input == rhs.input && lhs.workingDir == rhs.workingDir && lhs.mimetypes == rhs.mimetypes && lhs.actionName == rhs.actionName && lhs.cmdname == rhs.cmdname && lhs.saveMode == rhs.saveMode + && lhs.outputMode == rhs.outputMode && lhs.includeStderr == rhs.includeStderr; } // kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/addons/externaltools/kateexternaltool.h b/addons/externaltools/kateexternaltool.h index e891e1213..f5ab56d72 100644 --- a/addons/externaltools/kateexternaltool.h +++ b/addons/externaltools/kateexternaltool.h @@ -1,127 +1,126 @@ /* This file is part of the KDE project * * Copyright 2019 Dominik Haumann * * 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 KTEXTEDITOR_KATE_EXTERNALTOOL_H #define KTEXTEDITOR_KATE_EXTERNALTOOL_H #include #include #include class KConfigGroup; /** * This class defines a single external tool. */ class KateExternalTool { public: /** * Defines whether any document should be saved before running the tool. */ enum class SaveMode { //! Do not save any document. None, //! Save current document. CurrentDocument, //! Save all documents AllDocuments }; /** * Defines where to redirect stdout from the tool. */ - // enum class OutputMode { - // Ignore, - // InsertAtCursor, - // ReplaceSelectedText, - // AppendToCurrentDocument, - // InsertInNewDocument, - // DisplayInPane - // } - // Q_ENUM(OutputMode) + enum class OutputMode { + Ignore, + InsertAtCursor, + ReplaceSelectedText, + ReplaceCurrentDocument, + AppendToCurrentDocument, + InsertInNewDocument, + DisplayInPane + }; public: /// The category used in the menu to categorize the tool. QString category; /// The name used in the menu. QString name; /// the icon to use in the menu. QString icon; /// The name or path of the executable. QString executable; /// The command line arguments. QString arguments; /// The stdin input. QString input; /// The working directory, if specified. QString workingDir; /// Optional list of mimetypes for which this action is valid. QStringList mimetypes; /// The name for the action. This is generated first time the /// action is is created. QString actionName; /// The name for the commandline. QString cmdname; /// Possibly save documents prior to activating the tool command. SaveMode saveMode = SaveMode::None; - - /// Possibly redirect the stdout output of the tool. - // OutputMode outputMode; + /// Defines where to redirect the tool's output + OutputMode outputMode = OutputMode::Ignore; /// Include stderr output when running the tool. bool includeStderr = false; public: /// This is set when loading the Tool from disk. bool hasexec = false; /** * @return true if mimetypes is empty, or the @p mimetype matches. */ bool matchesMimetype(const QString& mimetype) const; /** * @return true if "executable" exists and has the executable bit set, or is * empty. * This is run at least once, and the tool is disabled if it fails. */ bool checkExec() const; /** * Load tool data from the config group @p cg. */ void load(const KConfigGroup& cg); /** * Save tool data to the config group @p cg. */ void save(KConfigGroup& cg) const; }; /** * Compares for equality. All fields have to match. */ bool operator==(const KateExternalTool & lhs, const KateExternalTool & rhs); // for use in QVariant (QAction::setData() and QAction::data()) Q_DECLARE_METATYPE(KateExternalTool*) #endif // KTEXTEDITOR_KATE_EXTERNALTOOL_H // kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/addons/externaltools/kateexternaltoolsconfigwidget.cpp b/addons/externaltools/kateexternaltoolsconfigwidget.cpp index 1f044158a..9f71f468b 100644 --- a/addons/externaltools/kateexternaltoolsconfigwidget.cpp +++ b/addons/externaltools/kateexternaltoolsconfigwidget.cpp @@ -1,386 +1,389 @@ /* This file is part of the KDE project * * Copyright 2019 Dominik Haumann * * 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. */ // TODO // Icons // Direct shortcut setting #include "kateexternaltoolsconfigwidget.h" #include "externaltoolsplugin.h" #include "kateexternaltool.h" #include "katemacroexpander.h" #include "katetoolrunner.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // BEGIN ToolItem /** * This is a QStandardItem, that has a KateExternalTool. * The text is the Name of the tool. */ class ToolItem : public QStandardItem { public: ToolItem(const QPixmap& icon, KateExternalTool* tool) : QStandardItem(icon, tool->name) { setData(QVariant::fromValue(tool)); } KateExternalTool * tool() { return qvariant_cast(data()); } }; // END ToolItem // BEGIN KateExternalToolServiceEditor KateExternalToolServiceEditor::KateExternalToolServiceEditor(KateExternalTool* tool, QWidget* parent) : QDialog(parent) , m_tool(tool) { setWindowTitle(i18n("Edit External Tool")); ui = new Ui::ToolDialog(); ui->setupUi(this); ui->btnIcon->setIconSize(KIconLoader::SizeSmall); connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &KateExternalToolServiceEditor::slotOKClicked); connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(ui->btnMimeType, &QToolButton::clicked, this, &KateExternalToolServiceEditor::showMTDlg); Q_ASSERT(m_tool != nullptr); ui->edtName->setText(m_tool->name); if (!m_tool->icon.isEmpty()) ui->btnIcon->setIcon(m_tool->icon); ui->edtExecutable->setText(m_tool->executable); ui->edtArgs->setText(m_tool->arguments); ui->edtInput->setText(m_tool->input); ui->edtCommand->setText(m_tool->cmdname); ui->edtWorkingDir->setText(m_tool->workingDir); ui->edtMimeType->setText(m_tool->mimetypes.join(QStringLiteral("; "))); ui->cmbSave->setCurrentIndex(static_cast(m_tool->saveMode)); + ui->cmbOutput->setCurrentIndex(static_cast(m_tool->outputMode)); ui->chkIncludeStderr->setChecked(m_tool->includeStderr); } void KateExternalToolServiceEditor::slotOKClicked() { if (ui->edtName->text().isEmpty() || ui->edtExecutable->text().isEmpty()) { QMessageBox::information(this, i18n("External Tool"), i18n("You must specify at least a name and an executable")); return; } accept(); } void KateExternalToolServiceEditor::showMTDlg() { QString text = i18n("Select the MimeTypes for which to enable this tool."); QStringList list = ui->edtMimeType->text().split(QRegularExpression(QStringLiteral("\\s*;\\s*")), QString::SkipEmptyParts); KMimeTypeChooserDialog d(i18n("Select Mime Types"), text, list, QStringLiteral("text"), this); if (d.exec() == QDialog::Accepted) { ui->edtMimeType->setText(d.chooser()->mimeTypes().join(QStringLiteral(";"))); } } // END KateExternalToolServiceEditor // BEGIN KateExternalToolsConfigWidget KateExternalToolsConfigWidget::KateExternalToolsConfigWidget(QWidget* parent, KateExternalToolsPlugin* plugin) : KTextEditor::ConfigPage(parent) , m_plugin(plugin) { setupUi(this); lbTools->setModel(&m_toolsModel); btnMoveUp->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); btnMoveDown->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); // connect(lbTools, &QTreeView::itemSelectionChanged, this, &KateExternalToolsConfigWidget::slotSelectionChanged); // connect(lbTools, &QTreeView::itemDoubleClicked, this, &KateExternalToolsConfigWidget::slotEdit); connect(btnNew, &QPushButton::clicked, this, &KateExternalToolsConfigWidget::slotNew); connect(btnRemove, &QPushButton::clicked, this, &KateExternalToolsConfigWidget::slotRemove); connect(btnEdit, &QPushButton::clicked, this, &KateExternalToolsConfigWidget::slotEdit); connect(btnMoveUp, &QPushButton::clicked, this, &KateExternalToolsConfigWidget::slotMoveUp); connect(btnMoveDown, &QPushButton::clicked, this, &KateExternalToolsConfigWidget::slotMoveDown); m_config = new KConfig(QStringLiteral("externaltools"), KConfig::NoGlobals, QStandardPaths::ApplicationsLocation); reset(); slotSelectionChanged(); } KateExternalToolsConfigWidget::~KateExternalToolsConfigWidget() { delete m_config; } QString KateExternalToolsConfigWidget::name() const { return i18n("External Tools"); } QString KateExternalToolsConfigWidget::fullName() const { return i18n("External Tools"); } QIcon KateExternalToolsConfigWidget::icon() const { return QIcon::fromTheme(QStringLiteral("system-run")); } void KateExternalToolsConfigWidget::reset() { // clear list m_toolsModel.clear(); // 2 steps: 1st step: create categories const auto tools = m_plugin->tools(); for (auto tool : tools) { auto clone = new KateExternalTool(*tool); auto item = new ToolItem(clone->icon.isEmpty() ? blankIcon() : SmallIcon(clone->icon), clone); auto category = addCategory(clone->category.isEmpty() ? i18n("Uncategorized") : clone->category); category->appendRow(item); } m_changed = false; } QPixmap KateExternalToolsConfigWidget::blankIcon() { QPixmap pm(KIconLoader::SizeSmall, KIconLoader::SizeSmall); pm.fill(); pm.setMask(pm.createHeuristicMask()); return pm; } void KateExternalToolsConfigWidget::apply() { if (!m_changed) return; m_changed = false; // collect all KateExternalTool items std::vector tools; auto rootItem = m_toolsModel.invisibleRootItem(); for (int i = 0; i < rootItem->rowCount(); ++i) { auto categoryItem = rootItem->child(i); for (int child = 0; child < categoryItem->rowCount(); ++child) { auto toolItem = static_cast(categoryItem->child(child)); tools.push_back(toolItem->tool()); } } m_config->group("Global").writeEntry("tools", static_cast(tools.size())); for (size_t i = 0; i < tools.size(); i++) { const QString section = QStringLiteral("Tool ") + QString::number(i); KConfigGroup cg(m_config, section); tools[i]->save(cg); } m_config->sync(); m_plugin->reload(); } void KateExternalToolsConfigWidget::slotSelectionChanged() { // update button state // bool hs = lbTools->currentItem() != nullptr; // btnEdit->setEnabled(hs && dynamic_cast(lbTools->currentItem())); // btnRemove->setEnabled(hs); // btnMoveUp->setEnabled((lbTools->currentRow() > 0) && hs); // btnMoveDown->setEnabled((lbTools->currentRow() < (int)lbTools->count() - 1) && hs); } QStandardItem * KateExternalToolsConfigWidget::addCategory(const QString & category) { // searach for existing category auto items = m_toolsModel.findItems(category); if (!items.empty()) { return items.front(); } // ...otherwise, create it auto item = new QStandardItem(category); m_toolsModel.appendRow(item); return item; } void KateExternalToolsConfigWidget::slotNew() { // display a editor, and if it is OK'd, create a new tool and // create a listbox item for it auto t = new KateExternalTool(); KateExternalToolServiceEditor editor(t, this); if (editor.exec() == QDialog::Accepted) { t->name = editor.ui->edtName->text(); t->icon = editor.ui->btnIcon->icon(); t->executable = editor.ui->edtExecutable->text(); t->arguments = editor.ui->edtArgs->text(); t->input = editor.ui->edtInput->toPlainText(); t->workingDir = editor.ui->edtWorkingDir->text(); t->mimetypes = editor.ui->edtMimeType->text().split(QRegularExpression(QStringLiteral("\\s*;\\s*")), QString::SkipEmptyParts); t->saveMode = static_cast(editor.ui->cmbSave->currentIndex()); + t->outputMode = static_cast(editor.ui->cmbOutput->currentIndex()); t->includeStderr = editor.ui->chkIncludeStderr->isChecked(); // This is sticky, it does not change again, so that shortcuts sticks // TODO check for dups t->actionName = QStringLiteral("externaltool_") + QString(t->name).remove(QRegularExpression(QStringLiteral("\\W+"))); auto item = new ToolItem(t->icon.isEmpty() ? blankIcon() : SmallIcon(t->icon), t); auto category = addCategory(item->tool()->category); category->appendRow(item); emit changed(); m_changed = true; } else { delete t; } } void KateExternalToolsConfigWidget::slotRemove() { auto item = m_toolsModel.itemFromIndex(lbTools->currentIndex()); auto toolItem = dynamic_cast(item); // add the tool action name to a list of removed items, // remove the current listbox item if (toolItem) { m_removed << toolItem->tool()->actionName; toolItem->parent()->removeRow(toolItem->index().row()); delete toolItem;; emit changed(); m_changed = true; } } void KateExternalToolsConfigWidget::slotEdit() { auto item = m_toolsModel.itemFromIndex(lbTools->currentIndex()); auto toolItem = dynamic_cast(item); if (!toolItem) return; // show the item in an editor KateExternalTool* t = toolItem->tool(); KateExternalToolServiceEditor editor(t, this); editor.resize(m_config->group("Editor").readEntry("Size", QSize())); if (editor.exec() == QDialog::Accepted) { const bool elementChanged = ((editor.ui->btnIcon->icon() != t->icon) || (editor.ui->edtName->text() != t->name)); t->name = editor.ui->edtName->text(); t->icon = editor.ui->btnIcon->icon(); t->executable = editor.ui->edtExecutable->text(); t->arguments = editor.ui->edtArgs->text(); t->input = editor.ui->edtInput->toPlainText(); t->cmdname = editor.ui->edtCommand->text(); t->workingDir = editor.ui->edtWorkingDir->text(); t->mimetypes = editor.ui->edtMimeType->text().split(QRegularExpression(QStringLiteral("\\s*;\\s*")), QString::SkipEmptyParts); t->saveMode = static_cast(editor.ui->cmbSave->currentIndex()); + t->outputMode = static_cast(editor.ui->cmbOutput->currentIndex()); t->includeStderr = editor.ui->chkIncludeStderr->isChecked(); // if the icon or name name changed, renew the item if (elementChanged) { toolItem->setText(t->name); toolItem->setIcon(t->icon.isEmpty() ? blankIcon() : SmallIcon(t->icon)); } emit changed(); m_changed = true; } m_config->group("Editor").writeEntry("Size", editor.size()); m_config->sync(); } void KateExternalToolsConfigWidget::slotMoveUp() { // move the current item in the listbox upwards if possible auto item = m_toolsModel.itemFromIndex(lbTools->currentIndex()); // auto toolItem = dynamic_cast(item); if (!item) return; QModelIndex srcParent = item->parent() ? item->parent()->index() : m_toolsModel.invisibleRootItem()->index(); int srcRow = item->index().row(); QModelIndex dstParent = (item->index().row() > 0) ? srcParent : QModelIndex(); int dstRow = item->index().row() > 0 ? (item->index().row() - 1) : 0; bool moved = m_toolsModel.moveRow(srcParent, srcRow, dstParent, dstRow); // slotSelectionChanged(); emit changed(); m_changed = true; } void KateExternalToolsConfigWidget::slotMoveDown() { // move the current item in the listbox downwards if possible auto item = m_toolsModel.itemFromIndex(lbTools->currentIndex()); // auto toolItem = dynamic_cast(item); if (!item) return; // int idx = lbTools->row(item); // if (idx > lbTools->count() - 1) // return; QModelIndex srcParent = item->parent() ? item->parent()->index() : m_toolsModel.invisibleRootItem()->index(); int srcRow = item->index().row(); QModelIndex dstParent = srcParent; int dstRow = item->index().row() + 1; // slotSelectionChanged(); emit changed(); m_changed = true; } // END KateExternalToolsConfigWidget // kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/addons/externaltools/tooldialog.ui b/addons/externaltools/tooldialog.ui index bd9cb57e1..598990b83 100644 --- a/addons/externaltools/tooldialog.ui +++ b/addons/externaltools/tooldialog.ui @@ -1,370 +1,365 @@ ToolDialog 0 0 470 510 Edit Tool Na&me: edtName The name will be displayed in the 'Tools->External Tools' menu. Short name of the tool E&xecutable: edtExecutable The executable used by the command. This is used to check if a tool should be displayed; if not set, the first word of <em>command</em> will be used. Application or interpreter ../../../../../../../../.designer/backup../../../../../../../../.designer/backup QToolButton::InstantPopup Ar&guments: edtArgs &Input: edtInput <p>The script to execute to invoke the tool. The script is passed to /bin/sh for execution. The following macros will be expanded:</p><ul><li><code>%URL</code> - the URL of the current document.</li><li><code>%URLs</code> - a list of the URLs of all open documents.</li><li><code>%directory</code> - the URL of the directory containing the current document.</li><li><code>%filename</code> - the filename of the current document.</li><li><code>%line</code> - the current line of the text cursor in the current view.</li><li><code>%column</code> - the column of the text cursor in the current view.</li><li><code>%selection</code> - the selected text in the current view.</li><li><code>%text</code> - the text of the current document.</li></ul> Optional standard input ../../../../../../../../.designer/backup../../../../../../../../.designer/backup QToolButton::InstantPopup Mime &types: edtMimeType A semicolon-separated list of mime types for which this tool should be available; if this is left empty, the tool is always available. To choose from known mimetypes, press the button on the right. Show tool only for given mime types Click for a dialog that can help you create a list of mimetypes. ../../../../../../../../.designer/backup../../../../../../../../.designer/backup Save: cmbSave You can choose to save the current or all [modified] documents prior to running the command. This is helpful if you want to pass URLs to an application like, for example, an FTP client. None Current Document All Documents Reload current document after execution O&utput: cmbOutput Ignore Insert at Cursor Position Replace Selected Text Replace Current Document Append to Current Document Insert in New Document Display in Pane - - - Embedded Console - - Editor command: edtCommand If you specify a name here, you can invoke the command from the view command line with exttool-the_name_you_specified_here. Please do not use spaces or tabs in the name. Optional command bar name QDialogButtonBox::Cancel|QDialogButtonBox::Ok Working &directory: edtWorkingDir Uses current document path if empty ../../../../../../../../.designer/backup../../../../../../../../.designer/backup QToolButton::InstantPopup Command line arguments ../../../../../../../../.designer/backup../../../../../../../../.designer/backup QToolButton::InstantPopup Include output from stderr KIconButton QPushButton
kiconbutton.h
edtName btnIcon edtExecutable btnExecutable edtArgs btnArgs edtInput btnInput edtWorkingDir btnWorkingDir edtMimeType btnMimeType cmbSave chkReload cmbOutput chkIncludeStderr edtCommand