/* * Stellarium * Copyright (C) 2006 Fabien Chereau * * 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. */ #include #include #include #include #include #include "StelModuleMgr.hpp" #include "StelApp.hpp" #include "StelModule.hpp" #include "StelFileMgr.hpp" #include "StelPluginInterface.hpp" #include "StelPropertyMgr.hpp" #include "StelIniParser.hpp" #include "StelLocaleMgr.hpp" #include "StelUtils.hpp" StelModuleMgr::StelModuleMgr() : callingListsToRegenerate(true), pluginDescriptorListLoaded(false) { qRegisterMetaType("StelModule::StelModuleSelectAction"); // Initialize empty call lists for each possible actions callOrders[StelModule::ActionDraw]=QList(); callOrders[StelModule::ActionUpdate]=QList(); callOrders[StelModule::ActionHandleMouseClicks]=QList(); callOrders[StelModule::ActionHandleMouseMoves]=QList(); callOrders[StelModule::ActionHandleKeys]=QList(); } StelModuleMgr::~StelModuleMgr() { } // Regenerate calling lists if necessary void StelModuleMgr::update() { if (callingListsToRegenerate) generateCallingLists(); callingListsToRegenerate = false; } /************************************************************************* Register a new StelModule to the list *************************************************************************/ void StelModuleMgr::registerModule(StelModule* m, bool fgenerateCallingLists) { QString name = m->objectName(); if (modules.contains(name)) { qWarning() << "Module" << name << "is already loaded."; return; } modules.insert(name, m); m->setParent(this); //register with StelPropertyMgr StelApp::getInstance().getStelPropertyManager()->registerObject(m); if (fgenerateCallingLists) generateCallingLists(); } /************************************************************************* Unregister and delete a StelModule. *************************************************************************/ void StelModuleMgr::unloadModule(const QString& moduleID, bool alsoDelete) { StelModule* m = getModule(moduleID); if (!m) { qWarning() << "Module" << moduleID << "is not loaded."; return; } modules.remove(moduleID); m->setParent(Q_NULLPTR); callingListsToRegenerate = true; if (alsoDelete) { m->deinit(); delete m; } } /************************************************************************* Get the corresponding module or Q_NULLPTR if can't find it. *************************************************************************/ StelModule* StelModuleMgr::getModule(const QString& moduleID, bool noWarning) const { StelModule* module = modules.value(moduleID, Q_NULLPTR); if (module == Q_NULLPTR) { if (noWarning==false) qWarning() << "Unable to find module called" << moduleID; } return module; } /************************************************************************* Load an external plugin *************************************************************************/ StelModule* StelModuleMgr::loadPlugin(const QString& moduleID) { for (const auto& desc : getPluginsList()) { if (desc.info.id==moduleID) { Q_ASSERT(desc.pluginInterface); StelModule* sMod = desc.pluginInterface->getStelModule(); qDebug() << "Loaded plugin" << moduleID; pluginDescriptorList[moduleID].loaded=true; return sMod; } } qWarning() << "Unable to find plugin called" << moduleID; return Q_NULLPTR; } QObjectList StelModuleMgr::loadExtensions(const QString &moduleID) { for (const auto& desc : getPluginsList()) { if (desc.info.id==moduleID) { Q_ASSERT(desc.pluginInterface); QObjectList exts = desc.pluginInterface->getExtensionList(); if(!exts.isEmpty()) { extensions.append(exts); emit extensionsAdded(exts); qDebug() << "Loaded"<getCallOrder(action)getCallOrder(action);} private: StelModule::StelModuleActionName action; }; // Unload all plugins void StelModuleMgr::unloadAllPlugins() { QListIterator i(getPluginsList()); i.toBack(); while (i.hasPrevious()) { const PluginDescriptor& d = i.previous(); if (d.loaded==false) continue; unloadModule(d.info.id, true); qDebug() << "Unloaded plugin" << d.info.id; } // Call update now to make sure that all references to the now deleted plugins modules // are removed (fix crashes at application shutdown). update(); } void StelModuleMgr::setPluginLoadAtStartup(const QString& key, bool b) { QSettings* conf = StelApp::getInstance().getSettings(); conf->setValue("plugins_load_at_startup/"+key, b); if (pluginDescriptorList.contains(key)) { pluginDescriptorList[key].loadAtStartup=b; } } bool StelModuleMgr::isPluginLoaded(const QString &moduleID) { if (pluginDescriptorList.contains(moduleID)) return pluginDescriptorList[moduleID].loaded; else return false; } /************************************************************************* Generate properly sorted calling lists for each action (e,g, draw, update) according to modules orders dependencies *************************************************************************/ void StelModuleMgr::generateCallingLists() { // For each actions (e.g. "draw", "update", etc..) for (auto mc = callOrders.begin(); mc != callOrders.end(); ++mc) { // Flush previous call orders mc.value().clear(); // and init them with modules in creation order for (auto* m : getAllModules()) { mc.value().push_back(m); } std::sort(mc.value().begin(), mc.value().end(), StelModuleOrderComparator(mc.key())); } } /************************************************************************* Return the list of all the external module found in the modules/ directories *************************************************************************/ QList StelModuleMgr::getPluginsList() { if (pluginDescriptorListLoaded) { return pluginDescriptorList.values(); } // First list all static plugins. // If a dynamic plugin with the same ID exists, it will take precedence on the static one. for (auto* plugin : QPluginLoader::staticInstances()) { StelPluginInterface* pluginInterface = qobject_cast(plugin); if (pluginInterface) { StelModuleMgr::PluginDescriptor mDesc; mDesc.info = pluginInterface->getPluginInfo(); mDesc.pluginInterface = pluginInterface; pluginDescriptorList.insert(mDesc.info.id, mDesc); } } // Then list dynamic libraries from the modules/ directory QSet moduleDirs; moduleDirs = StelFileMgr::listContents("modules",StelFileMgr::Directory); for (auto dir : moduleDirs) { QString moduleFullPath = QString("modules/") + dir + "/lib" + dir; #ifdef Q_OS_WIN moduleFullPath += ".dll"; #else #ifdef Q_OS_MAC moduleFullPath += ".dylib"; #else moduleFullPath += ".so"; #endif #endif moduleFullPath = StelFileMgr::findFile(moduleFullPath, StelFileMgr::File); if (moduleFullPath.isEmpty()) continue; QPluginLoader loader(moduleFullPath); if (!loader.load()) { qWarning() << "Couldn't load the dynamic library:" << QDir::toNativeSeparators(moduleFullPath) << ": " << loader.errorString(); qWarning() << "Plugin" << dir << "will not be loaded."; continue; } QObject* obj = loader.instance(); if (!obj) { qWarning() << "Couldn't open the dynamic library:" << QDir::toNativeSeparators(moduleFullPath) << ": " << loader.errorString(); qWarning() << "Plugin" << dir << "will not be open."; continue; } StelPluginInterface* pluginInterface = qobject_cast(obj); if (pluginInterface) { StelModuleMgr::PluginDescriptor mDesc; mDesc.info = pluginInterface->getPluginInfo(); mDesc.pluginInterface = pluginInterface; pluginDescriptorList.insert(mDesc.info.id, mDesc); } } // Load for each plugin if it should be loaded at startup QSettings* conf = StelApp::getInstance().getSettings(); Q_ASSERT(conf); conf->beginGroup("plugins_load_at_startup"); for (auto iter = pluginDescriptorList.begin(); iter != pluginDescriptorList.end(); ++iter) { bool startByDefault = iter.value().info.startByDefault; iter->loadAtStartup = conf->value(iter.key(), startByDefault).toBool(); // Save the value in case no such key exists conf->setValue(iter.key(), iter->loadAtStartup); } conf->endGroup(); pluginDescriptorListLoaded = true; return pluginDescriptorList.values(); } QString StelModuleMgr::getStandardSupportLinksInfo(QString moduleName, bool furtherInfo) { // Regexp to replace {text} with an HTML link. const QRegularExpression a_rx("[{]([^{]*)[}]"); QString html; html += "

" + q_("Links") + "

"; html += "

" + QString(q_("Support is provided via the Github website. Be sure to put \"%1\" in the subject when posting.")).arg(moduleName) + "

"; html += "

    "; // TRANSLATORS: The text between braces is the text of an HTML link. html += "
  • " + q_("If you have a question, you can {get an answer here}.").toHtmlEscaped().replace(a_rx, "\\1") + "
  • "; // TRANSLATORS: The text between braces is the text of an HTML link. html += "
  • " + q_("Bug reports and feature requests can be made {here}.").toHtmlEscaped().replace(a_rx, "\\1") + "
  • "; html += "

"; if (furtherInfo) { QStringList ver = StelUtils::getApplicationVersion().split("."); QString URL = QString("\\1").arg(ver[0], ver[1]); // TRANSLATORS: The text between braces is the text of an HTML link. html += "

" + q_("Further information can be found in the {developer documentation}.").toHtmlEscaped().replace(a_rx, URL) + "

"; } return html; }