Commit df0cfd1c authored by Alexander Lohnau's avatar Alexander Lohnau 💬
Browse files

Import KParts::Plugin class in LibKonq

The concept of plugins will be deprecated and removed in KF6.
Considering that most providers live in Konqueror already, we can import the
class.

Task: https://phabricator.kde.org/T12265
parent 0a0cf721
......@@ -9,6 +9,7 @@ set(konq_LIB_SRCS
konq_historyloader.cpp
konq_historyprovider.cpp # konqueror and konqueror/sidebar
konq_spellcheckingconfigurationdispatcher.cpp #konqueror and webenginepart
konq_kpart_plugin.cpp
)
ecm_qt_declare_logging_category(konq_LIB_SRCS HEADER libkonq_debug.h IDENTIFIER LIBKONQ_LOG CATEGORY_NAME org.kde.libkonq)
......
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "konq_kpart_plugin.h"
#include <KConfigGroup>
#include <KDesktopFile>
#include <KLocalizedString>
#include <KPluginFactory>
#include <KPluginLoader>
#include <KPluginMetaData>
#include <KSharedConfig>
#include <KXMLGUIFactory>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QStandardPaths>
using namespace KParts;
class KParts::PluginPrivate
{
public:
QString m_parentInstance;
QString m_library; // filename of the library
};
Plugin::Plugin(QObject *parent)
: QObject(parent)
, d(new PluginPrivate())
{
// qDebug() << className();
}
Plugin::~Plugin() = default;
QString Plugin::xmlFile() const
{
QString path = KXMLGUIClient::xmlFile();
if (d->m_parentInstance.isEmpty() || (!path.isEmpty() && QDir::isAbsolutePath(path))) {
return path;
}
QString absPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, d->m_parentInstance + QLatin1Char('/') + path);
Q_ASSERT(!absPath.isEmpty());
return absPath;
}
QString Plugin::localXMLFile() const
{
QString path = KXMLGUIClient::xmlFile();
if (d->m_parentInstance.isEmpty() || (!path.isEmpty() && QDir::isAbsolutePath(path))) {
return path;
}
QString absPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + d->m_parentInstance + QLatin1Char('/') + path;
return absPath;
}
// static
QList<Plugin::PluginInfo> Plugin::pluginInfos(const QString &componentName)
{
QList<PluginInfo> plugins;
QMap<QString, QStringList> sortedPlugins;
const QStringList dirs =
QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, componentName + QLatin1String("/kpartplugins"), QStandardPaths::LocateDirectory);
for (const QString &dir : dirs) {
const auto rcfiles = QDir(dir).entryList(QStringList(QStringLiteral("*.rc")));
for (const QString &file : rcfiles) {
const QFileInfo fInfo(dir + QLatin1Char('/') + file);
QMap<QString, QStringList>::Iterator mapIt = sortedPlugins.find(fInfo.fileName());
if (mapIt == sortedPlugins.end()) {
mapIt = sortedPlugins.insert(fInfo.fileName(), QStringList());
}
mapIt.value().append(fInfo.absoluteFilePath());
}
}
QMap<QString, QStringList>::ConstIterator mapIt = sortedPlugins.constBegin();
QMap<QString, QStringList>::ConstIterator mapEnd = sortedPlugins.constEnd();
for (; mapIt != mapEnd; ++mapIt) {
PluginInfo info;
QString doc;
info.m_absXMLFileName = KXMLGUIClient::findMostRecentXMLFile(mapIt.value(), doc);
if (info.m_absXMLFileName.isEmpty()) {
continue;
}
// qDebug() << "found KParts Plugin : " << info.m_absXMLFileName;
info.m_relXMLFileName = QLatin1String("kpartplugins/") + mapIt.key();
info.m_document.setContent(doc);
if (info.m_document.documentElement().isNull()) {
continue;
}
plugins.append(info);
}
return plugins;
}
void Plugin::loadPlugins(QObject *parent, const QString &componentName)
{
loadPlugins(parent, pluginInfos(componentName), componentName);
}
void Plugin::loadPlugins(QObject *parent, const QList<PluginInfo> &pluginInfos, const QString &componentName)
{
for (const auto &pluginInfo : pluginInfos) {
const QString library = pluginInfo.m_document.documentElement().attribute(QStringLiteral("library"));
if (library.isEmpty() || hasPlugin(parent, library)) {
continue;
}
Plugin *plugin = loadPlugin(parent, library, pluginInfo.m_document.documentElement().attribute(QStringLiteral("X-KDE-PluginKeyword")));
if (plugin) {
plugin->d->m_parentInstance = componentName;
plugin->setXMLFile(pluginInfo.m_relXMLFileName, false, false);
plugin->setDOMDocument(pluginInfo.m_document);
}
}
}
void Plugin::loadPlugins(QObject *parent, const QList<PluginInfo> &pluginInfos)
{
loadPlugins(parent, pluginInfos, QString());
}
// static
Plugin *Plugin::loadPlugin(QObject *parent, const QString &libname, const QString &keyword)
{
KPluginLoader loader(libname);
KPluginFactory *factory = loader.factory();
if (!factory) {
return nullptr;
}
Plugin *plugin = factory->create<Plugin>(keyword, parent);
if (!plugin) {
return nullptr;
}
plugin->d->m_library = libname;
return plugin;
}
QList<KParts::Plugin *> Plugin::pluginObjects(QObject *parent)
{
QList<KParts::Plugin *> objects;
if (!parent) {
return objects;
}
objects = parent->findChildren<Plugin *>(QString(), Qt::FindDirectChildrenOnly);
return objects;
}
bool Plugin::hasPlugin(QObject *parent, const QString &library)
{
const QObjectList plugins = parent->children();
return std::any_of(plugins.begin(), plugins.end(), [&library](QObject *p) {
Plugin *plugin = qobject_cast<Plugin *>(p);
return (plugin && plugin->d->m_library == library);
});
}
void Plugin::setMetaData(const KPluginMetaData &metaData)
{
// backward-compatible registration, usage deprecated
KXMLGUIClient::setComponentName(metaData.pluginId(), metaData.name());
}
void Plugin::loadPlugins(QObject *parent,
KXMLGUIClient *parentGUIClient,
const QString &componentName,
bool enableNewPluginsByDefault,
int interfaceVersionRequired)
{
KConfigGroup cfgGroup(KSharedConfig::openConfig(componentName + QLatin1String("rc")), "KParts Plugins");
const QList<PluginInfo> plugins = pluginInfos(componentName);
for (const auto &pluginInfo : plugins) {
QDomElement docElem = pluginInfo.m_document.documentElement();
QString library = docElem.attribute(QStringLiteral("library"));
QString keyword;
if (library.isEmpty()) {
continue;
}
// Check configuration
const QString name = docElem.attribute(QStringLiteral("name"));
bool pluginEnabled = enableNewPluginsByDefault;
if (cfgGroup.hasKey(name + QLatin1String("Enabled"))) {
pluginEnabled = cfgGroup.readEntry(name + QLatin1String("Enabled"), false);
} else { // no user-setting, load plugin default setting
QString relPath = componentName + QLatin1Char('/') + pluginInfo.m_relXMLFileName;
relPath.truncate(relPath.lastIndexOf(QLatin1Char('.'))); // remove extension
relPath += QLatin1String(".desktop");
// qDebug() << "looking for " << relPath;
const QString desktopfile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, relPath);
if (!desktopfile.isEmpty()) {
// qDebug() << "loadPlugins found desktop file for " << name << ": " << desktopfile;
KDesktopFile _desktop(desktopfile);
const KConfigGroup desktop = _desktop.desktopGroup();
keyword = desktop.readEntry("X-KDE-PluginKeyword", "");
pluginEnabled = desktop.readEntry("X-KDE-PluginInfo-EnabledByDefault", enableNewPluginsByDefault);
if (interfaceVersionRequired != 0) {
const int version = desktop.readEntry("X-KDE-InterfaceVersion", 1);
if (version != interfaceVersionRequired) {
// qDebug() << "Discarding plugin " << name << ", interface version " << version << ", expected " << interfaceVersionRequired;
pluginEnabled = false;
}
}
} else {
// qDebug() << "loadPlugins no desktop file found in " << relPath;
}
}
// search through already present plugins
const QObjectList pluginList = parent->children();
bool pluginFound = false;
for (auto *p : pluginList) {
Plugin *plugin = qobject_cast<Plugin *>(p);
if (plugin && plugin->d->m_library == library) {
// delete and unload disabled plugins
if (!pluginEnabled) {
// qDebug() << "remove plugin " << name;
KXMLGUIFactory *factory = plugin->factory();
if (factory) {
factory->removeClient(plugin);
}
delete plugin;
}
pluginFound = true;
break;
}
}
// if the plugin is already loaded or if it's disabled in the
// configuration do nothing
if (pluginFound || !pluginEnabled) {
continue;
}
// qDebug() << "load plugin " << name << " " << library << " " << keyword;
Plugin *plugin = loadPlugin(parent, library, keyword);
if (plugin) {
plugin->d->m_parentInstance = componentName;
plugin->setXMLFile(pluginInfo.m_relXMLFileName, false, false);
plugin->setDOMDocument(pluginInfo.m_document);
parentGUIClient->insertChildClient(plugin);
}
}
}
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef PLUGIN_H
#define PLUGIN_H
#include <libkonq_export.h>
#include <KXMLGUIClient>
#include <QDomElement>
#include <QObject>
#include <memory>
class KPluginMetaData;
namespace KParts
{
class PluginPrivate;
/**
* @class Plugin plugin.h <KParts/Plugin>
*
* @short A plugin is the way to add actions to an existing KParts application,
* or to a Part.
*
* The XML of those plugins looks exactly like of the shell or parts,
* with one small difference: The document tag should have an additional
* attribute, named "library", and contain the name of the library implementing
* the plugin.
*
* If you want this plugin to be used by a part, you need to
* install the rc file under the directory
* "data" (KDEDIR/share/apps usually)+"/instancename/kpartplugins/"
* where instancename is the name of the part's instance.
*
* You should also install a "plugin info" .desktop file with the same name.
* \see KPluginInfo
*/
class LIBKONQ_EXPORT Plugin : public QObject, virtual public KXMLGUIClient
{
Q_OBJECT
public:
struct PluginInfo {
QString m_relXMLFileName; // relative filename, i.e. kpartplugins/name
QString m_absXMLFileName; // full path of most recent filename matching the relative
// filename
QDomDocument m_document;
};
/**
* Construct a new KParts plugin.
*/
explicit Plugin(QObject *parent = nullptr);
/**
* Destructor.
*/
~Plugin() override;
/**
* Reimplemented for internal reasons
*/
QString xmlFile() const override;
/**
* Reimplemented for internal reasons
*/
QString localXMLFile() const override;
/**
* Load the plugin libraries from the directories appropriate
* to @p instance and make the Plugin objects children of @p parent.
*
* It is recommended to use the last loadPlugins method instead,
* to support enabling and disabling of plugins.
*/
static void loadPlugins(QObject *parent, const QString &instance);
/**
* Load the plugin libraries specified by the list @p docs and make the
* Plugin objects children of @p parent .
*
* It is recommended to use the last loadPlugins method instead,
* to support enabling and disabling of plugins.
*/
static void loadPlugins(QObject *parent, const QList<PluginInfo> &pluginInfos);
/**
* Load the plugin libraries specified by the list @p pluginInfos, make the
* Plugin objects children of @p parent, and use the given @p instance.
*
* It is recommended to use the last loadPlugins method instead,
* to support enabling and disabling of plugins.
*/
static void loadPlugins(QObject *parent, const QList<PluginInfo> &pluginInfos, const QString &instance);
/**
* Load the plugin libraries for the given @p instance, make the
* Plugin objects children of @p parent, and insert the plugin as a child GUI client
* of @p parentGUIClient.
*
* This method uses the KConfig object of the given instance, to find out which
* plugins are enabled and which are disabled. What happens by default (i.e.
* for new plugins that are not in that config file) is controlled by
* @p enableNewPluginsByDefault. It can be overridden by the plugin if it
* sets the X-KDE-PluginInfo-EnabledByDefault key in the .desktop file
* (with the same name as the .rc file)
*
* If a disabled plugin is already loaded it will be removed from the GUI
* factory and deleted.
*
* If you change the binary interface offered by your part, you can avoid crashes
* from old plugins lying around by setting X-KDE-InterfaceVersion=2 in the
* .desktop files of the plugins, and passing 2 to @p interfaceVersionRequired, so that
* the old plugins are not loaded. Increase both numbers every time a
* binary incompatible change in the application's plugin interface is made.
*
*
* This method is automatically called by KParts::Part and by KParts::MainWindow.
* @see PartBase::setPluginLoadingMode, PartBase::setPluginInterfaceVersion
*
* If you call this method in an already constructed GUI (like when the user
* has changed which plugins are enabled) you need to add the new plugins to
* the KXMLGUIFactory:
* \code
* if( factory() )
* {
* const QList<KParts::Plugin *> plugins = KParts::Plugin::pluginObjects( this );
* for ( KParts::Plugin * plugin : plugins )
* factory()->addClient( plugin );
* }
* \endcode
*/
static void loadPlugins(QObject *parent,
KXMLGUIClient *parentGUIClient,
const QString &instance,
bool enableNewPluginsByDefault = true,
int interfaceVersionRequired = 0);
/**
* Returns a list of plugin objects loaded for @p parent. This
* functions basically iterates over the children of the given object
* and returns those that inherit from KParts::Plugin.
**/
static QList<Plugin *> pluginObjects(QObject *parent);
protected:
/**
* @since 5.77
*/
void setMetaData(const KPluginMetaData &metaData);
private:
/**
* Look for plugins in the @p instance's "data" directory (+"/kpartplugins")
*
* @return A list of QDomDocument s, containing the parsed xml documents returned by plugins.
*/
static QList<Plugin::PluginInfo> pluginInfos(const QString &instance);
/**
* @internal
* @return The plugin created from the library @p libname
*/
static Plugin *loadPlugin(QObject *parent, const QString &libname, const QString &keyword = QString());
private:
static bool hasPlugin(QObject *parent, const QString &library);
std::unique_ptr<PluginPrivate> const d;
};
}
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment