/* * Copyright 2009 Ben Boeckel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ // Header include #include "PluginLoader.h" #include "PluginLoader_p.h" // Sigencore plugin includes #include #include #include // Sigmod includes #include // KDE includes #include #include #include // Qt includes #include #include using namespace Sigmod; using namespace Sigscript; using namespace Sigencore; using namespace Sigencore::Plugins; using namespace Sigtools; K_GLOBAL_STATIC(PluginLoader::Private, loader) QStringList PluginLoader::availablePlugins(const QString& type, const bool forceLookup) { if (forceLookup) loader->refresh(type); return loader->services(type); } KService::Ptr PluginLoader::service(const QString& type, const QString& name) { return loader->service(type, name); } QString PluginLoader::description(const QString& type, const QString& name) { PluginBase* plugin = loader->factory(type, name); if (plugin) return plugin->description(name); return QString(); } QIcon PluginLoader::icon(const QString& type, const QString& name) { PluginBase* plugin = loader->factory(type, name); if (plugin) return plugin->icon(name); return QIcon(); } QSharedPointer PluginLoader::game(const QString& name) { return loader->game(name); } Arena* PluginLoader::arena(const QString& name, GameWrapper* game, Config* parent) { ArenaPlugin* plugin = qobject_cast(loader->factory("Arena", name)); if (plugin) return plugin->getArena(name, game, parent); return NULL; } Canvas* PluginLoader::canvas(const QString& name, GameWrapper* game, Config* parent) { CanvasPlugin* plugin = qobject_cast(loader->factory("Canvas", name)); if (plugin) return plugin->getCanvas(name, game, parent); return NULL; } PluginLoader::Private::Private() { if (!KGlobal::dirs()->resourceDirs("sigmod").size()) KGlobal::dirs()->addResourceType("sigmod", "data", "sigmod/"); } PluginLoader::Private::~Private() { QStringList types = m_plugins.keys(); foreach (const QString& type, types) clean(type, true); } void PluginLoader::Private::refresh(const QString& type) { if (type == "Sigmod") m_games.clear(); // TODO: Progress dialog? m_available[type].clear(); clean(type); KService::List services = KServiceTypeTrader::self()->query(QString("Sigen/%1").arg(type), "[X-Sigen-MinVersion] <= 000101"); foreach (KService::Ptr service, services) { if (type == "Sigmod") { const QString name = service->property("X-Sigen-Sigmod-Name", QVariant::String).toString(); const QStringList version = service->property("X-Sigen-Sigmod-Version", QVariant::StringList).toStringList(); const QString variant = service->property("X-Sigen-Sigmod-Variant", QVariant::String).toString(); const QStringList variantVersion = service->property("X-Sigen-Sigmod-VariantVersion", QVariant::StringList).toStringList(); QString fullName; QString varPath; if (!variant.isEmpty()) varPath = QString("/%1/%2").arg(variant).arg(variantVersion.join("-")); fullName = QString("%1/%2%3").arg(name).arg(version.join("-")).arg(varPath); QString fileName = KStandardDirs::locate("sigmod", QString("%1.smod").arg(fullName)); if (!fileName.isEmpty()) { QFile file(fileName); QSharedPointer game; if (file.open(QIODevice::ReadOnly)) { QDomDocument xml; QString error; int line; int column; if (xml.setContent(&file, &error, &line, &column)) { if (xml.doctype().name() == "Sigmod") game = QSharedPointer(new Game(xml.documentElement())); else KMessageBox::error(NULL, "The file is not a Sigmod.", "Invalid Sigmod"); } else KMessageBox::error(NULL, QString("%1 at line %2, column %3").arg(error).arg(line).arg(column), "XML Error"); } file.close(); // TODO: validate if (game) { m_available[type][fullName] = Service(service, NULL); m_games[fullName] = game; } } } else { KPluginLoader loader(service->library()); KPluginFactory *factory = loader.factory(); if (factory) { PluginBase* plugin = NULL; if (type == "Arena") plugin = factory->create(this); else if (type == "Canvas") plugin = factory->create(this); else KMessageBox::information(NULL, QString("The plugin type \"Sigen/%1\" is not supported.").arg(type), "Unsupported plugin type"); if (plugin) { m_plugins[type].append(plugin); QStringList classes = plugin->classList(); foreach (const QString& className, classes) m_available[type][className] = Service(service, plugin); } } else KMessageBox::error(NULL, QString("The plugin of type \"Sigen/%1\" with name \"%2\" is not a valid Sigen plugin. The error was:\n%3").arg(type).arg(service->name()).arg(loader.errorString()), "Plugin loading error"); } } } QStringList PluginLoader::Private::services(const QString& type) { if (!m_available.contains(type)) refresh(type); if (m_available.contains(type)) return m_available[type].keys(); return QStringList(); } KService::Ptr PluginLoader::Private::service(const QString& type, const QString& name) { if (!m_available.contains(type)) refresh(type); if (m_available.contains(type) && m_available[type].contains(name)) return m_available[type][name].first; return KService::Ptr(); } PluginBase* PluginLoader::Private::factory(const QString& type, const QString& name) { if (!m_available.contains(type)) refresh(type); if (m_available.contains(type) && m_available[type].contains(name)) return m_available[type][name].second; return NULL; } QSharedPointer PluginLoader::Private::game(const QString& name) { if (!m_games.contains(name)) refresh("Sigmod"); if (m_games.contains(name)) return m_games[name]; return QSharedPointer(); } void PluginLoader::Private::clean(const QString& type, const bool errorOnExtra) { QMutableListIterator i(m_plugins[type]); while (i.hasNext()) { i.next(); if (!i.value()->classesUsedCount()) { delete i.value(); i.remove(); } else if (errorOnExtra) qCritical() << "There are still" << i.value()->classesUsedCount() << "classes in use by a plugin of type" << type; } }