File indexing completed on 2024-04-28 05:49:08

0001 /*  This file is part of the Kate project.
0002  *
0003  *  SPDX-FileCopyrightText: 2012 Christoph Cullmann <cullmann@kde.org>
0004  *
0005  *  SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 
0008 #include "kateprojectinfoviewterminal.h"
0009 #include "kateprojectpluginview.h"
0010 
0011 #include <KActionCollection>
0012 #include <KConfigGroup>
0013 #include <KLocalizedString>
0014 #include <KPluginFactory>
0015 #include <KSharedConfig>
0016 #include <KShell>
0017 #include <KTextEditor/MainWindow>
0018 #include <KXMLGUIClient>
0019 #include <KXMLGUIFactory>
0020 #include <kde_terminal_interface.h>
0021 #include <ktexteditor_utils.h>
0022 
0023 #include <QTabWidget>
0024 
0025 KPluginFactory *KateProjectInfoViewTerminal::s_pluginFactory = nullptr;
0026 
0027 KateProjectInfoViewTerminal::KateProjectInfoViewTerminal(KateProjectPluginView *pluginView, const QString &directory)
0028     : m_pluginView(pluginView)
0029     , m_directory(directory)
0030 {
0031     /**
0032      * layout widget
0033      */
0034     m_layout = new QVBoxLayout(this);
0035     m_layout->setSpacing(0);
0036     m_layout->setContentsMargins(0, 0, 0, 0);
0037 
0038     m_showProjectInfoViewAction = Utils::toolviewShowAction(m_pluginView->mainWindow(), QStringLiteral("kateprojectinfo"));
0039 }
0040 
0041 KateProjectInfoViewTerminal::~KateProjectInfoViewTerminal()
0042 {
0043     /**
0044      * avoid endless loop
0045      */
0046     if (m_konsolePart) {
0047         disconnect(m_konsolePart, &KParts::ReadOnlyPart::destroyed, this, &KateProjectInfoViewTerminal::loadTerminal);
0048     }
0049 }
0050 
0051 KPluginFactory *KateProjectInfoViewTerminal::pluginFactory()
0052 {
0053     if (s_pluginFactory) {
0054         return s_pluginFactory;
0055     }
0056     const QString konsolePart = QStringLiteral("kf6/parts/konsolepart");
0057     return s_pluginFactory = KPluginFactory::loadFactory(konsolePart).plugin;
0058 }
0059 
0060 void KateProjectInfoViewTerminal::showEvent(QShowEvent *)
0061 {
0062     /**
0063      * we delay the terminal construction until we have some part to have a usable WINDOWID, see bug 411965
0064      */
0065     if (!m_konsolePart) {
0066         loadTerminal();
0067     }
0068 }
0069 
0070 void KateProjectInfoViewTerminal::loadTerminal()
0071 {
0072     if (hasKonsole()) {
0073         /**
0074          * null in any case, if loadTerminal fails below and we are in the destroyed event
0075          */
0076         m_konsolePart = nullptr;
0077         setFocusProxy(nullptr);
0078 
0079         /**
0080          * we shall not arrive here without a factory, if it is not there, no terminal toolview shall be created
0081          */
0082         Q_ASSERT(pluginFactory());
0083 
0084         /**
0085          * create part
0086          */
0087         m_konsolePart = pluginFactory()->create<KParts::ReadOnlyPart>(this);
0088         if (!m_konsolePart) {
0089             return;
0090         }
0091 
0092         /**
0093          * init locale translation stuff
0094          */
0095         // FIXME KF5 KGlobal::locale()->insertCatalog("konsole");
0096 
0097         /**
0098          * switch to right directory
0099          */
0100         qobject_cast<TerminalInterface *>(m_konsolePart)->showShellInDir(m_directory);
0101 
0102         /**
0103          * add to widget
0104          */
0105         if (auto konsoleTabWidget = qobject_cast<QTabWidget *>(m_konsolePart->widget())) {
0106             konsoleTabWidget->setTabBarAutoHide(true);
0107             konsoleTabWidget->installEventFilter(this);
0108         }
0109         m_layout->addWidget(m_konsolePart->widget());
0110 
0111         setFocusProxy(m_konsolePart->widget());
0112 
0113         /**
0114          * guard destruction, create new terminal!
0115          */
0116         connect(m_konsolePart, &KParts::ReadOnlyPart::destroyed, this, &KateProjectInfoViewTerminal::loadTerminal);
0117         // clang-format off
0118         connect(m_konsolePart, SIGNAL(overrideShortcut(QKeyEvent*,bool&)), this, SLOT(overrideShortcut(QKeyEvent*,bool&)));
0119         // clang-format on
0120     }
0121 }
0122 
0123 void KateProjectInfoViewTerminal::overrideShortcut(QKeyEvent *keyEvent, bool &override)
0124 {
0125     if (m_showProjectInfoViewAction && !m_showProjectInfoViewAction->shortcut().isEmpty()) {
0126         int modifiers = keyEvent->modifiers();
0127         int key = keyEvent->key();
0128         QKeySequence k(modifiers | key);
0129         if (m_showProjectInfoViewAction->shortcut().matches(k)) {
0130             override = false;
0131             return;
0132         }
0133     }
0134 
0135     /**
0136      * let konsole handle all shortcuts
0137      */
0138     override = true;
0139 }
0140 
0141 // share with konsole plugin
0142 static const QStringList s_escapeExceptions{QStringLiteral("vi"), QStringLiteral("vim"), QStringLiteral("nvim")};
0143 
0144 bool KateProjectInfoViewTerminal::ignoreEsc() const
0145 {
0146     // if konsole is not found, do not ignore esc
0147     if (!m_konsolePart && !KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("Konsole")).exists()) {
0148         return false;
0149     }
0150 
0151     // If Hide with Esc is disabled in konsole settings, ignore esc press.
0152     if (!KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("Konsole")).readEntry("KonsoleEscKeyBehaviour", true)) {
0153         return true;
0154     }
0155     // Otherwise only ignore apps in given list
0156     else {
0157         const QStringList exceptList =
0158             KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("Konsole")).readEntry("KonsoleEscKeyExceptions", s_escapeExceptions);
0159         const auto app = qobject_cast<TerminalInterface *>(m_konsolePart)->foregroundProcessName();
0160         return exceptList.contains(app);
0161     }
0162 }
0163 
0164 bool KateProjectInfoViewTerminal::isLoadable()
0165 {
0166     return (pluginFactory() != nullptr);
0167 }
0168 
0169 void KateProjectInfoViewTerminal::respawn(const QString &directory)
0170 {
0171     if (!isLoadable()) {
0172         return;
0173     }
0174 
0175     m_directory = directory;
0176 
0177     if (m_konsolePart) {
0178         disconnect(m_konsolePart, &KParts::ReadOnlyPart::destroyed, this, &KateProjectInfoViewTerminal::loadTerminal);
0179         delete m_konsolePart;
0180     }
0181 
0182     loadTerminal();
0183 }
0184 
0185 static bool isCtrlShiftT(QKeyEvent *ke)
0186 {
0187     return ke->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier) && ke->key() == Qt::Key_T;
0188 }
0189 
0190 bool KateProjectInfoViewTerminal::eventFilter(QObject *w, QEvent *e)
0191 {
0192     if (!m_konsolePart) {
0193         return QWidget::eventFilter(w, e);
0194     }
0195 
0196     if (e->type() == QEvent::KeyPress || e->type() == QEvent::ShortcutOverride) {
0197         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
0198         if (isCtrlShiftT(keyEvent)) {
0199             e->accept();
0200             auto tiface = qobject_cast<TerminalInterface *>(m_konsolePart);
0201             const auto profile = QString{};
0202             const auto workingDir = tiface->currentWorkingDirectory();
0203             QMetaObject::invokeMethod(m_konsolePart, "createSession", Q_ARG(QString, profile), Q_ARG(QString, workingDir));
0204             return true;
0205         }
0206     }
0207 
0208     return QWidget::eventFilter(w, e);
0209 }
0210 
0211 void KateProjectInfoViewTerminal::runCommand(const QString &workingDir, const QString &cmd)
0212 {
0213     if (!m_konsolePart) {
0214         loadTerminal();
0215     }
0216     auto terminal = qobject_cast<TerminalInterface *>(m_konsolePart);
0217     terminal->sendInput(QStringLiteral("\x05\x15"));
0218     const QString changeDirCmd = QStringLiteral("cd ") + KShell::quoteArg(workingDir) + QStringLiteral("\n");
0219     terminal->sendInput(changeDirCmd);
0220     terminal->sendInput(cmd.trimmed() + QStringLiteral("\n"));
0221 }
0222 
0223 #include "moc_kateprojectinfoviewterminal.cpp"