File indexing completed on 2024-04-28 05:48:42

0001 /* This file is part of the KDE project
0002    SPDX-FileCopyrightText: 2001 Christoph Cullmann <cullmann@kde.org>
0003    SPDX-FileCopyrightText: 2002 Joseph Wenninger <jowenn@kde.org>
0004    SPDX-FileCopyrightText: 2002 Anders Lund <anders.lund@lund.tdcadsl.dk>
0005    SPDX-FileCopyrightText: 2007 Anders Lund <anders@alweb.dk>
0006    SPDX-FileCopyrightText: 2017 Ederag <edera@gmx.fr>
0007 
0008    SPDX-License-Identifier: LGPL-2.0-only
0009 */
0010 
0011 #include "kateconsole.h"
0012 
0013 #include <KLocalizedString>
0014 #include <ktexteditor/document.h>
0015 #include <ktexteditor/message.h>
0016 #include <ktexteditor/view.h>
0017 
0018 #include <KActionCollection>
0019 #include <KShell>
0020 #include <QAction>
0021 #include <kde_terminal_interface.h>
0022 #include <kparts/part.h>
0023 
0024 #include <KMessageBox>
0025 
0026 #include <QApplication>
0027 #include <QCheckBox>
0028 #include <QFileInfo>
0029 #include <QGroupBox>
0030 #include <QIcon>
0031 #include <QLabel>
0032 #include <QLineEdit>
0033 #include <QPainter>
0034 #include <QPushButton>
0035 #include <QShowEvent>
0036 #include <QStyle>
0037 #include <QTabWidget>
0038 #include <QVBoxLayout>
0039 
0040 #include <KAuthorized>
0041 #include <KColorScheme>
0042 #include <KConfigGroup>
0043 #include <KPluginFactory>
0044 #include <KSharedConfig>
0045 #include <KXMLGUIFactory>
0046 
0047 K_PLUGIN_FACTORY_WITH_JSON(KateKonsolePluginFactory, "katekonsoleplugin.json", registerPlugin<KateKonsolePlugin>();)
0048 
0049 static const QStringList s_escapeExceptions{QStringLiteral("vi"), QStringLiteral("vim"), QStringLiteral("nvim"), QStringLiteral("git")};
0050 
0051 KateKonsolePlugin::KateKonsolePlugin(QObject *parent, const QVariantList &)
0052     : KTextEditor::Plugin(parent)
0053     , m_previousEditorEnv(qgetenv("EDITOR"))
0054 {
0055     if (!KAuthorized::authorize(QStringLiteral("shell_access"))) {
0056         KMessageBox::error(nullptr, i18n("You do not have enough karma to access a shell or terminal emulation"));
0057     }
0058 }
0059 
0060 void setEditorEnv(const QByteArray &value)
0061 {
0062     if (value.isNull()) {
0063         qunsetenv("EDITOR");
0064     } else {
0065         qputenv("EDITOR", value.data());
0066     }
0067 }
0068 
0069 KateKonsolePlugin::~KateKonsolePlugin()
0070 {
0071     setEditorEnv(m_previousEditorEnv);
0072 }
0073 
0074 QObject *KateKonsolePlugin::createView(KTextEditor::MainWindow *mainWindow)
0075 {
0076     KateKonsolePluginView *view = new KateKonsolePluginView(this, mainWindow);
0077     return view;
0078 }
0079 
0080 KTextEditor::ConfigPage *KateKonsolePlugin::configPage(int number, QWidget *parent)
0081 {
0082     if (number != 0) {
0083         return nullptr;
0084     }
0085     return new KateKonsoleConfigPage(parent, this);
0086 }
0087 
0088 void KateKonsolePlugin::readConfig()
0089 {
0090     for (KateKonsolePluginView *view : qAsConst(mViews)) {
0091         view->readConfig();
0092     }
0093 }
0094 
0095 KateKonsolePluginView::KateKonsolePluginView(KateKonsolePlugin *plugin, KTextEditor::MainWindow *mainWindow)
0096     : QObject(mainWindow)
0097     , m_plugin(plugin)
0098 {
0099     // init console
0100     QWidget *toolview = mainWindow->createToolView(plugin,
0101                                                    QStringLiteral("kate_private_plugin_katekonsoleplugin"),
0102                                                    KTextEditor::MainWindow::Bottom,
0103                                                    QIcon::fromTheme(QStringLiteral("dialog-scripts")),
0104                                                    i18n("Terminal"));
0105     m_console = new KateConsole(m_plugin, mainWindow, toolview);
0106 
0107     // register this view
0108     m_plugin->mViews.append(this);
0109 }
0110 
0111 KateKonsolePluginView::~KateKonsolePluginView()
0112 {
0113     // unregister this view
0114     m_plugin->mViews.removeAll(this);
0115 
0116     // cleanup, kill toolview + console
0117     auto toolview = m_console->parent();
0118     delete m_console;
0119     delete toolview;
0120 }
0121 
0122 void KateKonsolePluginView::readConfig()
0123 {
0124     m_console->readConfig();
0125 }
0126 
0127 KateConsole::KateConsole(KateKonsolePlugin *plugin, KTextEditor::MainWindow *mw, QWidget *parent)
0128     : QWidget(parent)
0129     , m_part(nullptr)
0130     , m_mw(mw)
0131     , m_toolView(parent)
0132     , m_plugin(plugin)
0133 {
0134     KXMLGUIClient::setComponentName(QStringLiteral("katekonsole"), i18n("Terminal"));
0135     setXMLFile(QStringLiteral("ui.rc"));
0136 
0137     // make sure we have a vertical layout
0138     new QVBoxLayout(this);
0139     layout()->setContentsMargins(0, 1, 0, 0);
0140 
0141     QAction *a = actionCollection()->addAction(QStringLiteral("katekonsole_tools_pipe_to_terminal"));
0142     a->setIcon(QIcon::fromTheme(QStringLiteral("dialog-scripts")));
0143     a->setText(i18nc("@action", "&Pipe to Terminal"));
0144     connect(a, &QAction::triggered, this, &KateConsole::slotPipeToConsole);
0145 
0146     a = actionCollection()->addAction(QStringLiteral("katekonsole_tools_sync"));
0147     a->setText(i18nc("@action", "S&ynchronize Terminal with Current Document"));
0148     connect(a, &QAction::triggered, this, &KateConsole::slotManualSync);
0149 
0150     a = actionCollection()->addAction(QStringLiteral("katekonsole_tools_run"));
0151     a->setText(i18nc("@action", "Run Current Document"));
0152     connect(a, &QAction::triggered, this, &KateConsole::slotRun);
0153 
0154     a = actionCollection()->addAction(QStringLiteral("katekonsole_tools_toggle_visibility"));
0155     a->setIcon(QIcon::fromTheme(QStringLiteral("dialog-scripts")));
0156     a->setText(i18nc("@action", "S&how Terminal Panel"));
0157     actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::Key_F4));
0158     connect(a, &QAction::triggered, this, &KateConsole::slotToggleVisibility);
0159 
0160     a = actionCollection()->addAction(QStringLiteral("katekonsole_tools_toggle_focus"));
0161     a->setIcon(QIcon::fromTheme(QStringLiteral("swap-panels")));
0162     a->setText(i18nc("@action", "&Focus Terminal Panel"));
0163     actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_F4));
0164     connect(a, &QAction::triggered, this, &KateConsole::slotToggleFocus);
0165 
0166     connect(m_mw, &KTextEditor::MainWindow::unhandledShortcutOverride, this, &KateConsole::handleEsc);
0167 
0168     m_mw->guiFactory()->addClient(this);
0169 
0170     readConfig();
0171 
0172     connect(qApp, &QApplication::focusChanged, this, &KateConsole::focusChanged);
0173 }
0174 
0175 KateConsole::~KateConsole()
0176 {
0177     // we unplug our actions below, avoid that we trigger their usage
0178     disconnect(qApp, &QApplication::focusChanged, this, &KateConsole::focusChanged);
0179 
0180     m_mw->guiFactory()->removeClient(this);
0181     if (m_part) {
0182         disconnect(m_part, &KParts::ReadOnlyPart::destroyed, this, &KateConsole::slotDestroyed);
0183     }
0184 }
0185 
0186 void KateConsole::loadConsoleIfNeeded()
0187 {
0188     if (m_part) {
0189         return;
0190     }
0191 
0192     if (!window() || !parent()) {
0193         return;
0194     }
0195     if (!window() || !isVisibleTo(window())) {
0196         return;
0197     }
0198 
0199     if (hasKonsole()) {
0200         /**
0201          * get konsole part factory
0202          */
0203         const QString konsolePart = QStringLiteral("kf6/parts/konsolepart");
0204         KPluginFactory *factory = KPluginFactory::loadFactory(konsolePart).plugin;
0205         if (!factory) {
0206             return;
0207         }
0208 
0209         m_part = factory->create<KParts::ReadOnlyPart>(this);
0210 
0211         if (!m_part) {
0212             return;
0213         }
0214 
0215         if (auto konsoleTabWidget = qobject_cast<QTabWidget *>(m_part->widget())) {
0216             konsoleTabWidget->setTabBarAutoHide(true);
0217             konsoleTabWidget->installEventFilter(this);
0218         }
0219         layout()->addWidget(m_part->widget());
0220 
0221         // start the terminal
0222         qobject_cast<TerminalInterface *>(m_part)->showShellInDir(QString());
0223 
0224         //   KGlobal::locale()->insertCatalog("konsole"); // FIXME KF5: insert catalog
0225 
0226         setFocusProxy(m_part->widget());
0227 
0228         connect(m_part, &KParts::ReadOnlyPart::destroyed, this, &KateConsole::slotDestroyed);
0229         // clang-format off
0230         connect(m_part, SIGNAL(overrideShortcut(QKeyEvent*,bool&)), this, SLOT(overrideShortcut(QKeyEvent*,bool&)));
0231         // clang-format on
0232     }
0233 
0234     if (KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("Konsole")).readEntry("AutoSyncronize", true)) {
0235         slotSync();
0236     }
0237 }
0238 
0239 static bool isCtrlShiftT(QKeyEvent *ke)
0240 {
0241     return ke->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier) && ke->key() == Qt::Key_T;
0242 }
0243 
0244 bool KateConsole::eventFilter(QObject *w, QEvent *e)
0245 {
0246     if (!m_part) {
0247         return QWidget::eventFilter(w, e);
0248     }
0249 
0250     if (e->type() == QEvent::KeyPress || e->type() == QEvent::ShortcutOverride) {
0251         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
0252         if (isCtrlShiftT(keyEvent)) {
0253             e->accept();
0254             QMetaObject::invokeMethod(m_part, "newTab");
0255             return true;
0256         }
0257     }
0258 
0259     return QWidget::eventFilter(w, e);
0260 }
0261 
0262 void KateConsole::slotDestroyed()
0263 {
0264     m_part = nullptr;
0265     m_currentPath.clear();
0266     setFocusProxy(nullptr);
0267 
0268     // hide the dockwidget
0269     if (parent()) {
0270         m_mw->hideToolView(m_toolView);
0271     }
0272 }
0273 
0274 void KateConsole::overrideShortcut(QKeyEvent *, bool &override)
0275 {
0276     /**
0277      * let konsole handle all shortcuts
0278      */
0279     override = true;
0280 }
0281 
0282 void KateConsole::showEvent(QShowEvent *)
0283 {
0284     if (m_part) {
0285         return;
0286     }
0287 
0288     loadConsoleIfNeeded();
0289 }
0290 
0291 void KateConsole::paintEvent(QPaintEvent *e)
0292 {
0293     if (hasKonsole()) {
0294         QWidget::paintEvent(e);
0295         return;
0296     }
0297     QPainter p(this);
0298     p.setPen(QPen(KColorScheme().foreground(KColorScheme::NegativeText), 1));
0299     p.setBrush(Qt::NoBrush);
0300     p.drawRect(rect().adjusted(1, 1, -1, -1));
0301     auto font = p.font();
0302     font.setPixelSize(20);
0303     p.setFont(font);
0304     const QString text = i18n("Konsole not installed. Please install konsole to be able to use the terminal.");
0305     p.drawText(rect(), Qt::AlignCenter | Qt::TextWordWrap, text);
0306 }
0307 
0308 static constexpr QLatin1String eolChar()
0309 {
0310     // On windows, if the shell is powershell
0311     // '\r' needs to be sent to trigger enter
0312 #ifdef Q_OS_WIN
0313     return QLatin1String("\r\n");
0314 #else
0315     return QLatin1String("\n");
0316 #endif
0317 }
0318 
0319 void KateConsole::cd(const QString &path)
0320 {
0321     if (m_currentPath == path) {
0322         return;
0323     }
0324 
0325     if (!m_part) {
0326         return;
0327     }
0328 
0329     m_currentPath = path;
0330     QString command = QLatin1String(" cd ") + KShell::quoteArg(m_currentPath) + eolChar();
0331 
0332     // special handling for some interpreters
0333     TerminalInterface *t = qobject_cast<TerminalInterface *>(m_part);
0334     if (t) {
0335         // ghci doesn't allow \space dir names, does allow spaces in dir names
0336         // irb can take spaces or \space but doesn't allow " 'path' "
0337         if (t->foregroundProcessName() == QLatin1String("irb")) {
0338             command = QLatin1String("Dir.chdir(\"") + path + QLatin1String("\") \n");
0339         } else if (t->foregroundProcessName() == QLatin1String("ghc")) {
0340             command = QLatin1String(":cd ") + path + QLatin1Char('\n');
0341         } else if (!t->foregroundProcessName().isEmpty()) {
0342             // If something is running, dont try to cd anywhere
0343             return;
0344         }
0345     }
0346 
0347 #ifndef Q_OS_WIN // Doesnt work with PS or cmd.exe on windows
0348     // Send prior Ctrl-E, Ctrl-U to ensure the line is empty
0349     sendInput(QStringLiteral("\x05\x15"));
0350 #endif
0351     sendInput(command);
0352 }
0353 
0354 void KateConsole::sendInput(const QString &text)
0355 {
0356     loadConsoleIfNeeded();
0357 
0358     if (!m_part) {
0359         return;
0360     }
0361 
0362     if (TerminalInterface *t = qobject_cast<TerminalInterface *>(m_part)) {
0363         t->sendInput(text);
0364     }
0365 }
0366 
0367 KPluginFactory *KateConsole::pluginFactory()
0368 {
0369     if (s_pluginFactory) {
0370         return s_pluginFactory;
0371     }
0372     const QString konsolePart = QStringLiteral("kf6/parts/konsolepart");
0373     return s_pluginFactory = KPluginFactory::loadFactory(konsolePart).plugin;
0374 }
0375 
0376 void KateConsole::slotPipeToConsole()
0377 {
0378     if (KMessageBox::warningContinueCancel(
0379             m_mw->window(),
0380             i18n("Do you really want to pipe the text to the console? This will execute any contained commands with your user rights."),
0381             i18n("Pipe to Terminal?"),
0382             KGuiItem(i18n("Pipe to Terminal")),
0383             KStandardGuiItem::cancel(),
0384             QStringLiteral("Pipe To Terminal Warning"))
0385         != KMessageBox::Continue) {
0386         return;
0387     }
0388 
0389     KTextEditor::View *v = m_mw->activeView();
0390 
0391     if (!v) {
0392         return;
0393     }
0394 
0395     if (v->selection()) {
0396         sendInput(v->selectionText());
0397     } else {
0398         sendInput(v->document()->text());
0399     }
0400 }
0401 
0402 void KateConsole::slotSync()
0403 {
0404     if (m_mw->activeView()) {
0405         QUrl u = m_mw->activeView()->document()->url();
0406         if (u.isValid() && u.isLocalFile()) {
0407             QFileInfo fi(u.toLocalFile());
0408             cd(fi.absolutePath());
0409         } else if (!u.isEmpty()) {
0410             sendInput(QStringLiteral("### ") + i18n("Sorry, cannot cd into '%1'", u.toLocalFile()) + QLatin1Char('\n'));
0411         }
0412     }
0413 }
0414 
0415 void KateConsole::slotViewOrUrlChanged(KTextEditor::View *view)
0416 {
0417     disconnect(m_urlChangedConnection);
0418     if (view) {
0419         KTextEditor::Document *doc = view->document();
0420         m_urlChangedConnection = connect(doc, &KParts::ReadOnlyPart::urlChanged, this, &KateConsole::slotSync);
0421     }
0422 
0423     slotSync();
0424 }
0425 
0426 void KateConsole::slotManualSync()
0427 {
0428     m_currentPath.clear();
0429     slotSync();
0430 
0431     if (!m_part || !m_part->widget()->isVisible()) {
0432         m_mw->showToolView(qobject_cast<QWidget *>(parent()));
0433     }
0434 }
0435 
0436 void KateConsole::slotRun()
0437 {
0438     KTextEditor::View *view = m_mw->activeView();
0439     if (!view) {
0440         return;
0441     }
0442 
0443     KTextEditor::Document *document = view->document();
0444     const QUrl url = document->url();
0445     if (!url.isLocalFile()) {
0446         QPointer<KTextEditor::Message> message = new KTextEditor::Message(i18n("Not a local file: '%1'", url.toDisplayString()), KTextEditor::Message::Error);
0447         // auto hide is enabled and set to a sane default value of several seconds.
0448         message->setAutoHide(2000);
0449         message->setAutoHideMode(KTextEditor::Message::Immediate);
0450         document->postMessage(message);
0451         return;
0452     }
0453     // ensure that file is saved
0454     if (document->isModified()) {
0455         document->save();
0456     }
0457 
0458     KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("Konsole"));
0459     // The string that should be output to terminal, upon acceptance
0460     QString output_str;
0461     // Set prefix first
0462     QString first_line = document->line(0);
0463     const QLatin1String shebang("#!");
0464     if (first_line.startsWith(shebang)) {
0465         // If there's a shebang, respect it
0466         output_str += first_line.remove(0, shebang.size()).append(QLatin1Char(' '));
0467     } else {
0468         output_str += cg.readEntry("RunPrefix", "");
0469     }
0470 
0471     // then filename
0472     QFileInfo fileInfo(url.toLocalFile());
0473     const bool removeExt = cg.readEntry("RemoveExtension", true);
0474     // append filename without extension (i.e. keep only the basename)
0475     const QString path = fileInfo.absolutePath() + QLatin1Char('/') + (removeExt ? fileInfo.baseName() : fileInfo.fileName());
0476     output_str += KShell::quoteArg(path);
0477 
0478     const QString msg = i18n(
0479         "Do you really want to Run the document ?\n"
0480         "This will execute the following command,\n"
0481         "with your user rights, in the terminal:\n"
0482         "'%1'",
0483         output_str);
0484     const auto result = KMessageBox::warningContinueCancel(m_mw->window(),
0485                                                            msg,
0486                                                            i18n("Run in Terminal?"),
0487                                                            KGuiItem(i18n("Run")),
0488                                                            KStandardGuiItem::cancel(),
0489                                                            QStringLiteral("Konsole: Run in Terminal Warning"));
0490     if (result != KMessageBox::Continue) {
0491         return;
0492     }
0493     // echo to terminal
0494     sendInput(output_str + eolChar());
0495 }
0496 
0497 void KateConsole::slotToggleVisibility()
0498 {
0499     QAction *action = actionCollection()->action(QStringLiteral("katekonsole_tools_toggle_visibility"));
0500     if (!m_part || !m_part->widget()->isVisible()) {
0501         m_mw->showToolView(qobject_cast<QWidget *>(parent()));
0502         action->setText(i18nc("@action", "&Hide Terminal Panel"));
0503     } else {
0504         m_mw->hideToolView(m_toolView);
0505         action->setText(i18nc("@action", "S&how Terminal Panel"));
0506     }
0507 }
0508 
0509 void KateConsole::focusChanged(QWidget *, QWidget *now)
0510 {
0511     QAction *action = actionCollection()->action(QStringLiteral("katekonsole_tools_toggle_focus"));
0512     if (m_part && m_part->widget()->isAncestorOf(now)) {
0513         action->setText(i18n("Defocus Terminal Panel"));
0514     } else if (action->text() != i18n("Focus Terminal Panel")) {
0515         action->setText(i18n("Focus Terminal Panel"));
0516     }
0517 }
0518 
0519 void KateConsole::slotToggleFocus()
0520 {
0521     if (!m_part) {
0522         m_mw->showToolView(qobject_cast<QWidget *>(parent()));
0523         return; // this shows and focuses the konsole
0524     }
0525 
0526     if (m_part->widget()->hasFocus()) {
0527         if (m_mw->activeView()) {
0528             m_mw->activeView()->setFocus();
0529         }
0530     } else {
0531         // show the view if it is hidden
0532         if (auto p = qobject_cast<QWidget *>(parent()); p->isHidden()) {
0533             m_mw->showToolView(p);
0534         } else { // should focus the widget too!
0535             m_part->widget()->setFocus(Qt::OtherFocusReason);
0536         }
0537     }
0538 }
0539 
0540 void KateConsole::readConfig()
0541 {
0542     disconnect(m_mw, &KTextEditor::MainWindow::viewChanged, this, &KateConsole::slotViewOrUrlChanged);
0543     disconnect(m_urlChangedConnection);
0544 
0545     if (KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("Konsole")).readEntry("AutoSyncronize", true)) {
0546         connect(m_mw, &KTextEditor::MainWindow::viewChanged, this, &KateConsole::slotViewOrUrlChanged);
0547     }
0548 
0549     if (KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("Konsole")).readEntry("SetEditor", false)) {
0550         qputenv("EDITOR", "kate -b");
0551     } else {
0552         setEditorEnv(m_plugin->previousEditorEnv());
0553     }
0554 }
0555 
0556 void KateConsole::handleEsc(QEvent *e)
0557 {
0558     if (!KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("Konsole")).readEntry("KonsoleEscKeyBehaviour", true)) {
0559         return;
0560     }
0561 
0562     QStringList exceptList = KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("Konsole")).readEntry("KonsoleEscKeyExceptions", s_escapeExceptions);
0563 
0564     if (!m_mw || !m_toolView || !e) {
0565         return;
0566     }
0567 
0568     QKeyEvent *k = static_cast<QKeyEvent *>(e);
0569     if (k->key() == Qt::Key_Escape && k->modifiers() == Qt::NoModifier) {
0570         if (m_part) {
0571             const auto app = qobject_cast<TerminalInterface *>(m_part)->foregroundProcessName();
0572             if (m_toolView && m_toolView->isVisible() && !exceptList.contains(app)) {
0573                 m_mw->hideToolView(m_toolView);
0574             }
0575         } else if (m_toolView && m_toolView->isVisible()) {
0576             m_mw->hideToolView(m_toolView);
0577         }
0578     }
0579 }
0580 
0581 KateKonsoleConfigPage::KateKonsoleConfigPage(QWidget *parent, KateKonsolePlugin *plugin)
0582     : KTextEditor::ConfigPage(parent)
0583     , mPlugin(plugin)
0584 {
0585     QVBoxLayout *lo = new QVBoxLayout(this);
0586     lo->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing));
0587 
0588     cbAutoSyncronize = new QCheckBox(i18n("&Automatically synchronize the terminal with the current document when possible"), this);
0589     lo->addWidget(cbAutoSyncronize);
0590 
0591     QVBoxLayout *vboxRun = new QVBoxLayout;
0592     QGroupBox *groupRun = new QGroupBox(i18n("Run in terminal"), this);
0593     // Remove extension
0594     cbRemoveExtension = new QCheckBox(i18n("&Remove extension"), this);
0595     vboxRun->addWidget(cbRemoveExtension);
0596     // Prefix
0597     QFrame *framePrefix = new QFrame(this);
0598     QHBoxLayout *hboxPrefix = new QHBoxLayout(framePrefix);
0599     QLabel *label = new QLabel(i18n("Prefix:"), framePrefix);
0600     hboxPrefix->addWidget(label);
0601     lePrefix = new QLineEdit(framePrefix);
0602     hboxPrefix->addWidget(lePrefix);
0603     vboxRun->addWidget(framePrefix);
0604     // show warning next time
0605     QFrame *frameWarn = new QFrame(this);
0606     QHBoxLayout *hboxWarn = new QHBoxLayout(frameWarn);
0607     QPushButton *buttonWarn = new QPushButton(i18n("&Show warning next time"), frameWarn);
0608     buttonWarn->setWhatsThis(
0609         i18n("The next time '%1' is executed, "
0610              "make sure a warning window will pop up, "
0611              "displaying the command to be sent to terminal, "
0612              "for review.",
0613              i18n("Run in terminal")));
0614     connect(buttonWarn, &QPushButton::pressed, this, &KateKonsoleConfigPage::slotEnableRunWarning);
0615     hboxWarn->addWidget(buttonWarn);
0616     vboxRun->addWidget(frameWarn);
0617     groupRun->setLayout(vboxRun);
0618     lo->addWidget(groupRun);
0619 
0620     cbSetEditor = new QCheckBox(i18n("Set &EDITOR environment variable to 'kate -b'"), this);
0621     lo->addWidget(cbSetEditor);
0622     QLabel *tmp = new QLabel(this);
0623     tmp->setText(i18n("Important: The document has to be closed to make the console application continue"));
0624     lo->addWidget(tmp);
0625 
0626     cbSetEscHideKonsole = new QCheckBox(i18n("Hide Konsole on pressing 'Esc'"));
0627     lo->addWidget(cbSetEscHideKonsole);
0628     QLabel *hideKonsoleLabel =
0629         new QLabel(i18n("This may cause issues with terminal apps that use Esc key, for e.g., vim. Add these apps in the input below (Comma separated list)"),
0630                    this);
0631     hideKonsoleLabel->setWordWrap(true);
0632     lo->addWidget(hideKonsoleLabel);
0633 
0634     leEscExceptions = new QLineEdit(this);
0635     lo->addWidget(leEscExceptions);
0636 
0637     reset();
0638     lo->addStretch();
0639 
0640     connect(cbAutoSyncronize, &QCheckBox::stateChanged, this, &KateKonsoleConfigPage::changed);
0641     connect(cbRemoveExtension, &QCheckBox::stateChanged, this, &KTextEditor::ConfigPage::changed);
0642     connect(lePrefix, &QLineEdit::textChanged, this, &KateKonsoleConfigPage::changed);
0643     connect(cbSetEditor, &QCheckBox::stateChanged, this, &KateKonsoleConfigPage::changed);
0644     connect(cbSetEscHideKonsole, &QCheckBox::stateChanged, this, &KateKonsoleConfigPage::changed);
0645     connect(leEscExceptions, &QLineEdit::textChanged, this, &KateKonsoleConfigPage::changed);
0646 }
0647 
0648 void KateKonsoleConfigPage::slotEnableRunWarning()
0649 {
0650     KMessageBox::enableMessage(QStringLiteral("Konsole: Run in Terminal Warning"));
0651 }
0652 
0653 QString KateKonsoleConfigPage::name() const
0654 {
0655     return i18n("Terminal");
0656 }
0657 
0658 QString KateKonsoleConfigPage::fullName() const
0659 {
0660     return i18n("Terminal Settings");
0661 }
0662 
0663 QIcon KateKonsoleConfigPage::icon() const
0664 {
0665     return QIcon::fromTheme(QStringLiteral("dialog-scripts"));
0666 }
0667 
0668 void KateKonsoleConfigPage::apply()
0669 {
0670     KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("Konsole"));
0671     config.writeEntry("AutoSyncronize", cbAutoSyncronize->isChecked());
0672     config.writeEntry("RemoveExtension", cbRemoveExtension->isChecked());
0673     config.writeEntry("RunPrefix", lePrefix->text());
0674     config.writeEntry("SetEditor", cbSetEditor->isChecked());
0675     config.writeEntry("KonsoleEscKeyBehaviour", cbSetEscHideKonsole->isChecked());
0676     config.writeEntry("KonsoleEscKeyExceptions", leEscExceptions->text().split(QLatin1Char(',')));
0677     config.sync();
0678     mPlugin->readConfig();
0679 }
0680 
0681 void KateKonsoleConfigPage::reset()
0682 {
0683     KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("Konsole"));
0684     cbAutoSyncronize->setChecked(config.readEntry("AutoSyncronize", true));
0685     cbRemoveExtension->setChecked(config.readEntry("RemoveExtension", false));
0686     lePrefix->setText(config.readEntry("RunPrefix", ""));
0687     cbSetEditor->setChecked(config.readEntry("SetEditor", false));
0688     cbSetEscHideKonsole->setChecked(config.readEntry("KonsoleEscKeyBehaviour", true));
0689     leEscExceptions->setText(config.readEntry("KonsoleEscKeyExceptions", s_escapeExceptions).join(QLatin1Char(',')));
0690 }
0691 
0692 #include "kateconsole.moc"
0693 #include "moc_kateconsole.cpp"
0694 
0695 // kate: space-indent on; indent-width 4; replace-tabs on;