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 }