diff --git a/addons/externaltools/kateexternaltool.cpp b/addons/externaltools/kateexternaltool.cpp index faff59dce..e45eb6aa1 100644 --- a/addons/externaltools/kateexternaltool.cpp +++ b/addons/externaltools/kateexternaltool.cpp @@ -1,150 +1,161 @@ /* 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 > #include namespace { QString toString(KateExternalTool::SaveMode saveMode) { switch (saveMode) { case KateExternalTool::SaveMode::None: return QStringLiteral("None"); case KateExternalTool::SaveMode::CurrentDocument: return QStringLiteral("CurrentDocument"); case KateExternalTool::SaveMode::AllDocuments: return QStringLiteral("AllDocuments"); } Q_ASSERT(false); // yout forgot a case above return QStringLiteral("None"); } KateExternalTool::SaveMode toSaveMode(const QString &mode) { if (mode == QStringLiteral("None")) return KateExternalTool::SaveMode::None; if (mode == QStringLiteral("CurrentDocument")) return KateExternalTool::SaveMode::CurrentDocument; if (mode == QStringLiteral("AllDocuments")) return KateExternalTool::SaveMode::AllDocuments; return KateExternalTool::SaveMode::None; } QString toString(KateExternalTool::OutputMode outputMode) { switch (outputMode) { case KateExternalTool::OutputMode::Ignore: return QStringLiteral("Ignore"); case KateExternalTool::OutputMode::InsertAtCursor: return QStringLiteral("InsertAtCursor"); case KateExternalTool::OutputMode::ReplaceSelectedText: return QStringLiteral("ReplaceSelectedText"); case KateExternalTool::OutputMode::ReplaceCurrentDocument: return QStringLiteral("ReplaceCurrentDocument"); case KateExternalTool::OutputMode::AppendToCurrentDocument: return QStringLiteral("AppendToCurrentDocument"); case KateExternalTool::OutputMode::InsertInNewDocument: return QStringLiteral("InsertInNewDocument"); case KateExternalTool::OutputMode::CopyToClipboard: return QStringLiteral("CopyToClipboard"); case KateExternalTool::OutputMode::DisplayInPane: return QStringLiteral("DisplayInPane"); } Q_ASSERT(false); // yout forgot a case above return QStringLiteral("Ignore"); } KateExternalTool::OutputMode toOutputMode(const QString &mode) { if (mode == QStringLiteral("Ignore")) return KateExternalTool::OutputMode::Ignore; if (mode == QStringLiteral("InsertAtCursor")) return KateExternalTool::OutputMode::InsertAtCursor; if (mode == QStringLiteral("ReplaceSelectedText")) return KateExternalTool::OutputMode::ReplaceSelectedText; if (mode == QStringLiteral("ReplaceCurrentDocument")) return KateExternalTool::OutputMode::ReplaceCurrentDocument; if (mode == QStringLiteral("AppendToCurrentDocument")) return KateExternalTool::OutputMode::AppendToCurrentDocument; if (mode == QStringLiteral("InsertInNewDocument")) return KateExternalTool::OutputMode::InsertInNewDocument; if (mode == QStringLiteral("CopyToClipboard")) return KateExternalTool::OutputMode::CopyToClipboard; if (mode == QStringLiteral("DisplayInPane")) return KateExternalTool::OutputMode::DisplayInPane; return KateExternalTool::OutputMode::Ignore; } } 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 = toSaveMode(cg.readEntry("save", "None")); reload = cg.readEntry("reload", false); outputMode = toOutputMode(cg.readEntry("output", "Ignore")); 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", toString(saveMode)); cg.writeEntry("reload", reload); cg.writeEntry("output", toString(outputMode)); } +QString KateExternalTool::translatedName() const +{ + return name.isEmpty() ? QString() : i18n(name.toUtf8().data()); +} + +QString KateExternalTool::translatedCategory() const +{ + return category.isEmpty() ? QString() : i18n(category.toUtf8().data()); +} + 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.reload == rhs.reload && lhs.outputMode == rhs.outputMode; } // kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/addons/externaltools/kateexternaltool.h b/addons/externaltools/kateexternaltool.h index 014ab32c4..6fea8d617 100644 --- a/addons/externaltools/kateexternaltool.h +++ b/addons/externaltools/kateexternaltool.h @@ -1,118 +1,128 @@ /* 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, ReplaceCurrentDocument, AppendToCurrentDocument, InsertInNewDocument, CopyToClipboard, 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 for persistent keyboard shortcuts. /// 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; /// Reload current document after execution bool reload = false; /// Defines where to redirect the tool's output OutputMode outputMode = OutputMode::Ignore; 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; + + /** + * Returns the translated name if possible. + */ + QString translatedName() const; + + /** + * Returns the translated category if possible. + */ + QString translatedCategory() 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 bf9d8583b..cb819015b 100644 --- a/addons/externaltools/kateexternaltoolsconfigwidget.cpp +++ b/addons/externaltools/kateexternaltoolsconfigwidget.cpp @@ -1,562 +1,562 @@ /* 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 "kateexternaltoolsconfigwidget.h" #include "externaltoolsplugin.h" #include "kateexternaltool.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 namespace { constexpr int ToolRole = Qt::UserRole + 1; /** * Helper function to create a QStandardItem that internally stores a pointer to a KateExternalTool. */ QStandardItem *newToolItem(const QIcon &icon, KateExternalTool *tool) { - auto item = new QStandardItem(icon, i18n(tool->name.toUtf8().data())); + auto item = new QStandardItem(icon, tool->translatedName()); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled); item->setData(QVariant::fromValue(reinterpret_cast(tool)), ToolRole); return item; } /** * Helper function to return an internally stored KateExternalTool for a QStandardItem. * If a nullptr is returned, it means the QStandardItem is a category. */ KateExternalTool *toolForItem(QStandardItem *item) { return item ? reinterpret_cast(item->data(ToolRole).value()) : nullptr; } QIcon blankIcon() { QPixmap pm(KIconLoader::SizeSmall, KIconLoader::SizeSmall); pm.fill(); pm.setMask(pm.createHeuristicMask()); return QIcon(pm); } //! Helper that ensures that tool->actionName is unique static void makeActionNameUnique(KateExternalTool *tool, const std::vector &tools) { QString name = tool->actionName; int i = 1; while (true) { auto it = std::find_if(tools.cbegin(), tools.cend(), [tool, &name](const KateExternalTool *t) { return (t != tool) && (t->actionName == name); }); if (it == tools.cend()) { break; } name = tool->actionName + QString::number(i); ++i; } tool->actionName = name; } /** * Helper that ensures that the tool->cmdname is unique */ void makeEditorCommandUnique(KateExternalTool *tool, const std::vector &tools) { // empty command line name is OK if (tool->cmdname.isEmpty()) { return; } QString cmdname = tool->cmdname; int i = 1; while (true) { auto it = std::find_if(tools.cbegin(), tools.cend(), [tool, &cmdname](const KateExternalTool *t) { return (t != tool) && (t->cmdname == cmdname); }); if (it == tools.cend()) { break; } cmdname = tool->cmdname + QString::number(i); ++i; } tool->cmdname = cmdname; } static KateExternalTool defaultTool(const QString &actionName, const QVector &defaultTools) { auto it = std::find_if(defaultTools.cbegin(), defaultTools.cend(), [actionName](const KateExternalTool &defaultTool) { return actionName == defaultTool.actionName; }); return (it != defaultTools.cend()) ? *it : KateExternalTool(); } static bool isDefaultTool(KateExternalTool *tool, const QVector &defaultTools) { return tool && !defaultTool(tool->actionName, defaultTools).actionName.isEmpty(); } } // BEGIN KateExternalToolServiceEditor KateExternalToolServiceEditor::KateExternalToolServiceEditor(KateExternalTool *tool, KateExternalToolsPlugin *plugin, QWidget *parent) : QDialog(parent) , m_plugin(plugin) , m_tool(tool) { setWindowTitle(i18n("Edit External Tool")); setWindowIcon(QIcon::fromTheme(QStringLiteral("system-run"))); 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(i18n(m_tool->name.toUtf8().data())); + ui->edtName->setText(m_tool->translatedName()); 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->edtWorkingDir->setText(m_tool->workingDir); ui->edtMimeType->setText(m_tool->mimetypes.join(QStringLiteral("; "))); ui->cmbSave->setCurrentIndex(static_cast(m_tool->saveMode)); ui->chkReload->setChecked(m_tool->reload); ui->cmbOutput->setCurrentIndex(static_cast(m_tool->outputMode)); ui->edtCommand->setText(m_tool->cmdname); static const QRegularExpressionValidator cmdLineValidator(QRegularExpression(QStringLiteral("[\\w-]*"))); ui->edtCommand->setValidator(&cmdLineValidator); if (isDefaultTool(tool, m_plugin->defaultTools())) { ui->buttonBox->setStandardButtons(ui->buttonBox->standardButtons() | QDialogButtonBox::RestoreDefaults); ui->buttonBox->setToolTip(i18n("Revert tool to default settings")); connect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, [this, tool]() { const auto t = defaultTool(tool->actionName, m_plugin->defaultTools()); - ui->edtName->setText(i18n(t.name.toUtf8().data())); + ui->edtName->setText(t.translatedName()); ui->btnIcon->setIcon(t.icon); ui->edtExecutable->setText(t.executable); ui->edtArgs->setText(t.arguments); ui->edtInput->setText(t.input); ui->edtWorkingDir->setText(t.workingDir); ui->edtMimeType->setText(t.mimetypes.join(QStringLiteral("; "))); ui->cmbSave->setCurrentIndex(static_cast(t.saveMode)); ui->chkReload->setChecked(t.reload); ui->cmbOutput->setCurrentIndex(static_cast(t.outputMode)); ui->edtCommand->setText(t.cmdname); }); } // add support for variable expansion KTextEditor::Editor::instance()->addVariableExpansion({ui->edtExecutable->lineEdit(), ui->edtArgs, ui->edtInput, ui->edtWorkingDir->lineEdit()}); } 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 static std::vector childItems(const QStandardItem *item) { // collect all KateExternalTool items std::vector children; for (int i = 0; i < item->rowCount(); ++i) { children.push_back(item->child(i)); } return children; } static std::vector collectTools(const QStandardItemModel &model) { std::vector tools; for (auto categoryItem : childItems(model.invisibleRootItem())) { for (auto child : childItems(categoryItem)) { auto tool = toolForItem(child); Q_ASSERT(tool != nullptr); tools.push_back(tool); } } return tools; } // BEGIN KateExternalToolsConfigWidget KateExternalToolsConfigWidget::KateExternalToolsConfigWidget(QWidget *parent, KateExternalToolsPlugin *plugin) : KTextEditor::ConfigPage(parent) , m_plugin(plugin) { setupUi(this); layout()->setContentsMargins(0, 0, 0, 0); lbTools->setModel(&m_toolsModel); lbTools->setSelectionMode(QAbstractItemView::SingleSelection); lbTools->setDragEnabled(true); lbTools->setAcceptDrops(true); lbTools->setDefaultDropAction(Qt::MoveAction); lbTools->setDropIndicatorShown(true); lbTools->setDragDropOverwriteMode(false); lbTools->setDragDropMode(QAbstractItemView::InternalMove); // Add... button popup menu auto addMenu = new QMenu(); auto addToolAction = addMenu->addAction(i18n("Add Tool...")); auto addDefaultsMenu = addMenu->addMenu(i18n("Add Tool from Defaults")); addMenu->addSeparator(); auto addCategoryAction = addMenu->addAction(i18n("Add Category")); btnAdd->setMenu(addMenu); connect(addDefaultsMenu, &QMenu::aboutToShow, [this, addDefaultsMenu]() { lazyInitDefaultsMenu(addDefaultsMenu); }); connect(addCategoryAction, &QAction::triggered, this, &KateExternalToolsConfigWidget::slotAddCategory); connect(addToolAction, &QAction::triggered, this, &KateExternalToolsConfigWidget::slotAddTool); connect(btnRemove, &QPushButton::clicked, this, &KateExternalToolsConfigWidget::slotRemove); connect(btnEdit, &QPushButton::clicked, this, &KateExternalToolsConfigWidget::slotEdit); connect(lbTools->selectionModel(), &QItemSelectionModel::currentChanged, [this]() { slotSelectionChanged(); }); connect(lbTools, &QTreeView::doubleClicked, this, &KateExternalToolsConfigWidget::slotEdit); m_config = new KConfig(QStringLiteral("externaltools"), KConfig::NoGlobals, QStandardPaths::ApplicationsLocation); // reset triggers a reload of the existing tools reset(); slotSelectionChanged(); connect(&m_toolsModel, &QStandardItemModel::itemChanged, [this]() { m_changed = true; Q_EMIT changed(); }); } KateExternalToolsConfigWidget::~KateExternalToolsConfigWidget() { clearTools(); 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() { clearTools(); m_toolsModel.invisibleRootItem()->setFlags(Qt::NoItemFlags); // the "Uncategorized" category always exists m_noCategory = addCategory(i18n("Uncategorized")); m_noCategory->setFlags(Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled); // create other tools and categories const auto tools = m_plugin->tools(); for (auto tool : tools) { auto clone = new KateExternalTool(*tool); auto item = newToolItem(clone->icon.isEmpty() ? blankIcon() : QIcon::fromTheme(clone->icon), clone); auto category = clone->category.isEmpty() ? m_noCategory : addCategory(clone->category); category->appendRow(item); } lbTools->expandAll(); m_changed = false; } void KateExternalToolsConfigWidget::apply() { if (!m_changed) return; m_changed = false; // collect all KateExternalTool items std::vector tools; for (auto categoryItem : childItems(m_toolsModel.invisibleRootItem())) { const QString category = (categoryItem == m_noCategory) ? QString() : categoryItem->text(); for (auto child : childItems(categoryItem)) { auto tool = toolForItem(child); Q_ASSERT(tool != nullptr); // at this point, we have to overwrite the category, since it may have changed (and we never tracked this) tool->category = category; tools.push_back(tool); } } // write tool configuration to disk m_config->group("Global").writeEntry("firststart", false); 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 auto item = m_toolsModel.itemFromIndex(lbTools->currentIndex()); const bool isToolItem = toolForItem(item) != nullptr; const bool isCategory = item && !isToolItem; btnEdit->setEnabled(isToolItem || isCategory); btnRemove->setEnabled(isToolItem); } bool KateExternalToolsConfigWidget::editTool(KateExternalTool *tool) { bool changed = false; KateExternalToolServiceEditor editor(tool, m_plugin, this); editor.resize(m_config->group("Editor").readEntry("Size", QSize())); if (editor.exec() == QDialog::Accepted) { tool->name = editor.ui->edtName->text(); tool->icon = editor.ui->btnIcon->icon(); tool->executable = editor.ui->edtExecutable->text(); tool->arguments = editor.ui->edtArgs->text(); tool->input = editor.ui->edtInput->toPlainText(); tool->workingDir = editor.ui->edtWorkingDir->text(); tool->mimetypes = editor.ui->edtMimeType->text().split(QRegularExpression(QStringLiteral("\\s*;\\s*")), QString::SkipEmptyParts); tool->saveMode = static_cast(editor.ui->cmbSave->currentIndex()); tool->reload = editor.ui->chkReload->isChecked(); tool->outputMode = static_cast(editor.ui->cmbOutput->currentIndex()); tool->cmdname = editor.ui->edtCommand->text(); // sticky action collection name, never changes again, so that shortcuts stay if (tool->actionName.isEmpty()) { tool->actionName = QStringLiteral("externaltool_") + QString(tool->name).remove(QRegularExpression(QStringLiteral("\\W+"))); } const auto tools = collectTools(m_toolsModel); makeActionNameUnique(tool, tools); makeEditorCommandUnique(tool, tools); changed = true; } m_config->group("Editor").writeEntry("Size", editor.size()); m_config->sync(); return changed; } void KateExternalToolsConfigWidget::lazyInitDefaultsMenu(QMenu *defaultsMenu) { if (!defaultsMenu->isEmpty()) { return; } // create tool actions std::map categories; // first add categorized actions, such that the submenus appear at the top int defaultToolsIndex = 0; for (const auto &tool : m_plugin->defaultTools()) { - const QString category = tool.category.isEmpty() ? i18n("Uncategorized") : i18n(tool.category.toUtf8().data()); + const QString category = tool.category.isEmpty() ? i18n("Uncategorized") : tool.translatedCategory(); auto categoryMenu = categories[category]; if (!categoryMenu) { categoryMenu = new QMenu(category, this); categories[category] = categoryMenu; defaultsMenu->addMenu(categoryMenu); } - auto a = categoryMenu->addAction(QIcon::fromTheme(tool.icon), i18n(tool.name.toUtf8().data())); + auto a = categoryMenu->addAction(QIcon::fromTheme(tool.icon), tool.translatedName()); a->setData(defaultToolsIndex); connect(a, &QAction::triggered, [this, a]() { slotAddDefaultTool(a->data().toInt()); }); ++defaultToolsIndex; } } void KateExternalToolsConfigWidget::slotAddDefaultTool(int defaultToolsIndex) { const auto &defaultTools = m_plugin->defaultTools(); if (defaultToolsIndex < 0 || defaultToolsIndex > defaultTools.size()) { return; } addNewTool(new KateExternalTool(defaultTools[defaultToolsIndex])); } void KateExternalToolsConfigWidget::addNewTool(KateExternalTool *tool) { const auto tools = collectTools(m_toolsModel); makeActionNameUnique(tool, tools); makeEditorCommandUnique(tool, tools); auto item = newToolItem(tool->icon.isEmpty() ? blankIcon() : QIcon::fromTheme(tool->icon), tool); - auto category = addCategory(i18n(tool->category.toUtf8().data())); + auto category = addCategory(tool->translatedCategory()); category->appendRow(item); lbTools->setCurrentIndex(item->index()); Q_EMIT changed(); m_changed = true; } QStandardItem *KateExternalToolsConfigWidget::addCategory(const QString &translatedCategory) { - if (translatedCategory.isEmpty()) { + if (translatedCategory.isEmpty() || (m_noCategory && translatedCategory == i18n("Uncategorized"))) { return m_noCategory; } // search for existing category auto items = m_toolsModel.findItems(translatedCategory); if (!items.empty()) { return items.front(); } // ...otherwise, create it auto item = new QStandardItem(translatedCategory); // for now, categories are not movable, otherwise, the use can move a // category into another category, which is not supported right now item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled | Qt::ItemIsEditable); m_toolsModel.appendRow(item); return item; } QStandardItem *KateExternalToolsConfigWidget::currentCategory() const { auto index = lbTools->currentIndex(); if (!index.isValid()) { return m_noCategory; } auto item = m_toolsModel.itemFromIndex(index); auto tool = toolForItem(item); if (tool) { // the parent of a ToolItem is always a category return item->parent(); } // item is no ToolItem, so we must have a category at hand return item; } void KateExternalToolsConfigWidget::clearTools() { // collect all KateExternalTool items and delete them, since they are copies std::vector tools = collectTools(m_toolsModel); qDeleteAll(tools); tools.clear(); m_toolsModel.clear(); } void KateExternalToolsConfigWidget::slotAddCategory() { // find unique name QString name = i18n("New Category"); int i = 1; while (!m_toolsModel.findItems(name, Qt::MatchFixedString).isEmpty()) { name = (i18n("New Category %1", i++)); } // add category and switch to edit mode auto item = addCategory(name); lbTools->edit(item->index()); } void KateExternalToolsConfigWidget::slotAddTool() { auto t = new KateExternalTool(); if (editTool(t)) { addNewTool(t); } else { delete t; } } void KateExternalToolsConfigWidget::slotRemove() { auto item = m_toolsModel.itemFromIndex(lbTools->currentIndex()); auto tool = toolForItem(item); if (tool) { item->parent()->removeRow(item->index().row()); delete tool; Q_EMIT changed(); m_changed = true; } } void KateExternalToolsConfigWidget::slotEdit() { auto item = m_toolsModel.itemFromIndex(lbTools->currentIndex()); auto tool = toolForItem(item); if (!tool) { if (item) { lbTools->edit(item->index()); } return; } // show the item in an editor if (editTool(tool)) { // renew the icon and name item->setText(tool->name); item->setIcon(tool->icon.isEmpty() ? blankIcon() : QIcon::fromTheme(tool->icon)); Q_EMIT changed(); m_changed = true; } } // END KateExternalToolsConfigWidget // kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/addons/externaltools/kateexternaltoolsview.cpp b/addons/externaltools/kateexternaltoolsview.cpp index 768760dee..c0f4c9623 100644 --- a/addons/externaltools/kateexternaltoolsview.cpp +++ b/addons/externaltools/kateexternaltoolsview.cpp @@ -1,271 +1,271 @@ /* 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 "kateexternaltoolsview.h" #include "externaltoolsplugin.h" #include "kateexternaltool.h" #include "ui_toolview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // BEGIN KateExternalToolsMenuAction KateExternalToolsMenuAction::KateExternalToolsMenuAction(const QString &text, KActionCollection *collection, KateExternalToolsPlugin *plugin, KTextEditor::MainWindow *mw) : KActionMenu(text, mw) , m_plugin(plugin) , m_mainwindow(mw) , m_actionCollection(collection) { reload(); // track active view to adapt enabled tool actions connect(mw, &KTextEditor::MainWindow::viewChanged, this, &KateExternalToolsMenuAction::slotViewChanged); } KateExternalToolsMenuAction::~KateExternalToolsMenuAction() = default; void KateExternalToolsMenuAction::reload() { // clear action collection bool needs_readd = (m_actionCollection->takeAction(this) != nullptr); m_actionCollection->clear(); if (needs_readd) m_actionCollection->addAction(QStringLiteral("tools_external"), this); menu()->clear(); // create tool actions std::map categories; std::vector uncategorizedActions; // first add categorized actions, such that the submenus appear at the top for (auto tool : m_plugin->tools()) { if (tool->hasexec) { - auto a = new QAction(tool->name, this); + auto a = new QAction(tool->translatedName(), this); a->setIcon(QIcon::fromTheme(tool->icon)); a->setData(QVariant::fromValue(tool)); connect(a, &QAction::triggered, [this, a]() { m_plugin->runTool(*a->data().value(), m_mainwindow->activeView()); }); m_actionCollection->addAction(i18n(tool->actionName.toUtf8().data()), a); if (!tool->category.isEmpty()) { auto categoryMenu = categories[tool->category]; if (!categoryMenu) { - categoryMenu = new KActionMenu(i18n(tool->category.toUtf8().data()), this); + categoryMenu = new KActionMenu(tool->translatedCategory(), this); categories[tool->category] = categoryMenu; addAction(categoryMenu); } categoryMenu->addAction(a); } else { uncategorizedActions.push_back(a); } } } // now add uncategorized actions below for (auto uncategorizedAction : uncategorizedActions) { addAction(uncategorizedAction); } addSeparator(); auto cfgAction = new QAction(i18n("Configure..."), this); addAction(cfgAction); connect(cfgAction, &QAction::triggered, this, &KateExternalToolsMenuAction::showConfigPage, Qt::QueuedConnection); // load shortcuts KSharedConfig::Ptr pConfig = KSharedConfig::openConfig(QStringLiteral("externaltools"), KConfig::NoGlobals, QStandardPaths::ApplicationsLocation); KConfigGroup config(pConfig, "Global"); config = KConfigGroup(pConfig, "Shortcuts"); m_actionCollection->readSettings(&config); slotViewChanged(m_mainwindow->activeView()); } void KateExternalToolsMenuAction::slotViewChanged(KTextEditor::View *view) { // no active view, oh oh if (!view) { return; } // try to enable/disable to match current mime type const QString mimeType = view->document()->mimeType(); const auto actions = m_actionCollection->actions(); for (QAction *action : actions) { if (action && action->data().value()) { auto tool = action->data().value(); action->setEnabled(tool->matchesMimetype(mimeType)); } } } void KateExternalToolsMenuAction::showConfigPage() { m_mainwindow->showPluginConfigPage(m_plugin, 0); } // END KateExternalToolsMenuAction // BEGIN KateExternalToolsPluginView KateExternalToolsPluginView::KateExternalToolsPluginView(KTextEditor::MainWindow *mainWindow, KateExternalToolsPlugin *plugin) : QObject(mainWindow) , m_plugin(plugin) , m_mainWindow(mainWindow) , m_outputDoc(new QTextDocument(this)) , m_statusDoc(new QTextDocument(this)) { m_plugin->registerPluginView(this); KXMLGUIClient::setComponentName(QLatin1String("externaltools"), i18n("External Tools")); setXMLFile(QLatin1String("ui.rc")); if (KAuthorized::authorizeAction(QStringLiteral("shell_access"))) { m_externalToolsMenu = new KateExternalToolsMenuAction(i18n("External Tools"), actionCollection(), plugin, mainWindow); actionCollection()->addAction(QStringLiteral("tools_external"), m_externalToolsMenu); m_externalToolsMenu->setWhatsThis(i18n("Launch external helper applications")); } mainWindow->guiFactory()->addClient(this); // ESC should close & hide ToolView connect(m_mainWindow, &KTextEditor::MainWindow::unhandledShortcutOverride, [this](QEvent *event) { auto keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Escape && keyEvent->modifiers() == Qt::NoModifier) { deleteToolView(); } }); } KateExternalToolsPluginView::~KateExternalToolsPluginView() { m_plugin->unregisterPluginView(this); m_mainWindow->guiFactory()->removeClient(this); deleteToolView(); delete m_externalToolsMenu; m_externalToolsMenu = nullptr; } void KateExternalToolsPluginView::rebuildMenu() { if (m_externalToolsMenu) { KXMLGUIFactory *f = factory(); f->removeClient(this); reloadXML(); m_externalToolsMenu->reload(); f->addClient(this); } } KTextEditor::MainWindow *KateExternalToolsPluginView::mainWindow() const { return m_mainWindow; } void KateExternalToolsPluginView::createToolView() { if (!m_toolView) { m_toolView = mainWindow()->createToolView(m_plugin, QStringLiteral("ktexteditor_plugin_externaltools"), KTextEditor::MainWindow::Bottom, QIcon::fromTheme(QStringLiteral("system-run")), i18n("External Tools")); m_ui = new Ui::ToolView(); m_ui->setupUi(m_toolView); // set the documents m_ui->teOutput->setDocument(m_outputDoc); m_ui->teStatus->setDocument(m_statusDoc); // use fixed font for displaying status and output text const auto fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); m_ui->teOutput->setFont(fixedFont); m_ui->teStatus->setFont(fixedFont); // close button to delete tool view auto btnClose = new QToolButton(); btnClose->setAutoRaise(true); btnClose->setIcon(QIcon::fromTheme(QStringLiteral("tab-close"))); connect(btnClose, &QToolButton::clicked, this, &KateExternalToolsPluginView::deleteToolView); m_ui->tabWidget->setCornerWidget(btnClose); } } void KateExternalToolsPluginView::showToolView(ToolViewFocus tab) { createToolView(); if (tab == ToolViewFocus::OutputTab) { m_ui->tabWidget->setCurrentWidget(m_ui->tabOutput); } else { m_ui->tabWidget->setCurrentWidget(m_ui->tabStatus); } mainWindow()->showToolView(m_toolView); } void KateExternalToolsPluginView::clearToolView() { m_outputDoc->clear(); m_statusDoc->clear(); } void KateExternalToolsPluginView::addToolStatus(const QString &message) { QTextCursor cursor(m_statusDoc); cursor.movePosition(QTextCursor::End); cursor.insertText(message); cursor.insertText(QStringLiteral("\n")); } void KateExternalToolsPluginView::setOutputData(const QString &data) { QTextCursor cursor(m_outputDoc); cursor.movePosition(QTextCursor::End); cursor.insertText(data); } void KateExternalToolsPluginView::deleteToolView() { if (m_toolView) { delete m_ui; m_ui = nullptr; delete m_toolView; m_toolView = nullptr; } } // END KateExternalToolsPluginView // kate: space-indent on; indent-width 4; replace-tabs on;