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

0001 /* This file is part of the KDE project
0002    SPDX-FileCopyrightText: 2001 Christoph Cullmann <cullmann@kde.org>
0003    SPDX-FileCopyrightText: 2001 Joseph Wenninger <jowenn@kde.org>
0004    SPDX-FileCopyrightText: 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>
0005 
0006    SPDX-License-Identifier: LGPL-2.0-only
0007 */
0008 
0009 #include "katepluginmanager.h"
0010 
0011 #include "kateapp.h"
0012 #include "katemainwindow.h"
0013 
0014 #include <KConfig>
0015 #include <KConfigBase>
0016 #include <KConfigGroup>
0017 #include <KPluginFactory>
0018 #include <KTextEditor/Plugin>
0019 #include <KTextEditor/SessionConfigInterface>
0020 
0021 #include <QFileInfo>
0022 #include <array>
0023 
0024 QString KatePluginInfo::saveName() const
0025 {
0026     return QFileInfo(metaData.fileName()).baseName();
0027 }
0028 
0029 bool KatePluginInfo::operator<(const KatePluginInfo &other) const
0030 {
0031     if (sortOrder != other.sortOrder) {
0032         return sortOrder < other.sortOrder;
0033     }
0034 
0035     return saveName() < other.saveName();
0036 }
0037 
0038 KatePluginManager::KatePluginManager()
0039 {
0040     setupPluginList();
0041 }
0042 
0043 KatePluginManager::~KatePluginManager()
0044 {
0045     unloadAllPlugins();
0046 }
0047 
0048 void KatePluginManager::setupPluginList()
0049 {
0050     // no plugins for KWrite mode
0051     if (KateApp::isKWrite()) {
0052         Q_ASSERT(m_pluginList.empty());
0053         return;
0054     }
0055 
0056     // activate a hand-picked list of plugins per default, give them a hand-picked sort order for loading
0057     struct PluginNameAndSortOrder {
0058         const char *name;
0059         int sortOrder;
0060     };
0061 
0062     const std::array<PluginNameAndSortOrder, 8> defaultPlugins = {
0063         {{"katefiletreeplugin", -1000},
0064          {"katesearchplugin", -900},
0065          {"kateprojectplugin", -800},
0066          {"tabswitcherplugin", -100},
0067          {"textfilterplugin", -100},
0068          {"externaltoolsplugin", -100},
0069          {"lspclientplugin", -100},
0070          {"katekonsoleplugin", -100}},
0071     };
0072 
0073     // handle all install KTextEditor plugins
0074     m_pluginList.clear();
0075     QSet<QString> unique;
0076 
0077     const QList<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("kf6/ktexteditor"));
0078     for (const auto &pluginMetaData : plugins) {
0079         KatePluginInfo info;
0080         info.metaData = pluginMetaData;
0081         const QString saveName = info.saveName();
0082 
0083         // only load plugins once, even if found multiple times!unique
0084         if (unique.contains(saveName)) {
0085             continue;
0086         }
0087 
0088         for (auto dp : defaultPlugins) {
0089             if (QLatin1String(dp.name) == saveName) {
0090                 info.defaultLoad = true;
0091                 info.sortOrder = dp.sortOrder;
0092                 break;
0093             }
0094         }
0095 
0096         info.load = info.defaultLoad; // keep this in load, too, to avoid new sessions kill that info on writeConfig
0097         info.plugin = nullptr;
0098         m_pluginList.push_back(info);
0099         unique.insert(saveName);
0100     }
0101 
0102     // sort to ensure some deterministic plugin load order, this is important for tool-view creation order
0103     std::sort(m_pluginList.begin(), m_pluginList.end());
0104 }
0105 
0106 void KatePluginManager::loadConfig(KConfig *config)
0107 {
0108     // first: unload the plugins
0109     unloadAllPlugins();
0110 
0111     /**
0112      * ask config object
0113      */
0114     if (config) {
0115         KConfigGroup cg = KConfigGroup(config, QStringLiteral("Kate Plugins"));
0116 
0117         // disable all plugin if no config, beside the ones marked as default load
0118         for (auto &pluginInfo : m_pluginList) {
0119             pluginInfo.load = cg.readEntry(pluginInfo.saveName(), pluginInfo.defaultLoad);
0120         }
0121     }
0122 
0123     /**
0124      * load plugins
0125      */
0126     for (auto &pluginInfo : m_pluginList) {
0127         if (pluginInfo.load) {
0128             /**
0129              * load plugin + trigger update of GUI for already existing main windows
0130              */
0131             loadPlugin(&pluginInfo);
0132             enablePluginGUI(&pluginInfo);
0133 
0134             // restore config
0135             if (auto interface = qobject_cast<KTextEditor::SessionConfigInterface *>(pluginInfo.plugin)) {
0136                 KConfigGroup group(config, QStringLiteral("Plugin:%1:").arg(pluginInfo.saveName()));
0137                 interface->readSessionConfig(group);
0138             }
0139         }
0140     }
0141 }
0142 
0143 void KatePluginManager::writeConfig(KConfig *config)
0144 {
0145     Q_ASSERT(config);
0146 
0147     KConfigGroup cg = KConfigGroup(config, QStringLiteral("Kate Plugins"));
0148     for (const KatePluginInfo &plugin : qAsConst(m_pluginList)) {
0149         QString saveName = plugin.saveName();
0150 
0151         cg.writeEntry(saveName, plugin.load);
0152 
0153         // save config
0154         if (auto interface = qobject_cast<KTextEditor::SessionConfigInterface *>(plugin.plugin)) {
0155             KConfigGroup group(config, QStringLiteral("Plugin:%1:").arg(saveName));
0156             interface->writeSessionConfig(group);
0157         }
0158     }
0159 }
0160 
0161 void KatePluginManager::unloadAllPlugins()
0162 {
0163     for (auto &pluginInfo : m_pluginList) {
0164         if (pluginInfo.plugin) {
0165             unloadPlugin(&pluginInfo);
0166         }
0167     }
0168 }
0169 
0170 void KatePluginManager::enableAllPluginsGUI(KateMainWindow *win, KConfigBase *config)
0171 {
0172     for (auto &pluginInfo : m_pluginList) {
0173         if (pluginInfo.plugin) {
0174             enablePluginGUI(&pluginInfo, win, config);
0175         }
0176     }
0177 }
0178 
0179 void KatePluginManager::disableAllPluginsGUI(KateMainWindow *win)
0180 {
0181     for (auto &pluginInfo : m_pluginList) {
0182         if (pluginInfo.plugin) {
0183             disablePluginGUI(&pluginInfo, win);
0184         }
0185     }
0186 }
0187 
0188 bool KatePluginManager::loadPlugin(KatePluginInfo *item)
0189 {
0190     /**
0191      * try to load the plugin
0192      */
0193     item->plugin = KPluginFactory::instantiatePlugin<KTextEditor::Plugin>(item->metaData, KateApp::self(), QVariantList() << item->saveName()).plugin;
0194     item->load = item->plugin != nullptr;
0195 
0196     /**
0197      * tell the world about the success
0198      */
0199     if (item->plugin) {
0200         Q_EMIT KateApp::self()->wrapper()->pluginCreated(item->saveName(), item->plugin);
0201     }
0202 
0203     return item->plugin != nullptr;
0204 }
0205 
0206 void KatePluginManager::unloadPlugin(KatePluginInfo *item)
0207 {
0208     disablePluginGUI(item);
0209     delete item->plugin;
0210     KTextEditor::Plugin *plugin = item->plugin;
0211     item->plugin = nullptr;
0212     item->load = false;
0213     Q_EMIT KateApp::self()->wrapper()->pluginDeleted(item->saveName(), plugin);
0214 }
0215 
0216 void KatePluginManager::enablePluginGUI(KatePluginInfo *item, KateMainWindow *win, KConfigBase *config)
0217 {
0218     // plugin around at all?
0219     if (!item->plugin) {
0220         return;
0221     }
0222 
0223     // lookup if there is already a view for it..
0224     QObject *createdView = nullptr;
0225     if (!win->pluginViews().contains(item->plugin)) {
0226         // create the view + try to correctly load shortcuts, if it's a GUI Client
0227         createdView = item->plugin->createView(win->wrapper());
0228         if (createdView) {
0229             win->pluginViews().insert(item->plugin, createdView);
0230         }
0231     }
0232 
0233     // load session config if needed
0234     if (config && win->pluginViews().contains(item->plugin)) {
0235         if (auto interface = qobject_cast<KTextEditor::SessionConfigInterface *>(win->pluginViews().value(item->plugin))) {
0236             KConfigGroup group(config, QStringLiteral("Plugin:%1:MainWindow:0").arg(item->saveName()));
0237             interface->readSessionConfig(group);
0238         }
0239     }
0240 
0241     if (createdView) {
0242         Q_EMIT win->wrapper()->pluginViewCreated(item->saveName(), createdView);
0243     }
0244 }
0245 
0246 void KatePluginManager::enablePluginGUI(KatePluginInfo *item)
0247 {
0248     // plugin around at all?
0249     if (!item->plugin) {
0250         return;
0251     }
0252 
0253     // enable the gui for all mainwindows...
0254     for (int i = 0; i < KateApp::self()->mainWindowsCount(); i++) {
0255         enablePluginGUI(item, KateApp::self()->mainWindow(i), nullptr);
0256     }
0257 }
0258 
0259 void KatePluginManager::disablePluginGUI(KatePluginInfo *item, KateMainWindow *win)
0260 {
0261     // plugin around at all?
0262     if (!item->plugin) {
0263         return;
0264     }
0265 
0266     // lookup if there is a view for it..
0267     if (!win->pluginViews().contains(item->plugin)) {
0268         return;
0269     }
0270 
0271     // really delete the view of this plugin
0272     QObject *deletedView = win->pluginViews().value(item->plugin);
0273     delete deletedView;
0274     win->pluginViews().remove(item->plugin);
0275     Q_EMIT win->wrapper()->pluginViewDeleted(item->saveName(), deletedView);
0276 }
0277 
0278 void KatePluginManager::disablePluginGUI(KatePluginInfo *item)
0279 {
0280     // plugin around at all?
0281     if (!item->plugin) {
0282         return;
0283     }
0284 
0285     // disable the gui for all mainwindows...
0286     for (int i = 0; i < KateApp::self()->mainWindowsCount(); i++) {
0287         disablePluginGUI(item, KateApp::self()->mainWindow(i));
0288     }
0289 }
0290 
0291 KTextEditor::Plugin *KatePluginManager::plugin(const QString &name)
0292 {
0293     const auto it = std::find_if(m_pluginList.cbegin(), m_pluginList.cend(), [name](const KatePluginInfo &pi) {
0294         return pi.saveName() == name;
0295     });
0296     return (it == m_pluginList.cend()) ? nullptr : it->plugin;
0297 }