diff --git a/addons/konsole/kateconsole.cpp b/addons/konsole/kateconsole.cpp index a2a13c387..51284399c 100644 --- a/addons/konsole/kateconsole.cpp +++ b/addons/konsole/kateconsole.cpp @@ -1,500 +1,518 @@ /* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann Copyright (C) 2002 Joseph Wenninger Copyright (C) 2002 Anders Lund Copyright (C) 2007 Anders Lund Copyright (C) 2017 Ederag This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 "kateconsole.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 #include K_PLUGIN_FACTORY_WITH_JSON(KateKonsolePluginFactory, "katekonsoleplugin.json", registerPlugin();) KateKonsolePlugin::KateKonsolePlugin(QObject *parent, const QList &) : KTextEditor::Plugin(parent) { m_previousEditorEnv = qgetenv("EDITOR"); if (!KAuthorized::authorize(QStringLiteral("shell_access"))) { KMessageBox::sorry(nullptr, i18n("You do not have enough karma to access a shell or terminal emulation")); } } KateKonsolePlugin::~KateKonsolePlugin() { qputenv("EDITOR", m_previousEditorEnv.data()); } QObject *KateKonsolePlugin::createView(KTextEditor::MainWindow *mainWindow) { KateKonsolePluginView *view = new KateKonsolePluginView(this, mainWindow); return view; } KTextEditor::ConfigPage *KateKonsolePlugin::configPage(int number, QWidget *parent) { if (number != 0) return nullptr; return new KateKonsoleConfigPage(parent, this); } void KateKonsolePlugin::readConfig() { for (KateKonsolePluginView *view : qAsConst(mViews)) view->readConfig(); } KateKonsolePluginView::KateKonsolePluginView(KateKonsolePlugin *plugin, KTextEditor::MainWindow *mainWindow) : QObject(mainWindow) , m_plugin(plugin) { // init console QWidget *toolview = mainWindow->createToolView(plugin, QStringLiteral("kate_private_plugin_katekonsoleplugin"), KTextEditor::MainWindow::Bottom, QIcon::fromTheme(QStringLiteral("utilities-terminal")), i18n("Terminal")); m_console = new KateConsole(m_plugin, mainWindow, toolview); // register this view m_plugin->mViews.append(this); } KateKonsolePluginView::~KateKonsolePluginView() { // unregister this view m_plugin->mViews.removeAll(this); // cleanup, kill toolview + console QWidget *toolview = m_console->parentWidget(); delete m_console; delete toolview; } void KateKonsolePluginView::readConfig() { m_console->readConfig(); } KateConsole::KateConsole(KateKonsolePlugin *plugin, KTextEditor::MainWindow *mw, QWidget *parent) : QWidget(parent) , m_part(nullptr) , m_mw(mw) , m_toolView(parent) , m_plugin(plugin) { KXMLGUIClient::setComponentName(QStringLiteral("katekonsole"), i18n("Kate Terminal")); setXMLFile(QStringLiteral("ui.rc")); // make sure we have a vertical layout new QVBoxLayout(this); layout()->setContentsMargins(0, 0, 0, 0); QAction *a = actionCollection()->addAction(QStringLiteral("katekonsole_tools_pipe_to_terminal")); a->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal"))); a->setText(i18nc("@action", "&Pipe to Terminal")); connect(a, &QAction::triggered, this, &KateConsole::slotPipeToConsole); a = actionCollection()->addAction(QStringLiteral("katekonsole_tools_sync")); a->setText(i18nc("@action", "S&ynchronize Terminal with Current Document")); connect(a, &QAction::triggered, this, &KateConsole::slotManualSync); a = actionCollection()->addAction(QStringLiteral("katekonsole_tools_run")); a->setText(i18nc("@action", "Run Current Document")); connect(a, &QAction::triggered, this, &KateConsole::slotRun); - a = actionCollection()->addAction(QStringLiteral("katekonsole_tools_toggle_focus")); + a = actionCollection()->addAction(QStringLiteral("katekonsole_tools_toggle_visibility")); a->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal"))); - a->setText(i18nc("@action", "&Focus Terminal")); + a->setText(i18nc("@action", "S&how Terminal")); actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::Key_F4)); + connect(a, &QAction::triggered, this, &KateConsole::slotToggleVisibility); + + a = actionCollection()->addAction(QStringLiteral("katekonsole_tools_toggle_focus")); + a->setIcon(QIcon::fromTheme(QStringLiteral("swap-panels"))); + a->setText(i18nc("@action", "&Focus Terminal")); + actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_F4)); connect(a, &QAction::triggered, this, &KateConsole::slotToggleFocus); m_mw->guiFactory()->addClient(this); readConfig(); } KateConsole::~KateConsole() { m_mw->guiFactory()->removeClient(this); if (m_part) disconnect(m_part, &KParts::ReadOnlyPart::destroyed, this, &KateConsole::slotDestroyed); } void KateConsole::loadConsoleIfNeeded() { if (m_part) return; if (!window() || !parentWidget()) return; if (!window() || !isVisibleTo(window())) return; /** * get konsole part factory */ KPluginFactory *factory = KPluginLoader(QStringLiteral("konsolepart")).factory(); if (!factory) return; m_part = factory->create(this, this); if (!m_part) return; layout()->addWidget(m_part->widget()); // start the terminal qobject_cast(m_part)->showShellInDir(QString()); // KGlobal::locale()->insertCatalog("konsole"); // FIXME KF5: insert catalog setFocusProxy(m_part->widget()); m_part->widget()->show(); connect(m_part, &KParts::ReadOnlyPart::destroyed, this, &KateConsole::slotDestroyed); connect(m_part, SIGNAL(overrideShortcut(QKeyEvent *, bool &)), this, SLOT(overrideShortcut(QKeyEvent *, bool &))); slotSync(); } void KateConsole::slotDestroyed() { m_part = nullptr; m_currentPath.clear(); setFocusProxy(nullptr); // hide the dockwidget if (parentWidget()) { m_mw->hideToolView(m_toolView); } } void KateConsole::overrideShortcut(QKeyEvent *, bool &override) { /** * let konsole handle all shortcuts */ override = true; } void KateConsole::showEvent(QShowEvent *) { if (m_part) return; loadConsoleIfNeeded(); } void KateConsole::cd(const QString &path) { if (m_currentPath == path) return; if (!m_part) return; m_currentPath = path; QString command = QLatin1String(" cd ") + KShell::quoteArg(m_currentPath) + QLatin1Char('\n'); // special handling for some interpreters TerminalInterface *t = qobject_cast(m_part); if (t) { // ghci doesn't allow \space dir names, does allow spaces in dir names // irb can take spaces or \space but doesn't allow " 'path' " if (t->foregroundProcessName() == QLatin1String("irb")) { command = QLatin1String("Dir.chdir(\"") + path + QLatin1String("\") \n"); } else if (t->foregroundProcessName() == QLatin1String("ghc")) { command = QLatin1String(":cd ") + path + QLatin1Char('\n'); } } // Send prior Ctrl-E, Ctrl-U to ensure the line is empty sendInput(QStringLiteral("\x05\x15")); sendInput(command); } void KateConsole::sendInput(const QString &text) { loadConsoleIfNeeded(); if (!m_part) return; TerminalInterface *t = qobject_cast(m_part); if (!t) return; t->sendInput(text); } void KateConsole::slotPipeToConsole() { if (KMessageBox::warningContinueCancel(m_mw->window(), i18n("Do you really want to pipe the text to the console? This will execute any contained commands with your user rights."), i18n("Pipe to Terminal?"), KGuiItem(i18n("Pipe to Terminal")), KStandardGuiItem::cancel(), QStringLiteral("Pipe To Terminal Warning")) != KMessageBox::Continue) return; KTextEditor::View *v = m_mw->activeView(); if (!v) return; if (v->selection()) sendInput(v->selectionText()); else sendInput(v->document()->text()); } void KateConsole::slotSync(KTextEditor::View *) { if (m_mw->activeView()) { QUrl u = m_mw->activeView()->document()->url(); if (u.isValid() && u.isLocalFile()) { QFileInfo fi(u.toLocalFile()); cd(fi.absolutePath()); } else if (!u.isEmpty()) { sendInput(QStringLiteral("### ") + i18n("Sorry, cannot cd into '%1'", u.toLocalFile()) + QLatin1Char('\n')); } } } void KateConsole::slotManualSync() { m_currentPath.clear(); slotSync(); if (!m_part || !m_part->widget()->isVisible()) m_mw->showToolView(parentWidget()); } void KateConsole::slotRun() { if (m_mw->activeView()) { KTextEditor::Document *document = m_mw->activeView()->document(); QUrl u = document->url(); if (!u.isLocalFile()) { QPointer message = new KTextEditor::Message(i18n("Not a local file: '%1'", u.path()), KTextEditor::Message::Error); // auto hide is enabled and set to a sane default value of several seconds. message->setAutoHide(2000); message->setAutoHideMode(KTextEditor::Message::Immediate); document->postMessage(message); return; } // ensure that file is saved if (document->isModified()) { document->save(); } // The string that should be output to terminal, upon acceptance QString output_str; // prefix first output_str += KConfigGroup(KSharedConfig::openConfig(), "Konsole").readEntry("RunPrefix", ""); // then filename if (KConfigGroup(KSharedConfig::openConfig(), "Konsole").readEntry("RemoveExtension", true)) { // append filename without extension (i.e. keep only the basename) output_str += QFileInfo(u.path()).baseName() + QLatin1Char('\n'); } else { // append filename to the terminal output_str += QFileInfo(u.path()).fileName() + QLatin1Char('\n'); } if (KMessageBox::Continue != KMessageBox::warningContinueCancel(m_mw->window(), i18n("Do you really want to Run the document ?\n" "This will execute the following command,\n" "with your user rights, in the terminal:\n" "'%1'", output_str), i18n("Run in Terminal?"), KGuiItem(i18n("Run")), KStandardGuiItem::cancel(), QStringLiteral("Konsole: Run in Terminal Warning"))) { return; } // echo to terminal sendInput(output_str); } } +void KateConsole::slotToggleVisibility() +{ + QAction *action = actionCollection()->action(QStringLiteral("katekonsole_tools_toggle_visibility")); + if (!m_part || !m_part->widget()->isVisible()) { + m_mw->showToolView(parentWidget()); + action->setText(i18nc("@action", "&Hide Terminal")); + } else { + m_mw->hideToolView(m_toolView); + action->setText(i18nc("@action", "S&how Terminal")); + } +} + void KateConsole::slotToggleFocus() { QAction *action = actionCollection()->action(QStringLiteral("katekonsole_tools_toggle_focus")); if (!m_part) { m_mw->showToolView(parentWidget()); action->setText(i18n("Defocus Terminal")); return; // this shows and focuses the konsole } if (!m_part) return; if (m_part->widget()->hasFocus()) { if (m_mw->activeView()) m_mw->activeView()->setFocus(); action->setText(i18n("Focus Terminal")); } else { // show the view if it is hidden if (parentWidget()->isHidden()) m_mw->showToolView(parentWidget()); else // should focus the widget too! m_part->widget()->setFocus(Qt::OtherFocusReason); action->setText(i18n("Defocus Terminal")); } } void KateConsole::readConfig() { disconnect(m_mw, &KTextEditor::MainWindow::viewChanged, this, &KateConsole::slotSync); if (KConfigGroup(KSharedConfig::openConfig(), "Konsole").readEntry("AutoSyncronize", true)) { connect(m_mw, &KTextEditor::MainWindow::viewChanged, this, &KateConsole::slotSync); } if (KConfigGroup(KSharedConfig::openConfig(), "Konsole").readEntry("SetEditor", false)) qputenv("EDITOR", "kate -b"); else qputenv("EDITOR", m_plugin->previousEditorEnv().data()); } KateKonsoleConfigPage::KateKonsoleConfigPage(QWidget *parent, KateKonsolePlugin *plugin) : KTextEditor::ConfigPage(parent) , mPlugin(plugin) { QVBoxLayout *lo = new QVBoxLayout(this); lo->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); lo->setContentsMargins(0, 0, 0, 0); cbAutoSyncronize = new QCheckBox(i18n("&Automatically synchronize the terminal with the current document when possible"), this); lo->addWidget(cbAutoSyncronize); QVBoxLayout *vboxRun = new QVBoxLayout; QGroupBox *groupRun = new QGroupBox(i18n("Run in terminal"), this); // Remove extension cbRemoveExtension = new QCheckBox(i18n("&Remove extension"), this); vboxRun->addWidget(cbRemoveExtension); // Prefix QFrame *framePrefix = new QFrame(this); QHBoxLayout *hboxPrefix = new QHBoxLayout(framePrefix); QLabel *label = new QLabel(i18n("Prefix:"), framePrefix); hboxPrefix->addWidget(label); lePrefix = new QLineEdit(framePrefix); hboxPrefix->addWidget(lePrefix); vboxRun->addWidget(framePrefix); // show warning next time QFrame *frameWarn = new QFrame(this); QHBoxLayout *hboxWarn = new QHBoxLayout(frameWarn); QPushButton *buttonWarn = new QPushButton(i18n("&Show warning next time"), frameWarn); buttonWarn->setWhatsThis( i18n("The next time '%1' is executed, " "make sure a warning window will pop up, " "displaying the command to be sent to terminal, " "for review.", i18n("Run in terminal"))); connect(buttonWarn, &QPushButton::pressed, this, &KateKonsoleConfigPage::slotEnableRunWarning); hboxWarn->addWidget(buttonWarn); vboxRun->addWidget(frameWarn); groupRun->setLayout(vboxRun); lo->addWidget(groupRun); cbSetEditor = new QCheckBox(i18n("Set &EDITOR environment variable to 'kate -b'"), this); lo->addWidget(cbSetEditor); QLabel *tmp = new QLabel(this); tmp->setText(i18n("Important: The document has to be closed to make the console application continue")); lo->addWidget(tmp); reset(); lo->addStretch(); connect(cbAutoSyncronize, &QCheckBox::stateChanged, this, &KateKonsoleConfigPage::changed); connect(cbRemoveExtension, &QCheckBox::stateChanged, this, &KTextEditor::ConfigPage::changed); connect(lePrefix, &QLineEdit::textChanged, this, &KateKonsoleConfigPage::changed); connect(cbSetEditor, &QCheckBox::stateChanged, this, &KateKonsoleConfigPage::changed); } void KateKonsoleConfigPage::slotEnableRunWarning() { KMessageBox::enableMessage(QStringLiteral("Konsole: Run in Terminal Warning")); } QString KateKonsoleConfigPage::name() const { return i18n("Terminal"); } QString KateKonsoleConfigPage::fullName() const { return i18n("Terminal Settings"); } QIcon KateKonsoleConfigPage::icon() const { return QIcon::fromTheme(QStringLiteral("utilities-terminal")); } void KateKonsoleConfigPage::apply() { KConfigGroup config(KSharedConfig::openConfig(), "Konsole"); config.writeEntry("AutoSyncronize", cbAutoSyncronize->isChecked()); config.writeEntry("RemoveExtension", cbRemoveExtension->isChecked()); config.writeEntry("RunPrefix", lePrefix->text()); config.writeEntry("SetEditor", cbSetEditor->isChecked()); config.sync(); mPlugin->readConfig(); } void KateKonsoleConfigPage::reset() { KConfigGroup config(KSharedConfig::openConfig(), "Konsole"); cbAutoSyncronize->setChecked(config.readEntry("AutoSyncronize", true)); cbRemoveExtension->setChecked(config.readEntry("RemoveExtension", false)); lePrefix->setText(config.readEntry("RunPrefix", "")); cbSetEditor->setChecked(config.readEntry("SetEditor", false)); } #include "kateconsole.moc" // kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/addons/konsole/kateconsole.h b/addons/konsole/kateconsole.h index a8f3ee192..15c414f2f 100644 --- a/addons/konsole/kateconsole.h +++ b/addons/konsole/kateconsole.h @@ -1,240 +1,245 @@ /* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann Copyright (C) 2002 Joseph Wenninger Copyright (C) 2002 Anders Lund Copyright (C) 2017 Ederag This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 __KATE_CONSOLE_H__ #define __KATE_CONSOLE_H__ #include #include #include #include #include #include class QShowEvent; namespace KParts { class ReadOnlyPart; } class KateConsole; class KateKonsolePluginView; class KateKonsolePlugin : public KTextEditor::Plugin { Q_OBJECT friend class KateKonsolePluginView; public: explicit KateKonsolePlugin(QObject *parent = nullptr, const QList & = QList()); ~KateKonsolePlugin() override; QObject *createView(KTextEditor::MainWindow *mainWindow) override; int configPages() const override { return 1; } KTextEditor::ConfigPage *configPage(int number = 0, QWidget *parent = nullptr) override; void readConfig(); QByteArray previousEditorEnv() { return m_previousEditorEnv; } private: QList mViews; QByteArray m_previousEditorEnv; }; class KateKonsolePluginView : public QObject { Q_OBJECT public: /** * Constructor. */ KateKonsolePluginView(KateKonsolePlugin *plugin, KTextEditor::MainWindow *mainWindow); /** * Virtual destructor. */ ~KateKonsolePluginView() override; void readConfig(); private: KateKonsolePlugin *m_plugin; KateConsole *m_console; }; /** * KateConsole * This class is used for the internal terminal emulator * It uses internally the konsole part, thx to konsole devs :) */ class KateConsole : public QWidget, public KXMLGUIClient { Q_OBJECT public: /** * construct us * @param mw main window * @param parent toolview */ KateConsole(KateKonsolePlugin *plugin, KTextEditor::MainWindow *mw, QWidget *parent); /** * destruct us */ ~KateConsole() override; void readConfig(); /** * cd to dir * @param path given local directory */ void cd(const QString &path); /** * send given text to console * @param text commands for console */ void sendInput(const QString &text); KTextEditor::MainWindow *mainWindow() { return m_mw; } public Q_SLOTS: /** * pipe current document to console */ void slotPipeToConsole(); /** * synchronize the konsole with the current document (cd to the directory) */ void slotSync(KTextEditor::View *view = nullptr); /** * When syncing is done by the user, also show the terminal if it is hidden */ void slotManualSync(); /** * run the current document in the konsole */ void slotRun(); private Q_SLOTS: /** * the konsole exited ;) * handle that, hide the dock */ void slotDestroyed(); /** * construct console if needed */ void loadConsoleIfNeeded(); + /** + * Show or hide the konsole view as appropriate. + */ + void slotToggleVisibility(); + /** * set or clear focus as appropriate. */ void slotToggleFocus(); /** * Handle that shortcuts are not eaten by console */ void overrideShortcut(QKeyEvent *event, bool &override); protected: /** * the konsole get shown * @param ev show event */ void showEvent(QShowEvent *ev) override; private: /** * console part */ KParts::ReadOnlyPart *m_part; /** * main window of this console */ KTextEditor::MainWindow *m_mw; /** * toolview for this console */ QWidget *m_toolView; KateKonsolePlugin *m_plugin; QString m_currentPath; }; class KateKonsoleConfigPage : public KTextEditor::ConfigPage { Q_OBJECT public: explicit KateKonsoleConfigPage(QWidget *parent = nullptr, KateKonsolePlugin *plugin = nullptr); ~KateKonsoleConfigPage() override { } QString name() const override; QString fullName() const override; QIcon icon() const override; void apply() override; void reset() override; void defaults() override { } private: class QCheckBox *cbAutoSyncronize; class QCheckBox *cbRemoveExtension; class QLineEdit *lePrefix; class QCheckBox *cbSetEditor; KateKonsolePlugin *mPlugin; private Q_SLOTS: /** * Enable the warning dialog for the next "Run in terminal" */ void slotEnableRunWarning(); }; #endif // kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/addons/konsole/ui.rc b/addons/konsole/ui.rc index 778815601..39dd5212a 100644 --- a/addons/konsole/ui.rc +++ b/addons/konsole/ui.rc @@ -1,14 +1,15 @@ - + &Tools + + -