| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). |
| ** All rights reserved. |
| ** Contact: Nokia Corporation (qt-info@nokia.com) |
| ** |
| ** This file is part of the Qt Designer of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** GNU Lesser General Public License Usage |
| ** This file may be used under the terms of the GNU Lesser General Public |
| ** License version 2.1 as published by the Free Software Foundation and |
| ** appearing in the file LICENSE.LGPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU Lesser |
| ** General Public License version 2.1 requirements will be met: |
| ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| ** |
| ** In addition, as a special exception, Nokia gives you certain additional |
| ** rights. These rights are described in the Nokia Qt LGPL Exception |
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "pluginmanager_p.h" |
| #include "qdesigner_utils_p.h" |
| #include "qdesigner_qsettings_p.h" |
| |
| #include <QtDesigner/QDesignerFormEditorInterface> |
| #include <QtDesigner/QDesignerCustomWidgetInterface> |
| #include <QtDesigner/QExtensionManager> |
| #include <QtDesigner/QDesignerLanguageExtension> |
| |
| #include <QtCore/QDir> |
| #include <QtCore/QFile> |
| #include <QtCore/QFileInfo> |
| #include <QtCore/QSet> |
| #include <QtCore/QPluginLoader> |
| #include <QtCore/QLibrary> |
| #include <QtCore/QLibraryInfo> |
| #include <QtCore/qdebug.h> |
| #include <QtCore/QMap> |
| #include <QtCore/QSettings> |
| #include <QtCore/QCoreApplication> |
| |
| #include <QtCore/QXmlStreamReader> |
| #include <QtCore/QXmlStreamAttributes> |
| #include <QtCore/QXmlStreamAttribute> |
| |
| static const char *uiElementC = "ui"; |
| static const char *languageAttributeC = "language"; |
| static const char *widgetElementC = "widget"; |
| static const char *displayNameAttributeC = "displayname"; |
| static const char *classAttributeC = "class"; |
| static const char *customwidgetElementC = "customwidget"; |
| static const char *extendsElementC = "extends"; |
| static const char *addPageMethodC = "addpagemethod"; |
| static const char *propertySpecsC = "propertyspecifications"; |
| static const char *stringPropertySpecC = "stringpropertyspecification"; |
| static const char *stringPropertyNameAttrC = "name"; |
| static const char *stringPropertyTypeAttrC = "type"; |
| static const char *stringPropertyNoTrAttrC = "notr"; |
| static const char *jambiLanguageC = "jambi"; |
| |
| enum { debugPluginManager = 0 }; |
| |
| /* Custom widgets: Loading custom widgets is a 2-step process: PluginManager |
| * scans for its plugins in the constructor. At this point, it might not be safe |
| * to immediately initialize the custom widgets it finds, because the rest of |
| * Designer is not initialized yet. |
| * Later on, in ensureInitialized(), the plugin instances (including static ones) |
| * are iterated and the custom widget plugins are initialized and added to internal |
| * list of custom widgets and parsed data. Should there be a parse error or a language |
| * mismatch, it kicks out the respective custom widget. The m_initialized flag |
| * is used to indicate the state. |
| * Later, someone might call registerNewPlugins(), which agains clears the flag via |
| * registerPlugin() and triggers the process again. |
| * Also note that Jambi fakes a custom widget collection that changes its contents |
| * every time the project is switched. So, custom widget plugins can actually |
| * disappear, and the custom widget list must be cleared and refilled in |
| * ensureInitialized() after registerNewPlugins. */ |
| |
| QT_BEGIN_NAMESPACE |
| |
| static QStringList unique(const QStringList &lst) |
| { |
| const QSet<QString> s = QSet<QString>::fromList(lst); |
| return s.toList(); |
| } |
| |
| QStringList QDesignerPluginManager::defaultPluginPaths() |
| { |
| QStringList result; |
| |
| const QStringList path_list = QCoreApplication::libraryPaths(); |
| |
| const QString designer = QLatin1String("designer"); |
| foreach (const QString &path, path_list) { |
| QString libPath = path; |
| libPath += QDir::separator(); |
| libPath += designer; |
| result.append(libPath); |
| } |
| |
| QString homeLibPath = QDir::homePath(); |
| homeLibPath += QDir::separator(); |
| homeLibPath += QLatin1String(".designer"); |
| homeLibPath += QDir::separator(); |
| homeLibPath += QLatin1String("plugins"); |
| |
| result.append(homeLibPath); |
| return result; |
| } |
| |
| // Figure out the language designer is running. ToDo: Introduce some |
| // Language name API to QDesignerLanguageExtension? |
| |
| static inline QString getDesignerLanguage(QDesignerFormEditorInterface *core) |
| { |
| if (QDesignerLanguageExtension *lang = qt_extension<QDesignerLanguageExtension *>(core->extensionManager(), core)) { |
| if (lang->uiExtension() == QLatin1String("jui")) |
| return QLatin1String(jambiLanguageC); |
| return QLatin1String("unknown"); |
| } |
| return QLatin1String("c++"); |
| } |
| |
| // ---------------- QDesignerCustomWidgetSharedData |
| |
| class QDesignerCustomWidgetSharedData : public QSharedData { |
| public: |
| // Type of a string property |
| typedef QPair<qdesigner_internal::TextPropertyValidationMode, bool> StringPropertyType; |
| typedef QHash<QString, StringPropertyType> StringPropertyTypeMap; |
| |
| explicit QDesignerCustomWidgetSharedData(const QString &thePluginPath) : pluginPath(thePluginPath) {} |
| void clearXML(); |
| |
| QString pluginPath; |
| |
| QString xmlClassName; |
| QString xmlDisplayName; |
| QString xmlLanguage; |
| QString xmlAddPageMethod; |
| QString xmlExtends; |
| |
| StringPropertyTypeMap xmlStringPropertyTypeMap; |
| }; |
| |
| void QDesignerCustomWidgetSharedData::clearXML() |
| { |
| xmlClassName.clear(); |
| xmlDisplayName.clear(); |
| xmlLanguage.clear(); |
| xmlAddPageMethod.clear(); |
| xmlExtends.clear(); |
| xmlStringPropertyTypeMap.clear(); |
| } |
| |
| // ---------------- QDesignerCustomWidgetData |
| |
| QDesignerCustomWidgetData::QDesignerCustomWidgetData(const QString &pluginPath) : |
| m_d(new QDesignerCustomWidgetSharedData(pluginPath)) |
| { |
| } |
| |
| QDesignerCustomWidgetData::QDesignerCustomWidgetData(const QDesignerCustomWidgetData &o) : |
| m_d(o.m_d) |
| { |
| } |
| |
| QDesignerCustomWidgetData& QDesignerCustomWidgetData::operator=(const QDesignerCustomWidgetData &o) |
| { |
| m_d.operator=(o.m_d); |
| return *this; |
| } |
| |
| QDesignerCustomWidgetData::~QDesignerCustomWidgetData() |
| { |
| } |
| |
| bool QDesignerCustomWidgetData::isNull() const |
| { |
| return m_d->xmlClassName.isEmpty() || m_d->pluginPath.isEmpty(); |
| } |
| |
| QString QDesignerCustomWidgetData::xmlClassName() const |
| { |
| return m_d->xmlClassName; |
| } |
| |
| QString QDesignerCustomWidgetData::xmlLanguage() const |
| { |
| return m_d->xmlLanguage; |
| } |
| |
| QString QDesignerCustomWidgetData::xmlAddPageMethod() const |
| { |
| return m_d->xmlAddPageMethod; |
| } |
| |
| QString QDesignerCustomWidgetData::xmlExtends() const |
| { |
| return m_d->xmlExtends; |
| } |
| |
| QString QDesignerCustomWidgetData::xmlDisplayName() const |
| { |
| return m_d->xmlDisplayName; |
| } |
| |
| QString QDesignerCustomWidgetData::pluginPath() const |
| { |
| return m_d->pluginPath; |
| } |
| |
| bool QDesignerCustomWidgetData::xmlStringPropertyType(const QString &name, StringPropertyType *type) const |
| { |
| QDesignerCustomWidgetSharedData::StringPropertyTypeMap::const_iterator it = m_d->xmlStringPropertyTypeMap.constFind(name); |
| if (it == m_d->xmlStringPropertyTypeMap.constEnd()) { |
| *type = StringPropertyType(qdesigner_internal::ValidationRichText, true); |
| return false; |
| } |
| *type = it.value(); |
| return true; |
| } |
| |
| // Wind a QXmlStreamReader until it finds an element. Returns index or one of FindResult |
| enum FindResult { FindError = -2, ElementNotFound = -1 }; |
| |
| static int findElement(const QStringList &desiredElts, QXmlStreamReader &sr) |
| { |
| while (true) { |
| switch(sr.readNext()) { |
| case QXmlStreamReader::EndDocument: |
| return ElementNotFound; |
| case QXmlStreamReader::Invalid: |
| return FindError; |
| case QXmlStreamReader::StartElement: { |
| const int index = desiredElts.indexOf(sr.name().toString().toLower()); |
| if (index >= 0) |
| return index; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| return FindError; |
| } |
| |
| static inline QString msgXmlError(const QString &name, const QString &errorMessage) |
| { |
| return QDesignerPluginManager::tr("An XML error was encountered when parsing the XML of the custom widget %1: %2").arg(name, errorMessage); |
| } |
| |
| static inline QString msgAttributeMissing(const QString &name) |
| { |
| return QDesignerPluginManager::tr("A required attribute ('%1') is missing.").arg(name); |
| } |
| |
| static qdesigner_internal::TextPropertyValidationMode typeStringToType(const QString &v, bool *ok) |
| { |
| *ok = true; |
| if (v == QLatin1String("multiline")) |
| return qdesigner_internal::ValidationMultiLine; |
| if (v == QLatin1String("richtext")) |
| return qdesigner_internal::ValidationRichText; |
| if (v == QLatin1String("stylesheet")) |
| return qdesigner_internal::ValidationStyleSheet; |
| if (v == QLatin1String("singleline")) |
| return qdesigner_internal::ValidationSingleLine; |
| if (v == QLatin1String("objectname")) |
| return qdesigner_internal::ValidationObjectName; |
| if (v == QLatin1String("objectnamescope")) |
| return qdesigner_internal::ValidationObjectNameScope; |
| if (v == QLatin1String("url")) |
| return qdesigner_internal::ValidationURL; |
| *ok = false; |
| return qdesigner_internal::ValidationRichText; |
| } |
| |
| static bool parsePropertySpecs(QXmlStreamReader &sr, |
| QDesignerCustomWidgetSharedData::StringPropertyTypeMap *rc, |
| QString *errorMessage) |
| { |
| const QString propertySpecs = QLatin1String(propertySpecsC); |
| const QString stringPropertySpec = QLatin1String(stringPropertySpecC); |
| const QString stringPropertyTypeAttr = QLatin1String(stringPropertyTypeAttrC); |
| const QString stringPropertyNoTrAttr = QLatin1String(stringPropertyNoTrAttrC); |
| const QString stringPropertyNameAttr = QLatin1String(stringPropertyNameAttrC); |
| |
| while (!sr.atEnd()) { |
| switch(sr.readNext()) { |
| case QXmlStreamReader::StartElement: { |
| if (sr.name() != stringPropertySpec) { |
| *errorMessage = QDesignerPluginManager::tr("An invalid property specification ('%1') was encountered. Supported types: %2").arg(sr.name().toString(), stringPropertySpec); |
| return false; |
| } |
| const QXmlStreamAttributes atts = sr.attributes(); |
| const QString name = atts.value(stringPropertyNameAttr).toString(); |
| const QString type = atts.value(stringPropertyTypeAttr).toString(); |
| const QString notrS = atts.value(stringPropertyNoTrAttr).toString(); //Optional |
| |
| if (type.isEmpty()) { |
| *errorMessage = msgAttributeMissing(stringPropertyTypeAttr); |
| return false; |
| } |
| if (name.isEmpty()) { |
| *errorMessage = msgAttributeMissing(stringPropertyNameAttr); |
| return false; |
| } |
| bool typeOk; |
| const bool noTr = notrS == QLatin1String("true") || notrS == QLatin1String("1"); |
| QDesignerCustomWidgetSharedData::StringPropertyType v(typeStringToType(type, &typeOk), !noTr); |
| if (!typeOk) { |
| *errorMessage = QDesignerPluginManager::tr("'%1' is not a valid string property specification.").arg(type); |
| return false; |
| } |
| rc->insert(name, v); |
| } |
| break; |
| case QXmlStreamReader::EndElement: // Outer </stringproperties> |
| if (sr.name() == propertySpecs) |
| return true; |
| default: |
| break; |
| } |
| } |
| return true; |
| } |
| |
| QDesignerCustomWidgetData::ParseResult |
| QDesignerCustomWidgetData::parseXml(const QString &xml, const QString &name, QString *errorMessage) |
| { |
| if (debugPluginManager) |
| qDebug() << Q_FUNC_INFO << name; |
| |
| QDesignerCustomWidgetSharedData &data = *m_d; |
| data.clearXML(); |
| |
| QXmlStreamReader sr(xml); |
| |
| bool foundUI = false; |
| bool foundWidget = false; |
| ParseResult rc = ParseOk; |
| // Parse for the (optional) <ui> or the first <widget> element |
| QStringList elements; |
| elements.push_back(QLatin1String(uiElementC)); |
| elements.push_back(QLatin1String(widgetElementC)); |
| for (int i = 0; i < 2 && !foundWidget; i++) { |
| switch (findElement(elements, sr)) { |
| case FindError: |
| *errorMessage = msgXmlError(name, sr.errorString()); |
| return ParseError; |
| case ElementNotFound: |
| *errorMessage = QDesignerPluginManager::tr("The XML of the custom widget %1 does not contain any of the elements <widget> or <ui>.").arg(name); |
| return ParseError; |
| case 0: { // <ui> |
| const QXmlStreamAttributes attributes = sr.attributes(); |
| data.xmlLanguage = attributes.value(QLatin1String(languageAttributeC)).toString(); |
| data.xmlDisplayName = attributes.value(QLatin1String(displayNameAttributeC)).toString(); |
| foundUI = true; |
| } |
| break; |
| case 1: // <widget>: Do some sanity checks |
| data.xmlClassName = sr.attributes().value(QLatin1String(classAttributeC)).toString(); |
| if (data.xmlClassName.isEmpty()) { |
| *errorMessage = QDesignerPluginManager::tr("The class attribute for the class %1 is missing.").arg(name); |
| rc = ParseWarning; |
| } else { |
| if (data.xmlClassName != name) { |
| *errorMessage = QDesignerPluginManager::tr("The class attribute for the class %1 does not match the class name %2.").arg(data.xmlClassName, name); |
| rc = ParseWarning; |
| } |
| } |
| foundWidget = true; |
| break; |
| } |
| } |
| // Parse out the <customwidget> element which might be present if <ui> was there |
| if (!foundUI) |
| return rc; |
| elements.clear(); |
| elements.push_back(QLatin1String(customwidgetElementC)); |
| switch (findElement(elements, sr)) { |
| case FindError: |
| *errorMessage = msgXmlError(name, sr.errorString()); |
| return ParseError; |
| case ElementNotFound: |
| return rc; |
| default: |
| break; |
| } |
| // Find <extends>, <addPageMethod>, <stringproperties> |
| elements.clear(); |
| elements.push_back(QLatin1String(extendsElementC)); |
| elements.push_back(QLatin1String(addPageMethodC)); |
| elements.push_back(QLatin1String(propertySpecsC)); |
| while (true) { |
| switch (findElement(elements, sr)) { |
| case FindError: |
| *errorMessage = msgXmlError(name, sr.errorString()); |
| return ParseError; |
| case ElementNotFound: |
| return rc; |
| case 0: // <extends> |
| data.xmlExtends = sr.readElementText(); |
| if (sr.tokenType() != QXmlStreamReader::EndElement) { |
| *errorMessage = msgXmlError(name, sr.errorString()); |
| return ParseError; |
| } |
| break; |
| case 1: // <addPageMethod> |
| data.xmlAddPageMethod = sr.readElementText(); |
| if (sr.tokenType() != QXmlStreamReader::EndElement) { |
| *errorMessage = msgXmlError(name, sr.errorString()); |
| return ParseError; |
| } |
| break; |
| case 2: // <stringproperties> |
| if (!parsePropertySpecs(sr, &m_d->xmlStringPropertyTypeMap, errorMessage)) { |
| *errorMessage = msgXmlError(name, *errorMessage); |
| return ParseError; |
| } |
| break; |
| } |
| } |
| return rc; |
| } |
| |
| // ---------------- QDesignerPluginManagerPrivate |
| |
| class QDesignerPluginManagerPrivate { |
| public: |
| typedef QPair<QString, QString> ClassNamePropertyNameKey; |
| |
| QDesignerPluginManagerPrivate(QDesignerFormEditorInterface *core); |
| |
| void clearCustomWidgets(); |
| bool addCustomWidget(QDesignerCustomWidgetInterface *c, |
| const QString &pluginPath, |
| const QString &designerLanguage); |
| void addCustomWidgets(const QObject *o, |
| const QString &pluginPath, |
| const QString &designerLanguage); |
| |
| QDesignerFormEditorInterface *m_core; |
| QStringList m_pluginPaths; |
| QStringList m_registeredPlugins; |
| // TODO: QPluginLoader also caches invalid plugins -> This seems to be dead code |
| QStringList m_disabledPlugins; |
| |
| typedef QMap<QString, QString> FailedPluginMap; |
| FailedPluginMap m_failedPlugins; |
| |
| // Synced lists of custom widgets and their data. Note that the list |
| // must be ordered for collections to appear in order. |
| QList<QDesignerCustomWidgetInterface *> m_customWidgets; |
| QList<QDesignerCustomWidgetData> m_customWidgetData; |
| |
| QStringList defaultPluginPaths() const; |
| |
| bool m_initialized; |
| }; |
| |
| QDesignerPluginManagerPrivate::QDesignerPluginManagerPrivate(QDesignerFormEditorInterface *core) : |
| m_core(core), |
| m_initialized(false) |
| { |
| } |
| |
| void QDesignerPluginManagerPrivate::clearCustomWidgets() |
| { |
| m_customWidgets.clear(); |
| m_customWidgetData.clear(); |
| } |
| |
| // Add a custom widget to the list if it parses correctly |
| // and is of the right language |
| bool QDesignerPluginManagerPrivate::addCustomWidget(QDesignerCustomWidgetInterface *c, |
| const QString &pluginPath, |
| const QString &designerLanguage) |
| { |
| if (debugPluginManager) |
| qDebug() << Q_FUNC_INFO << c->name(); |
| |
| if (!c->isInitialized()) |
| c->initialize(m_core); |
| // Parse the XML even if the plugin is initialized as Jambi might play tricks here |
| QDesignerCustomWidgetData data(pluginPath); |
| const QString domXml = c->domXml(); |
| if (!domXml.isEmpty()) { // Legacy: Empty XML means: Do not show up in widget box. |
| QString errorMessage; |
| const QDesignerCustomWidgetData::ParseResult pr = data.parseXml(domXml, c->name(), &errorMessage); |
| switch (pr) { |
| case QDesignerCustomWidgetData::ParseOk: |
| break; |
| case QDesignerCustomWidgetData::ParseWarning: |
| qdesigner_internal::designerWarning(errorMessage); |
| break; |
| case QDesignerCustomWidgetData::ParseError: |
| qdesigner_internal::designerWarning(errorMessage); |
| return false; |
| } |
| // Does the language match? |
| const QString pluginLanguage = data.xmlLanguage(); |
| if (!pluginLanguage.isEmpty() && pluginLanguage.compare(designerLanguage, Qt::CaseInsensitive)) |
| return false; |
| } |
| m_customWidgets.push_back(c); |
| m_customWidgetData.push_back(data); |
| return true; |
| } |
| |
| // Check the plugin interface for either a custom widget or a collection and |
| // add all contained custom widgets. |
| void QDesignerPluginManagerPrivate::addCustomWidgets(const QObject *o, |
| const QString &pluginPath, |
| const QString &designerLanguage) |
| { |
| if (QDesignerCustomWidgetInterface *c = qobject_cast<QDesignerCustomWidgetInterface*>(o)) { |
| addCustomWidget(c, pluginPath, designerLanguage); |
| return; |
| } |
| if (const QDesignerCustomWidgetCollectionInterface *coll = qobject_cast<QDesignerCustomWidgetCollectionInterface*>(o)) { |
| foreach(QDesignerCustomWidgetInterface *c, coll->customWidgets()) |
| addCustomWidget(c, pluginPath, designerLanguage); |
| } |
| } |
| |
| |
| // ---------------- QDesignerPluginManager |
| // As of 4.4, the header will be distributed with the Eclipse plugin. |
| |
| QDesignerPluginManager::QDesignerPluginManager(QDesignerFormEditorInterface *core) : |
| QObject(core), |
| m_d(new QDesignerPluginManagerPrivate(core)) |
| { |
| m_d->m_pluginPaths = defaultPluginPaths(); |
| const QSettings settings(qApp->organizationName(), QDesignerQSettings::settingsApplicationName()); |
| m_d->m_disabledPlugins = unique(settings.value(QLatin1String("PluginManager/DisabledPlugins")).toStringList()); |
| |
| // Register plugins |
| updateRegisteredPlugins(); |
| |
| if (debugPluginManager) |
| qDebug() << "QDesignerPluginManager::disabled: " << m_d->m_disabledPlugins << " static " << m_d->m_customWidgets.size(); |
| } |
| |
| QDesignerPluginManager::~QDesignerPluginManager() |
| { |
| syncSettings(); |
| delete m_d; |
| } |
| |
| QDesignerFormEditorInterface *QDesignerPluginManager::core() const |
| { |
| return m_d->m_core; |
| } |
| |
| QStringList QDesignerPluginManager::findPlugins(const QString &path) |
| { |
| if (debugPluginManager) |
| qDebug() << Q_FUNC_INFO << path; |
| const QDir dir(path); |
| if (!dir.exists()) |
| return QStringList(); |
| |
| const QFileInfoList infoList = dir.entryInfoList(QDir::Files); |
| if (infoList.empty()) |
| return QStringList(); |
| |
| // Load symbolic links but make sure all file names are unique as not |
| // to fall for something like 'libplugin.so.1 -> libplugin.so' |
| QStringList result; |
| const QFileInfoList::const_iterator icend = infoList.constEnd(); |
| for (QFileInfoList::const_iterator it = infoList.constBegin(); it != icend; ++it) { |
| QString fileName; |
| if (it->isSymLink()) { |
| const QFileInfo linkTarget = QFileInfo(it->symLinkTarget()); |
| if (linkTarget.exists() && linkTarget.isFile()) |
| fileName = linkTarget.absoluteFilePath(); |
| } else { |
| fileName = it->absoluteFilePath(); |
| } |
| if (!fileName.isEmpty() && QLibrary::isLibrary(fileName) && !result.contains(fileName)) |
| result += fileName; |
| } |
| return result; |
| } |
| |
| void QDesignerPluginManager::setDisabledPlugins(const QStringList &disabled_plugins) |
| { |
| m_d->m_disabledPlugins = disabled_plugins; |
| updateRegisteredPlugins(); |
| } |
| |
| void QDesignerPluginManager::setPluginPaths(const QStringList &plugin_paths) |
| { |
| m_d->m_pluginPaths = plugin_paths; |
| updateRegisteredPlugins(); |
| } |
| |
| QStringList QDesignerPluginManager::disabledPlugins() const |
| { |
| return m_d->m_disabledPlugins; |
| } |
| |
| QStringList QDesignerPluginManager::failedPlugins() const |
| { |
| return m_d->m_failedPlugins.keys(); |
| } |
| |
| QString QDesignerPluginManager::failureReason(const QString &pluginName) const |
| { |
| return m_d->m_failedPlugins.value(pluginName); |
| } |
| |
| QStringList QDesignerPluginManager::registeredPlugins() const |
| { |
| return m_d->m_registeredPlugins; |
| } |
| |
| QStringList QDesignerPluginManager::pluginPaths() const |
| { |
| return m_d->m_pluginPaths; |
| } |
| |
| QObject *QDesignerPluginManager::instance(const QString &plugin) const |
| { |
| if (m_d->m_disabledPlugins.contains(plugin)) |
| return 0; |
| |
| QPluginLoader loader(plugin); |
| return loader.instance(); |
| } |
| |
| void QDesignerPluginManager::updateRegisteredPlugins() |
| { |
| if (debugPluginManager) |
| qDebug() << Q_FUNC_INFO; |
| m_d->m_registeredPlugins.clear(); |
| foreach (const QString &path, m_d->m_pluginPaths) |
| registerPath(path); |
| } |
| |
| bool QDesignerPluginManager::registerNewPlugins() |
| { |
| if (debugPluginManager) |
| qDebug() << Q_FUNC_INFO; |
| |
| const int before = m_d->m_registeredPlugins.size(); |
| foreach (const QString &path, m_d->m_pluginPaths) |
| registerPath(path); |
| const bool newPluginsFound = m_d->m_registeredPlugins.size() > before; |
| // We force a re-initialize as Jambi collection might return |
| // different widget lists when switching projects. |
| m_d->m_initialized = false; |
| ensureInitialized(); |
| |
| return newPluginsFound; |
| } |
| |
| void QDesignerPluginManager::registerPath(const QString &path) |
| { |
| if (debugPluginManager) |
| qDebug() << Q_FUNC_INFO << path; |
| QStringList candidates = findPlugins(path); |
| |
| foreach (const QString &plugin, candidates) |
| registerPlugin(plugin); |
| } |
| |
| void QDesignerPluginManager::registerPlugin(const QString &plugin) |
| { |
| if (debugPluginManager) |
| qDebug() << Q_FUNC_INFO << plugin; |
| if (m_d->m_disabledPlugins.contains(plugin)) |
| return; |
| if (m_d->m_registeredPlugins.contains(plugin)) |
| return; |
| |
| QPluginLoader loader(plugin); |
| if (loader.isLoaded() || loader.load()) { |
| m_d->m_registeredPlugins += plugin; |
| QDesignerPluginManagerPrivate::FailedPluginMap::iterator fit = m_d->m_failedPlugins.find(plugin); |
| if (fit != m_d->m_failedPlugins.end()) |
| m_d->m_failedPlugins.erase(fit); |
| return; |
| } |
| |
| const QString errorMessage = loader.errorString(); |
| m_d->m_failedPlugins.insert(plugin, errorMessage); |
| } |
| |
| |
| |
| bool QDesignerPluginManager::syncSettings() |
| { |
| QSettings settings(qApp->organizationName(), QDesignerQSettings::settingsApplicationName()); |
| settings.beginGroup(QLatin1String("PluginManager")); |
| settings.setValue(QLatin1String("DisabledPlugins"), m_d->m_disabledPlugins); |
| settings.endGroup(); |
| return settings.status() == QSettings::NoError; |
| } |
| |
| void QDesignerPluginManager::ensureInitialized() |
| { |
| if (debugPluginManager) |
| qDebug() << Q_FUNC_INFO << m_d->m_initialized << m_d->m_customWidgets.size(); |
| |
| if (m_d->m_initialized) |
| return; |
| |
| const QString designerLanguage = getDesignerLanguage(m_d->m_core); |
| |
| m_d->clearCustomWidgets(); |
| // Add the static custom widgets |
| const QObjectList staticPluginObjects = QPluginLoader::staticInstances(); |
| if (!staticPluginObjects.empty()) { |
| const QString staticPluginPath = QCoreApplication::applicationFilePath(); |
| foreach(QObject *o, staticPluginObjects) |
| m_d->addCustomWidgets(o, staticPluginPath, designerLanguage); |
| } |
| foreach (const QString &plugin, m_d->m_registeredPlugins) |
| if (QObject *o = instance(plugin)) |
| m_d->addCustomWidgets(o, plugin, designerLanguage); |
| |
| m_d->m_initialized = true; |
| } |
| |
| QDesignerPluginManager::CustomWidgetList QDesignerPluginManager::registeredCustomWidgets() const |
| { |
| const_cast<QDesignerPluginManager*>(this)->ensureInitialized(); |
| return m_d->m_customWidgets; |
| } |
| |
| QDesignerCustomWidgetData QDesignerPluginManager::customWidgetData(QDesignerCustomWidgetInterface *w) const |
| { |
| const int index = m_d->m_customWidgets.indexOf(w); |
| if (index == -1) |
| return QDesignerCustomWidgetData(); |
| return m_d->m_customWidgetData.at(index); |
| } |
| |
| QDesignerCustomWidgetData QDesignerPluginManager::customWidgetData(const QString &name) const |
| { |
| const int count = m_d->m_customWidgets.size(); |
| for (int i = 0; i < count; i++) |
| if (m_d->m_customWidgets.at(i)->name() == name) |
| return m_d->m_customWidgetData.at(i); |
| return QDesignerCustomWidgetData(); |
| } |
| |
| QObjectList QDesignerPluginManager::instances() const |
| { |
| QStringList plugins = registeredPlugins(); |
| |
| QObjectList lst; |
| foreach (const QString &plugin, plugins) { |
| if (QObject *o = instance(plugin)) |
| lst.append(o); |
| } |
| |
| return lst; |
| } |
| |
| QT_END_NAMESPACE |