File indexing completed on 2024-04-21 04:20:51
0001 /************************************************************************ 0002 * * 0003 * This file is part of Kooka, a scanning/OCR application using * 0004 * Qt <http://www.qt.io> and KDE Frameworks <http://www.kde.org>. * 0005 * * 0006 * Copyright (C) 2018 Jonathan Marten <jjm@keelhaul.me.uk> * 0007 * * 0008 * Kooka is free software; you can redistribute it and/or modify it * 0009 * under the terms of the GNU Library General Public License as * 0010 * published by the Free Software Foundation and appearing in the * 0011 * file COPYING included in the packaging of this file; either * 0012 * version 2 of the License, or (at your option) any later version. * 0013 * * 0014 * As a special exception, permission is given to link this program * 0015 * with any version of the KADMOS OCR/ICR engine (a product of * 0016 * reRecognition GmbH, Kreuzlingen), and distribute the resulting * 0017 * executable without including the source code for KADMOS in the * 0018 * source distribution. * 0019 * * 0020 * This program is distributed in the hope that it will be useful, * 0021 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0023 * GNU General Public License for more details. * 0024 * * 0025 * You should have received a copy of the GNU General Public * 0026 * License along with this program; see the file COPYING. If * 0027 * not, see <http://www.gnu.org/licenses/>. * 0028 * * 0029 ************************************************************************/ 0030 0031 #include "pluginmanager.h" 0032 0033 #include <kpluginmetadata.h> 0034 #include <kpluginfactory.h> 0035 #include <klocalizedstring.h> 0036 0037 #include "abstractplugin.h" 0038 #include "kooka_logging.h" 0039 0040 #include <qcoreapplication.h> 0041 0042 0043 static PluginManager *sInstance = nullptr; 0044 0045 0046 PluginManager::PluginManager() 0047 { 0048 // There is an anomaly between KPluginMetaData::findPlugins() which 0049 // is used by allPlugins(), and the KPluginMetaData(const QString &file) 0050 // constructor which is used by loadPlugin(). KPluginMetaData::findPlugins() 0051 // uses KPluginMetaDataPrivate::forEachPlugin() which prepends the 0052 // application directory (containing the current executable) to 0053 // QCoreApplication::libraryPaths(), giving built but uninstalled plugins 0054 // priority over installed ones. The KPluginMetaData(const QString &file) 0055 // constructor does not do this and passes the specified file name directly 0056 // to QPluginLoader, so using QCoreApplication::libraryPaths() unchanged. 0057 // The result is that allPlugins() will enumerate uninstalled development 0058 // plugins but loadPlugin() will not necessarily load from the same location. 0059 // 0060 // To work around this, ensure that the application directory is at the 0061 // front of QCoreApplication::libraryPaths(). 0062 0063 QStringList pluginPaths = QCoreApplication::libraryPaths(); 0064 qCDebug(KOOKA_LOG) << "initial paths" << pluginPaths; 0065 Q_ASSERT(!pluginPaths.isEmpty()); 0066 0067 QString appDirPath = QCoreApplication::applicationDirPath(); 0068 pluginPaths.removeAll(appDirPath); 0069 pluginPaths.prepend(appDirPath); 0070 0071 qCDebug(KOOKA_LOG) << "using paths" << pluginPaths; 0072 QCoreApplication::setLibraryPaths(pluginPaths); 0073 } 0074 0075 0076 PluginManager *PluginManager::self() 0077 { 0078 if (sInstance==nullptr) 0079 { 0080 sInstance = new PluginManager(); 0081 qCDebug(KOOKA_LOG) << "allocated global instance"; 0082 } 0083 return (sInstance); 0084 } 0085 0086 0087 static QString commentAsRichText(const QString &comment) 0088 { 0089 // The 'comment' returned from KService is a QString which may have KUIT markup. 0090 // The conversion from that to a KLocalizedString, then back to a QString, actually 0091 // implements the KUIT markup. The "@info" context ensures that the markup is 0092 // converted to rich text (HTML). 0093 // 0094 // There is no need to specify a translation domain, because the string from the 0095 // service desktop file will already have been translated. 0096 0097 return (kxi18nc("@info", comment.toLocal8Bit().constData()).toString()); 0098 } 0099 0100 0101 static QString pluginTypeString(PluginManager::PluginType type) 0102 { 0103 switch (type) 0104 { 0105 case PluginManager::OcrPlugin: return ("ocr"); 0106 case PluginManager::DestinationPlugin: return ("destination"); 0107 } 0108 Q_UNREACHABLE(); 0109 return QString(); 0110 } 0111 0112 0113 AbstractPlugin *PluginManager::loadPlugin(PluginManager::PluginType type, const QString &name) 0114 { 0115 qCDebug(KOOKA_LOG) << "want type" << type << name; 0116 0117 AbstractPlugin *plugin = mLoadedPlugins.value(type); 0118 if (plugin!=nullptr) // a plugin is loaded 0119 { 0120 if (name==plugin->pluginInfo()->key) // wanted plugin is already loaded 0121 { 0122 qCDebug(KOOKA_LOG) << "already loaded"; 0123 return (plugin); 0124 } 0125 0126 qCDebug(KOOKA_LOG) << "unloading current" << plugin->pluginInfo()->key; 0127 delete plugin; 0128 plugin = nullptr; 0129 } 0130 0131 if (name.isEmpty()) // just want to unload current 0132 { 0133 mLoadedPlugins[type] = nullptr; // note that nothing is loaded 0134 return (nullptr); // no more to do 0135 } 0136 0137 KPluginMetaData md(QStringLiteral("kooka_") + pluginTypeString(type) + QStringLiteral("/") + name); 0138 plugin = KPluginFactory::instantiatePlugin<AbstractPlugin>(md).plugin; 0139 0140 if (plugin!=nullptr) 0141 { 0142 qCDebug(KOOKA_LOG) << "created plugin from library" << md.fileName(); 0143 0144 AbstractPluginInfo *info = new AbstractPluginInfo; 0145 info->key = md.pluginId(); 0146 info->name = md.name(); 0147 info->icon = md.iconName(); 0148 info->description = commentAsRichText(md.description()); 0149 0150 plugin->mPluginInfo = info; 0151 } 0152 else qCWarning(KOOKA_LOG) << "Cannot create plugin from library" << md.fileName(); 0153 0154 mLoadedPlugins[type] = plugin; 0155 return (plugin); 0156 } 0157 0158 0159 QMap<QString,AbstractPluginInfo> PluginManager::allPlugins(PluginManager::PluginType type) const 0160 { 0161 qCDebug(KOOKA_LOG) << "want all of type" << type; 0162 0163 QMap<QString,AbstractPluginInfo> plugins; 0164 0165 const QVector<KPluginMetaData> list = KPluginMetaData::findPlugins(QStringLiteral("kooka_") + pluginTypeString(type)); 0166 0167 qCDebug(KOOKA_LOG) << "query count" << list.count(); 0168 if (list.isEmpty()) qCWarning(KOOKA_LOG) << "No plugin services found"; 0169 else 0170 { 0171 for (const KPluginMetaData &service : list) 0172 { 0173 qCDebug(KOOKA_LOG) << " found" << service.pluginId(); 0174 0175 struct AbstractPluginInfo info; 0176 info.key = service.pluginId(); 0177 info.name = service.name(); 0178 info.icon = service.iconName(); 0179 info.description = commentAsRichText(service.description()); 0180 0181 plugins[info.key] = info; 0182 } 0183 } 0184 0185 return (plugins); 0186 } 0187 0188 0189 AbstractPlugin *PluginManager::currentPlugin(PluginManager::PluginType type) const 0190 { 0191 return (mLoadedPlugins.value(type)); 0192 }