diff --git a/addons/externaltools/tools.cfg b/addons/externaltools/defaultexternaltoolsrc similarity index 92% rename from addons/externaltools/tools.cfg rename to addons/externaltools/defaultexternaltoolsrc index 5e7cc9715..813ce905b 100644 --- a/addons/externaltools/tools.cfg +++ b/addons/externaltools/defaultexternaltoolsrc @@ -1,153 +1,138 @@ [Global] tools=9 version=1 [Tool 0] actionName=externaltool_RunShellScript arguments=-e sh -c "cd %{Document:Path} && pwd && chmod -vc a+x %{Document:FileName} && ./%{Document:FileName} ; echo Press any key to continue. && read -n 1" category= cmdname=run-script executable=konsole icon=system-run input= mimetypes= name=Run Shell Script output=Ignore reload=false save=CurrentDocument workingDir=%{Document:Path} [Tool 1] actionName=externaltool_GoogleSelectedText arguments="https://www.google.com/search?q=%{Document:Selection:Text}" category= cmdname=google executable=xdg-open icon=globe input= mimetypes= name=Google Selected Text output=Ignore reload=false save=None workingDir= [Tool 2] actionName=externaltool_gitcola arguments=-r %{Document:Path} category=Git cmdname=git-cola executable=git-cola icon=git-cola input= mimetypes= name=git-cola output=Ignore reload=false save=None workingDir= [Tool 3] actionName=externaltool_gitk arguments= category=Git cmdname=gitk executable=gitk icon=git-gui input= mimetypes= name=gitk output=Ignore reload=false save=None workingDir=%{Document:Path} [Tool 4] actionName=externaltool_gitblame arguments=gui blame %{Document:FileName} category=Git cmdname=git-blame executable=git icon= input= mimetypes= name=git blame output=Ignore reload=false save=CurrentDocument workingDir=%{Document:Path} [Tool 5] actionName=externaltool_QtQuick2Previewqmlscene arguments=%{Document:FileName} category=Tools cmdname=qml-preview executable=qmlscene icon= input= mimetypes=text/x-qml name=Qt Quick 2 Preview (qmlscene) output=Ignore reload=false save=CurrentDocument workingDir=%{Document:Path} [Tool 6] actionName=externaltool_InsertUUID arguments=%{UUID} category=Tools cmdname=uuid executable=echo icon= input= mimetypes= name=Insert UUID output=InsertAtCursor reload=false save=None workingDir= [Tool 7] actionName=externaltool_ClangFormatFullFile arguments=-i %{Document:FileName} category=Tools cmdname=clang-format-file executable=clang-format icon= input= mimetypes= name=Clang Format Full File output=Ignore reload=true save=CurrentDocument workingDir=%{Document:Path} [Tool 8] actionName=externaltool_ClangFormatSelectedText arguments=-assume-filename=%{Document:FileName} category=Tools cmdname=clang-format-selection executable=clang-format icon= input=\s%{Document:Selection:Text} mimetypes= name=Clang Format Selected Text output=ReplaceSelectedText reload=false save=None workingDir=%{Document:Path} - -[Tool 9] -actionName=externaltool_perl -arguments=%{ENV:KATE_PID -category=Tools -cmdname=perl -executable=echo -icon= -input= -mimetypes= -name=perl -output=DisplayInPane -reload=false -save=None -workingDir= diff --git a/addons/externaltools/externaltoolsplugin.cpp b/addons/externaltools/externaltoolsplugin.cpp index 43517caf0..fe9788d16 100644 --- a/addons/externaltools/externaltoolsplugin.cpp +++ b/addons/externaltools/externaltoolsplugin.cpp @@ -1,282 +1,313 @@ /* 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 "externaltoolsplugin.h" #include "kateexternaltoolsview.h" #include "kateexternaltool.h" #include "kateexternaltoolscommand.h" #include "katetoolrunner.h" #include "kateexternaltoolsconfigwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include +static QVector readDefaultTools() +{ + QVector tools; + KConfig systemConfig(QStringLiteral("defaultexternaltoolsrc")); + KConfigGroup config(&systemConfig, "Global"); + const int toolCount = config.readEntry("tools", 0); + for (int i = 0; i < toolCount; ++i) { + config = KConfigGroup(&systemConfig, QStringLiteral("Tool %1").arg(i)); + + KateExternalTool t; + t.load(config); + tools.push_back(t); + } + return tools; +} + K_PLUGIN_FACTORY_WITH_JSON(KateExternalToolsFactory, "externaltoolsplugin.json", registerPlugin();) KateExternalToolsPlugin::KateExternalToolsPlugin(QObject* parent, const QList&) : KTextEditor::Plugin(parent) { + // read built-in external tools from compiled-in resource file + m_defaultTools = readDefaultTools(); + + // load config from disk reload(); } KateExternalToolsPlugin::~KateExternalToolsPlugin() { delete m_command; m_command = nullptr; } QObject* KateExternalToolsPlugin::createView(KTextEditor::MainWindow* mainWindow) { KateExternalToolsPluginView* view = new KateExternalToolsPluginView(mainWindow, this); connect(this, &KateExternalToolsPlugin::externalToolsChanged, view, &KateExternalToolsPluginView::rebuildMenu); return view; } void KateExternalToolsPlugin::reload() { delete m_command; m_command = nullptr; m_commands.clear(); qDeleteAll(m_tools); m_tools.clear(); KConfig _config(QStringLiteral("externaltools"), KConfig::NoGlobals, QStandardPaths::ApplicationsLocation); KConfigGroup config(&_config, "Global"); const int toolCount = config.readEntry("tools", 0); + const bool firstStart = config.readEntry("firststart", true); - for (int i = 0; i < toolCount; ++i) { - config = KConfigGroup(&_config, QStringLiteral("Tool %1").arg(i)); + if (!firstStart || toolCount > 0) { + // read user config + for (int i = 0; i < toolCount; ++i) { + config = KConfigGroup(&_config, QStringLiteral("Tool %1").arg(i)); - auto t = new KateExternalTool(); - t->load(config); - m_tools.push_back(t); + auto t = new KateExternalTool(); + t->load(config); + m_tools.push_back(t); + } + } else { + // first start -> use system config + for (const auto & tool : m_defaultTools) { + m_tools.push_back(new KateExternalTool(tool)); + } + } - // FIXME test for a command name first! - if (t->hasexec && (!t->cmdname.isEmpty())) { - m_commands.push_back(t->cmdname); + // FIXME test for a command name first! + for (auto tool : m_tools) { + if (tool->hasexec && (!tool->cmdname.isEmpty())) { + m_commands.push_back(tool->cmdname); } } if (KAuthorized::authorizeAction(QStringLiteral("shell_access"))) { m_command = new KateExternalToolsCommand(this); } Q_EMIT externalToolsChanged(); } QStringList KateExternalToolsPlugin::commands() const { return m_commands; } const KateExternalTool* KateExternalToolsPlugin::toolForCommand(const QString& cmd) const { for (auto tool : m_tools) { if (tool->cmdname == cmd) { return tool; } } return nullptr; } const QVector & KateExternalToolsPlugin::tools() const { return m_tools; } void KateExternalToolsPlugin::runTool(const KateExternalTool& tool, KTextEditor::View* view) { // expand the macros in command if any, // and construct a command with an absolute path auto mw = view->mainWindow(); // save documents if requested if (tool.saveMode == KateExternalTool::SaveMode::CurrentDocument) { // only save if modified, to avoid unnecessary recompiles if (view->document()->isModified()) { view->document()->save(); } } else if (tool.saveMode == KateExternalTool::SaveMode::AllDocuments) { foreach (KXMLGUIClient* client, mw->guiFactory()->clients()) { if (QAction* a = client->actionCollection()->action(QStringLiteral("file_save_all"))) { a->trigger(); break; } } } // copy tool std::unique_ptr copy(new KateExternalTool(tool)); // clear previous toolview data auto pluginView = viewForMainWindow(mw); pluginView->clearToolView(); pluginView->addToolStatus(i18n("Running external tool: %1", copy->name)); pluginView->addToolStatus(i18n("- Executable: %1", copy->executable)); pluginView->addToolStatus(i18n("- Arguments : %1", copy->arguments)); pluginView->addToolStatus(i18n("- Input : %1", copy->input)); pluginView->addToolStatus(QString()); // expand macros auto editor = KTextEditor::Editor::instance(); editor->expandText(copy->executable, view, copy->executable); editor->expandText(copy->arguments, view, copy->arguments); editor->expandText(copy->workingDir, view, copy->workingDir); editor->expandText(copy->input, view, copy->input); // Allocate runner on heap such that it lives as long as the child // process is running and does not block the main thread. auto runner = new KateToolRunner(std::move(copy), view, this); // use QueuedConnection, since handleToolFinished deletes the runner connect(runner, &KateToolRunner::toolFinished, this, &KateExternalToolsPlugin::handleToolFinished, Qt::QueuedConnection); runner->run(); } void KateExternalToolsPlugin::handleToolFinished(KateToolRunner* runner, int exitCode, bool crashed) { auto view = runner->view(); if (view && !runner->outputData().isEmpty()) { switch (runner->tool()->outputMode) { case KateExternalTool::OutputMode::InsertAtCursor: { KTextEditor::Document::EditingTransaction transaction(view->document()); view->removeSelection(); view->insertText(runner->outputData()); break; } case KateExternalTool::OutputMode::ReplaceSelectedText: { KTextEditor::Document::EditingTransaction transaction(view->document()); view->removeSelectionText(); view->insertText(runner->outputData()); break; } case KateExternalTool::OutputMode::ReplaceCurrentDocument: { KTextEditor::Document::EditingTransaction transaction(view->document()); view->document()->clear(); view->insertText(runner->outputData()); break; } case KateExternalTool::OutputMode::AppendToCurrentDocument: { view->document()->insertText(view->document()->documentEnd(), runner->outputData()); break; } case KateExternalTool::OutputMode::InsertInNewDocument: { auto mainWindow = view->mainWindow(); auto newView = mainWindow->openUrl({}); newView->insertText(runner->outputData()); mainWindow->activateView(newView->document()); break; } default: break; } } if (view && runner->tool()->reload) { // updates-enabled trick: avoid some flicker const bool wereUpdatesEnabled = view->updatesEnabled(); view->setUpdatesEnabled(false); view->document()->documentReload(); view->setUpdatesEnabled(wereUpdatesEnabled); } KateExternalToolsPluginView* pluginView = runner->view() ? viewForMainWindow(runner->view()->mainWindow()) : nullptr; if (pluginView) { bool hasOutputInPane = false; if (runner->tool()->outputMode == KateExternalTool::OutputMode::DisplayInPane) { pluginView->setOutputData(runner->outputData()); hasOutputInPane = !runner->outputData().isEmpty(); } if (!runner->errorData().isEmpty()) { pluginView->addToolStatus(i18n("Data written to stderr:")); pluginView->addToolStatus(runner->errorData()); } // empty line pluginView->addToolStatus(QString()); // print crash & exit code if (crashed) { pluginView->addToolStatus(i18n("Warning: External tool crashed.")); } pluginView->addToolStatus(i18n("Finished with exit code: %1", exitCode)); if (crashed || exitCode != 0) { pluginView->showToolView(ToolViewFocus::StatusTab); } else if (hasOutputInPane) { pluginView->showToolView(ToolViewFocus::OutputTab); } } delete runner; } int KateExternalToolsPlugin::configPages() const { return 1; } KTextEditor::ConfigPage* KateExternalToolsPlugin::configPage(int number, QWidget* parent) { if (number == 0) { return new KateExternalToolsConfigWidget(parent, this); } return nullptr; } void KateExternalToolsPlugin::registerPluginView(KateExternalToolsPluginView * view) { Q_ASSERT(!m_views.contains(view)); m_views.push_back(view); } void KateExternalToolsPlugin::unregisterPluginView(KateExternalToolsPluginView * view) { Q_ASSERT(m_views.contains(view)); m_views.removeAll(view); } KateExternalToolsPluginView* KateExternalToolsPlugin::viewForMainWindow(KTextEditor::MainWindow* mainWindow) const { for (auto view : m_views) { if (view->mainWindow() == mainWindow) { return view; } } return nullptr; } #include "externaltoolsplugin.moc" // kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/addons/externaltools/externaltoolsplugin.h b/addons/externaltools/externaltoolsplugin.h index 3935c5e53..96cb5cf54 100644 --- a/addons/externaltools/externaltoolsplugin.h +++ b/addons/externaltools/externaltoolsplugin.h @@ -1,128 +1,127 @@ /* 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_EXTERNALTOOLS_PLUGIN_H #define KTEXTEDITOR_EXTERNALTOOLS_PLUGIN_H #include #include namespace KTextEditor { class View; } class KateExternalToolsMenuAction; class KateExternalToolsPluginView; class KateExternalToolsCommand; class KateExternalTool; class KateToolRunner; class KateExternalToolsPlugin : public KTextEditor::Plugin { Q_OBJECT public: explicit KateExternalToolsPlugin(QObject *parent = nullptr, const QList & = QList()); virtual ~KateExternalToolsPlugin(); /** * Reimplemented to return the number of config pages, in this case 1. */ int configPages() const override; /** * Reimplemented to return the KateExternalToolConfigWidget for number==0. */ KTextEditor::ConfigPage *configPage(int number = 0, QWidget *parent = nullptr) override; /** * Reimplemented to instanciate a PluginView for each MainWindow. */ QObject *createView(KTextEditor::MainWindow *mainWindow) override; /** * Reloads the external tools from disk. */ void reload(); /** * Returns a list of KTextEDitor::Command strings. This is needed by * the KateExternalToolsCommand constructor to pass the list of commands to * the KTextEditor::Editor. */ QStringList commands() const; /** * Returns the KateExternalTool for a specific command line command 'cmd. */ const KateExternalTool *toolForCommand(const QString &cmd) const; /** * Returns a list of all existing external tools. */ const QVector &tools() const; /** * Executes the tool based on the view as current document. */ void runTool(const KateExternalTool &tool, KTextEditor::View *view); Q_SIGNALS: /** * This signal is emitted whenever the external tools change. * This is typically the case when external tools were modified, * added, or removed via the config page. */ void externalToolsChanged(); public: /** * Called by the KateExternalToolsPluginView to register itself. */ void registerPluginView(KateExternalToolsPluginView *view); /** * Called by the KateExternalToolsPluginView to unregister itself. */ void unregisterPluginView(KateExternalToolsPluginView *view); /** * Returns the KateExternalToolsPluginView for the given mainWindow. */ KateExternalToolsPluginView *viewForMainWindow(KTextEditor::MainWindow *mainWindow) const; private: + QVector m_defaultTools; QVector m_views; QVector m_tools; QStringList m_commands; KateExternalToolsCommand *m_command = nullptr; -private - Q_SLOT : - /** - * Called whenever an external tool is done. - */ - void - handleToolFinished(KateToolRunner *runner, int exitCode, bool crashed); +private Q_SLOTS: + /** + * Called whenever an external tool is done. + */ + void handleToolFinished(KateToolRunner *runner, int exitCode, bool crashed); }; #endif // kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/addons/externaltools/kateexternaltoolsconfigwidget.cpp b/addons/externaltools/kateexternaltoolsconfigwidget.cpp index 2b80887da..977173f1e 100644 --- a/addons/externaltools/kateexternaltoolsconfigwidget.cpp +++ b/addons/externaltools/kateexternaltoolsconfigwidget.cpp @@ -1,458 +1,459 @@ /* 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 namespace { constexpr int ToolRole = Qt::UserRole + 1; /** * Helper function to create a QStandardItem that internally stores a pointer to a KateExternalTool. */ QStandardItem * newToolItem(const QPixmap& icon, KateExternalTool* tool) { auto item = new QStandardItem(icon, tool->name); 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; } } // BEGIN KateExternalToolServiceEditor KateExternalToolServiceEditor::KateExternalToolServiceEditor(KateExternalTool* tool, QWidget* parent) : QDialog(parent) , 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(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->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); // add support for variable expansion KTextEditor::Editor::instance()->addVariableExpansion( { ui->edtExecutable, ui->edtArgs, ui->edtInput, ui->edtWorkingDir } ); } 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()->setMargin(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 addCategoryAction = addMenu->addAction(i18n("Add Category")); btnAdd->setMenu(addMenu); 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() : SmallIcon(clone->icon), clone); auto category = clone->category.isEmpty() ? m_noCategory : addCategory(clone->category); category->appendRow(item); } lbTools->expandAll(); 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; 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, 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 tool->actionName = QStringLiteral("externaltool_") + QString(tool->name).remove(QRegularExpression(QStringLiteral("\\W+"))); changed = true; } m_config->group("Editor").writeEntry("Size", editor.size()); m_config->sync(); return changed; } 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); // 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()); } //! Helper that ensures that tool->actionName is unique static void makeActionNameUnique(KateExternalTool* tool, const std::vector & tools) { QString name = tool->actionName; int i = 1; bool notUnique = true; while (notUnique) { auto it = std::find_if(tools.cbegin(), tools.cend(), [&name](const KateExternalTool* tool) { return tool->actionName == name; }); if (it == tools.cend()) { break; } name = tool->actionName + QString::number(i); ++i; } tool->actionName = name; } void KateExternalToolsConfigWidget::slotAddTool() { auto t = new KateExternalTool(); if (editTool(t)) { makeActionNameUnique(t, collectTools(m_toolsModel)); auto item = newToolItem(t->icon.isEmpty() ? blankIcon() : SmallIcon(t->icon), t); auto category = currentCategory(); category->appendRow(item); lbTools->setCurrentIndex(item->index()); Q_EMIT changed(); m_changed = true; } 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() : SmallIcon(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/plugin.qrc b/addons/externaltools/plugin.qrc index 929d46dc3..6fd115bfc 100644 --- a/addons/externaltools/plugin.qrc +++ b/addons/externaltools/plugin.qrc @@ -1,9 +1,9 @@ ui.rc - - tools.cfg + + defaultexternaltoolsrc