tools/designer/src/lib/shared/pluginmanager.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Designer of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "pluginmanager_p.h"
       
    43 #include "qdesigner_utils_p.h"
       
    44 #include "qdesigner_qsettings_p.h"
       
    45 
       
    46 #include <QtDesigner/QDesignerFormEditorInterface>
       
    47 #include <QtDesigner/QDesignerCustomWidgetInterface>
       
    48 #include <QtDesigner/QExtensionManager>
       
    49 #include <QtDesigner/QDesignerLanguageExtension>
       
    50 
       
    51 #include <QtCore/QDir>
       
    52 #include <QtCore/QFile>
       
    53 #include <QtCore/QFileInfo>
       
    54 #include <QtCore/QSet>
       
    55 #include <QtCore/QPluginLoader>
       
    56 #include <QtCore/QLibrary>
       
    57 #include <QtCore/QLibraryInfo>
       
    58 #include <QtCore/qdebug.h>
       
    59 #include <QtCore/QMap>
       
    60 #include <QtCore/QSettings>
       
    61 #include <QtCore/QCoreApplication>
       
    62 
       
    63 #include <QtCore/QXmlStreamReader>
       
    64 #include <QtCore/QXmlStreamAttributes>
       
    65 #include <QtCore/QXmlStreamAttribute>
       
    66 
       
    67 static const char *uiElementC = "ui";
       
    68 static const char *languageAttributeC = "language";
       
    69 static const char *widgetElementC = "widget";
       
    70 static const char *displayNameAttributeC = "displayname";
       
    71 static const char *classAttributeC = "class";
       
    72 static const char *customwidgetElementC = "customwidget";
       
    73 static const char *extendsElementC = "extends";
       
    74 static const char *addPageMethodC = "addpagemethod";
       
    75 static const char *propertySpecsC = "propertyspecifications";
       
    76 static const char *stringPropertySpecC = "stringpropertyspecification";
       
    77 static const char *stringPropertyNameAttrC = "name";
       
    78 static const char *stringPropertyTypeAttrC = "type";
       
    79 static const char *stringPropertyNoTrAttrC = "notr";
       
    80 static const char *jambiLanguageC = "jambi";
       
    81 
       
    82 enum { debugPluginManager = 0 };
       
    83 
       
    84 /* Custom widgets: Loading custom widgets is a 2-step process: PluginManager
       
    85  * scans for its plugins in the constructor. At this point, it might not be safe
       
    86  * to immediately initialize the custom widgets it finds, because the rest of
       
    87  * Designer is not initialized yet.
       
    88  * Later on, in ensureInitialized(), the plugin instances (including static ones)
       
    89  * are iterated and the custom widget plugins are initialized and added to internal
       
    90  * list of custom widgets and parsed data. Should there be a parse error or a language
       
    91  * mismatch, it kicks out the respective custom widget. The m_initialized flag
       
    92  * is used to indicate the state.
       
    93  * Later, someone might call registerNewPlugins(), which agains clears the flag via
       
    94  * registerPlugin() and triggers the process again.
       
    95  * Also note that Jambi fakes a custom widget collection that changes its contents
       
    96  * every time the project is switched. So, custom widget plugins can actually
       
    97  * disappear, and the custom widget list must be cleared and refilled in
       
    98  * ensureInitialized() after registerNewPlugins. */
       
    99 
       
   100 QT_BEGIN_NAMESPACE
       
   101 
       
   102 static QStringList unique(const QStringList &lst)
       
   103 {
       
   104     const QSet<QString> s = QSet<QString>::fromList(lst);
       
   105     return s.toList();
       
   106 }
       
   107 
       
   108 QStringList QDesignerPluginManager::defaultPluginPaths()
       
   109 {
       
   110     QStringList result;
       
   111 
       
   112     const QStringList path_list = QCoreApplication::libraryPaths();
       
   113 
       
   114     const QString designer = QLatin1String("designer");
       
   115     foreach (const QString &path, path_list) {
       
   116         QString libPath = path;
       
   117         libPath += QDir::separator();
       
   118         libPath += designer;
       
   119         result.append(libPath);
       
   120     }
       
   121 
       
   122     QString homeLibPath = QDir::homePath();
       
   123     homeLibPath += QDir::separator();
       
   124     homeLibPath += QLatin1String(".designer");
       
   125     homeLibPath += QDir::separator();
       
   126     homeLibPath += QLatin1String("plugins");
       
   127 
       
   128     result.append(homeLibPath);
       
   129     return result;
       
   130 }
       
   131 
       
   132 // Figure out the language designer is running. ToDo: Introduce some
       
   133 // Language name API to QDesignerLanguageExtension?
       
   134 
       
   135 static inline QString getDesignerLanguage(QDesignerFormEditorInterface *core)
       
   136 {
       
   137     if (QDesignerLanguageExtension *lang = qt_extension<QDesignerLanguageExtension *>(core->extensionManager(), core)) {
       
   138         if (lang->uiExtension() == QLatin1String("jui"))
       
   139             return QLatin1String(jambiLanguageC);
       
   140         return QLatin1String("unknown");
       
   141     }
       
   142     return QLatin1String("c++");
       
   143 }
       
   144 
       
   145 // ----------------  QDesignerCustomWidgetSharedData
       
   146 
       
   147 class QDesignerCustomWidgetSharedData : public QSharedData {
       
   148 public:
       
   149     // Type of a string property
       
   150     typedef QPair<qdesigner_internal::TextPropertyValidationMode, bool> StringPropertyType;
       
   151     typedef QHash<QString, StringPropertyType> StringPropertyTypeMap;
       
   152 
       
   153     explicit QDesignerCustomWidgetSharedData(const QString &thePluginPath) : pluginPath(thePluginPath) {}
       
   154     void clearXML();
       
   155 
       
   156     QString pluginPath;
       
   157 
       
   158     QString xmlClassName;
       
   159     QString xmlDisplayName;
       
   160     QString xmlLanguage;
       
   161     QString xmlAddPageMethod;
       
   162     QString xmlExtends;
       
   163 
       
   164     StringPropertyTypeMap xmlStringPropertyTypeMap;
       
   165 };
       
   166 
       
   167 void QDesignerCustomWidgetSharedData::clearXML()
       
   168 {
       
   169     xmlClassName.clear();
       
   170     xmlDisplayName.clear();
       
   171     xmlLanguage.clear();
       
   172     xmlAddPageMethod.clear();
       
   173     xmlExtends.clear();
       
   174     xmlStringPropertyTypeMap.clear();
       
   175 }
       
   176 
       
   177 // ----------------  QDesignerCustomWidgetData
       
   178 
       
   179 QDesignerCustomWidgetData::QDesignerCustomWidgetData(const QString &pluginPath) :
       
   180     m_d(new QDesignerCustomWidgetSharedData(pluginPath))
       
   181 {
       
   182 }
       
   183 
       
   184 QDesignerCustomWidgetData::QDesignerCustomWidgetData(const QDesignerCustomWidgetData &o) :
       
   185      m_d(o.m_d)
       
   186 {
       
   187 }
       
   188 
       
   189 QDesignerCustomWidgetData& QDesignerCustomWidgetData::operator=(const QDesignerCustomWidgetData &o)
       
   190 {
       
   191     m_d.operator=(o.m_d);
       
   192     return *this;
       
   193 }
       
   194 
       
   195 QDesignerCustomWidgetData::~QDesignerCustomWidgetData()
       
   196 {
       
   197 }
       
   198 
       
   199 bool QDesignerCustomWidgetData::isNull() const
       
   200 {
       
   201     return m_d->xmlClassName.isEmpty() || m_d->pluginPath.isEmpty();
       
   202 }
       
   203 
       
   204 QString QDesignerCustomWidgetData::xmlClassName() const
       
   205 {
       
   206     return m_d->xmlClassName;
       
   207 }
       
   208 
       
   209 QString QDesignerCustomWidgetData::xmlLanguage() const
       
   210 {
       
   211     return m_d->xmlLanguage;
       
   212 }
       
   213 
       
   214 QString QDesignerCustomWidgetData::xmlAddPageMethod() const
       
   215 {
       
   216     return m_d->xmlAddPageMethod;
       
   217 }
       
   218 
       
   219 QString QDesignerCustomWidgetData::xmlExtends() const
       
   220 {
       
   221     return m_d->xmlExtends;
       
   222 }
       
   223 
       
   224 QString QDesignerCustomWidgetData::xmlDisplayName() const
       
   225 {
       
   226     return m_d->xmlDisplayName;
       
   227 }
       
   228 
       
   229 QString QDesignerCustomWidgetData::pluginPath() const
       
   230 {
       
   231     return m_d->pluginPath;
       
   232 }
       
   233 
       
   234 bool QDesignerCustomWidgetData::xmlStringPropertyType(const QString &name, StringPropertyType *type) const
       
   235 {
       
   236     QDesignerCustomWidgetSharedData::StringPropertyTypeMap::const_iterator it = m_d->xmlStringPropertyTypeMap.constFind(name);
       
   237     if (it == m_d->xmlStringPropertyTypeMap.constEnd()) {
       
   238         *type = StringPropertyType(qdesigner_internal::ValidationRichText, true);
       
   239         return false;
       
   240     }
       
   241     *type = it.value();
       
   242     return true;
       
   243 }
       
   244 
       
   245 // Wind a QXmlStreamReader  until it finds an element. Returns index or one of FindResult
       
   246 enum FindResult { FindError = -2, ElementNotFound = -1 };
       
   247 
       
   248 static int findElement(const QStringList &desiredElts, QXmlStreamReader &sr)
       
   249 {
       
   250     while (true) {
       
   251         switch(sr.readNext()) {
       
   252         case QXmlStreamReader::EndDocument:
       
   253             return ElementNotFound;
       
   254         case QXmlStreamReader::Invalid:
       
   255             return FindError;
       
   256         case QXmlStreamReader::StartElement: {
       
   257             const int index = desiredElts.indexOf(sr.name().toString().toLower());
       
   258             if (index >= 0)
       
   259                 return index;
       
   260         }
       
   261             break;
       
   262         default:
       
   263             break;
       
   264         }
       
   265     }
       
   266     return FindError;
       
   267 }
       
   268 
       
   269 static inline QString msgXmlError(const QString &name, const QString &errorMessage)
       
   270 {
       
   271     return QDesignerPluginManager::tr("An XML error was encountered when parsing the XML of the custom widget %1: %2").arg(name, errorMessage);
       
   272 }
       
   273 
       
   274 static inline QString msgAttributeMissing(const QString &name)
       
   275 {
       
   276     return QDesignerPluginManager::tr("A required attribute ('%1') is missing.").arg(name);
       
   277 }
       
   278 
       
   279 static qdesigner_internal::TextPropertyValidationMode typeStringToType(const QString &v, bool *ok)
       
   280 {
       
   281     *ok = true;
       
   282     if (v  == QLatin1String("multiline"))
       
   283         return qdesigner_internal::ValidationMultiLine;
       
   284     if (v  == QLatin1String("richtext"))
       
   285         return qdesigner_internal::ValidationRichText;
       
   286     if (v  == QLatin1String("stylesheet"))
       
   287         return qdesigner_internal::ValidationStyleSheet;
       
   288     if (v  == QLatin1String("singleline"))
       
   289         return qdesigner_internal::ValidationSingleLine;
       
   290     if (v  == QLatin1String("objectname"))
       
   291         return qdesigner_internal::ValidationObjectName;
       
   292     if (v  == QLatin1String("objectnamescope"))
       
   293         return qdesigner_internal::ValidationObjectNameScope;
       
   294     if (v  == QLatin1String("url"))
       
   295         return qdesigner_internal::ValidationURL;
       
   296     *ok = false;
       
   297     return qdesigner_internal::ValidationRichText;
       
   298 }
       
   299 
       
   300 static  bool parsePropertySpecs(QXmlStreamReader &sr,
       
   301                                    QDesignerCustomWidgetSharedData::StringPropertyTypeMap *rc,
       
   302                                    QString *errorMessage)
       
   303 {
       
   304     const QString propertySpecs = QLatin1String(propertySpecsC);
       
   305     const QString stringPropertySpec = QLatin1String(stringPropertySpecC);
       
   306     const QString stringPropertyTypeAttr = QLatin1String(stringPropertyTypeAttrC);
       
   307     const QString stringPropertyNoTrAttr = QLatin1String(stringPropertyNoTrAttrC);
       
   308     const QString stringPropertyNameAttr = QLatin1String(stringPropertyNameAttrC);
       
   309 
       
   310     while (!sr.atEnd()) {
       
   311         switch(sr.readNext()) {
       
   312         case QXmlStreamReader::StartElement: {
       
   313             if (sr.name() != stringPropertySpec) {
       
   314                 *errorMessage = QDesignerPluginManager::tr("An invalid property specification ('%1') was encountered. Supported types: %2").arg(sr.name().toString(), stringPropertySpec);
       
   315                 return false;
       
   316             }
       
   317             const QXmlStreamAttributes atts = sr.attributes();
       
   318             const QString name = atts.value(stringPropertyNameAttr).toString();
       
   319             const QString type = atts.value(stringPropertyTypeAttr).toString();
       
   320             const QString notrS = atts.value(stringPropertyNoTrAttr).toString(); //Optional
       
   321 
       
   322             if (type.isEmpty()) {
       
   323                 *errorMessage = msgAttributeMissing(stringPropertyTypeAttr);
       
   324                 return false;
       
   325             }
       
   326             if (name.isEmpty()) {
       
   327                 *errorMessage = msgAttributeMissing(stringPropertyNameAttr);
       
   328                 return false;
       
   329             }
       
   330             bool typeOk;
       
   331             const bool noTr = notrS == QLatin1String("true") || notrS == QLatin1String("1");
       
   332             QDesignerCustomWidgetSharedData::StringPropertyType v(typeStringToType(type, &typeOk), !noTr);
       
   333             if (!typeOk) {
       
   334                 *errorMessage = QDesignerPluginManager::tr("'%1' is not a valid string property specification.").arg(type);
       
   335                 return false;
       
   336             }
       
   337             rc->insert(name, v);
       
   338         }
       
   339             break;
       
   340         case QXmlStreamReader::EndElement: // Outer </stringproperties>
       
   341             if (sr.name() == propertySpecs)
       
   342                 return true;
       
   343         default:
       
   344             break;
       
   345         }
       
   346     }
       
   347     return true;
       
   348 }
       
   349 
       
   350 QDesignerCustomWidgetData::ParseResult
       
   351                        QDesignerCustomWidgetData::parseXml(const QString &xml, const QString &name, QString *errorMessage)
       
   352 {
       
   353     if (debugPluginManager)
       
   354         qDebug() << Q_FUNC_INFO << name;
       
   355 
       
   356     QDesignerCustomWidgetSharedData &data =  *m_d;
       
   357     data.clearXML();
       
   358 
       
   359     QXmlStreamReader sr(xml);
       
   360 
       
   361     bool foundUI = false;
       
   362     bool foundWidget = false;
       
   363     ParseResult rc = ParseOk;
       
   364     // Parse for the (optional) <ui> or the first <widget> element
       
   365     QStringList elements;
       
   366     elements.push_back(QLatin1String(uiElementC));
       
   367     elements.push_back(QLatin1String(widgetElementC));
       
   368     for (int i = 0; i < 2 && !foundWidget; i++) {
       
   369         switch (findElement(elements, sr)) {
       
   370         case FindError:
       
   371             *errorMessage = msgXmlError(name, sr.errorString());
       
   372             return ParseError;
       
   373         case ElementNotFound:
       
   374             *errorMessage = QDesignerPluginManager::tr("The XML of the custom widget %1 does not contain any of the elements <widget> or <ui>.").arg(name);
       
   375             return ParseError;
       
   376         case 0: { // <ui>
       
   377             const QXmlStreamAttributes attributes = sr.attributes();
       
   378             data.xmlLanguage = attributes.value(QLatin1String(languageAttributeC)).toString();
       
   379             data.xmlDisplayName = attributes.value(QLatin1String(displayNameAttributeC)).toString();
       
   380             foundUI = true;
       
   381         }
       
   382             break;
       
   383         case 1: // <widget>: Do some sanity checks
       
   384             data.xmlClassName = sr.attributes().value(QLatin1String(classAttributeC)).toString();
       
   385             if (data.xmlClassName.isEmpty()) {
       
   386                 *errorMessage = QDesignerPluginManager::tr("The class attribute for the class %1 is missing.").arg(name);
       
   387                 rc = ParseWarning;
       
   388             } else {
       
   389                 if (data.xmlClassName != name) {
       
   390                     *errorMessage = QDesignerPluginManager::tr("The class attribute for the class %1 does not match the class name %2.").arg(data.xmlClassName, name);
       
   391                     rc = ParseWarning;
       
   392                 }
       
   393             }
       
   394             foundWidget = true;
       
   395             break;
       
   396         }
       
   397     }
       
   398     // Parse out the <customwidget> element which might be present if  <ui> was there
       
   399     if (!foundUI)
       
   400         return rc;
       
   401     elements.clear();
       
   402     elements.push_back(QLatin1String(customwidgetElementC));
       
   403     switch (findElement(elements, sr)) {
       
   404     case FindError:
       
   405         *errorMessage = msgXmlError(name, sr.errorString());
       
   406         return ParseError;
       
   407     case ElementNotFound:
       
   408         return rc;
       
   409     default:
       
   410         break;
       
   411     }
       
   412     // Find <extends>, <addPageMethod>, <stringproperties>
       
   413     elements.clear();
       
   414     elements.push_back(QLatin1String(extendsElementC));
       
   415     elements.push_back(QLatin1String(addPageMethodC));
       
   416     elements.push_back(QLatin1String(propertySpecsC));
       
   417     while (true) {
       
   418         switch (findElement(elements, sr)) {
       
   419         case FindError:
       
   420             *errorMessage = msgXmlError(name, sr.errorString());
       
   421             return ParseError;
       
   422         case ElementNotFound:
       
   423             return rc;
       
   424         case 0: // <extends>
       
   425             data.xmlExtends = sr.readElementText();
       
   426             if (sr.tokenType() != QXmlStreamReader::EndElement) {
       
   427                 *errorMessage = msgXmlError(name, sr.errorString());
       
   428                 return ParseError;
       
   429             }
       
   430             break;
       
   431         case 1: // <addPageMethod>
       
   432             data.xmlAddPageMethod = sr.readElementText();
       
   433             if (sr.tokenType() != QXmlStreamReader::EndElement) {
       
   434                 *errorMessage = msgXmlError(name, sr.errorString());
       
   435                 return ParseError;
       
   436             }
       
   437             break;
       
   438         case 2: // <stringproperties>
       
   439             if (!parsePropertySpecs(sr, &m_d->xmlStringPropertyTypeMap, errorMessage)) {
       
   440                 *errorMessage = msgXmlError(name, *errorMessage);
       
   441                 return ParseError;
       
   442             }
       
   443             break;
       
   444         }
       
   445     }
       
   446     return rc;
       
   447 }
       
   448 
       
   449 // ---------------- QDesignerPluginManagerPrivate
       
   450 
       
   451 class QDesignerPluginManagerPrivate {
       
   452     public:
       
   453     typedef QPair<QString, QString> ClassNamePropertyNameKey;
       
   454 
       
   455     QDesignerPluginManagerPrivate(QDesignerFormEditorInterface *core);
       
   456 
       
   457     void clearCustomWidgets();
       
   458     bool addCustomWidget(QDesignerCustomWidgetInterface *c,
       
   459                          const QString &pluginPath,
       
   460                          const QString &designerLanguage);
       
   461     void addCustomWidgets(const QObject *o,
       
   462                           const QString &pluginPath,
       
   463                           const QString &designerLanguage);
       
   464 
       
   465     QDesignerFormEditorInterface *m_core;
       
   466     QStringList m_pluginPaths;
       
   467     QStringList m_registeredPlugins;
       
   468     // TODO: QPluginLoader also caches invalid plugins -> This seems to be dead code
       
   469     QStringList m_disabledPlugins;
       
   470 
       
   471     typedef QMap<QString, QString> FailedPluginMap;
       
   472     FailedPluginMap m_failedPlugins;
       
   473 
       
   474     // Synced lists of custom widgets and their data. Note that the list
       
   475     // must be ordered for collections to appear in order.
       
   476     QList<QDesignerCustomWidgetInterface *> m_customWidgets;
       
   477     QList<QDesignerCustomWidgetData> m_customWidgetData;
       
   478 
       
   479     QStringList defaultPluginPaths() const;
       
   480 
       
   481     bool m_initialized;
       
   482 };
       
   483 
       
   484 QDesignerPluginManagerPrivate::QDesignerPluginManagerPrivate(QDesignerFormEditorInterface *core) :
       
   485    m_core(core),
       
   486    m_initialized(false)
       
   487 {
       
   488 }
       
   489 
       
   490 void QDesignerPluginManagerPrivate::clearCustomWidgets()
       
   491 {
       
   492     m_customWidgets.clear();
       
   493     m_customWidgetData.clear();
       
   494 }
       
   495 
       
   496 // Add a custom widget to the list if it parses correctly
       
   497 // and is of the right language
       
   498 bool QDesignerPluginManagerPrivate::addCustomWidget(QDesignerCustomWidgetInterface *c,
       
   499                                                     const QString &pluginPath,
       
   500                                                     const QString &designerLanguage)
       
   501 {
       
   502     if (debugPluginManager)
       
   503         qDebug() << Q_FUNC_INFO << c->name();
       
   504 
       
   505     if (!c->isInitialized())
       
   506         c->initialize(m_core);
       
   507     // Parse the XML even if the plugin is initialized as Jambi might play tricks here
       
   508     QDesignerCustomWidgetData data(pluginPath);
       
   509     const QString domXml = c->domXml();
       
   510     if (!domXml.isEmpty()) { // Legacy: Empty XML means: Do not show up in widget box.
       
   511         QString errorMessage;
       
   512         const QDesignerCustomWidgetData::ParseResult pr = data.parseXml(domXml, c->name(), &errorMessage);
       
   513         switch (pr) {
       
   514             case QDesignerCustomWidgetData::ParseOk:
       
   515             break;
       
   516             case QDesignerCustomWidgetData::ParseWarning:
       
   517             qdesigner_internal::designerWarning(errorMessage);
       
   518             break;
       
   519             case QDesignerCustomWidgetData::ParseError:
       
   520             qdesigner_internal::designerWarning(errorMessage);
       
   521             return false;
       
   522         }
       
   523         // Does the language match?
       
   524         const QString pluginLanguage = data.xmlLanguage();
       
   525         if (!pluginLanguage.isEmpty() && pluginLanguage.compare(designerLanguage, Qt::CaseInsensitive))
       
   526             return false;
       
   527     }
       
   528     m_customWidgets.push_back(c);
       
   529     m_customWidgetData.push_back(data);
       
   530     return true;
       
   531 }
       
   532 
       
   533 // Check the plugin interface for either a custom widget or a collection and
       
   534 // add all contained custom widgets.
       
   535 void QDesignerPluginManagerPrivate::addCustomWidgets(const QObject *o,
       
   536                                                      const QString &pluginPath,
       
   537                                                      const QString &designerLanguage)
       
   538 {
       
   539     if (QDesignerCustomWidgetInterface *c = qobject_cast<QDesignerCustomWidgetInterface*>(o)) {
       
   540         addCustomWidget(c, pluginPath, designerLanguage);
       
   541         return;
       
   542     }
       
   543     if (const QDesignerCustomWidgetCollectionInterface *coll = qobject_cast<QDesignerCustomWidgetCollectionInterface*>(o)) {
       
   544         foreach(QDesignerCustomWidgetInterface *c, coll->customWidgets())
       
   545             addCustomWidget(c, pluginPath, designerLanguage);
       
   546     }
       
   547 }
       
   548 
       
   549 
       
   550 // ---------------- QDesignerPluginManager
       
   551 // As of 4.4, the header will be distributed with the Eclipse plugin.
       
   552 
       
   553 QDesignerPluginManager::QDesignerPluginManager(QDesignerFormEditorInterface *core) :
       
   554     QObject(core),
       
   555     m_d(new QDesignerPluginManagerPrivate(core))
       
   556 {
       
   557     m_d->m_pluginPaths = defaultPluginPaths();
       
   558     const QSettings settings(qApp->organizationName(), QDesignerQSettings::settingsApplicationName());
       
   559     m_d->m_disabledPlugins = unique(settings.value(QLatin1String("PluginManager/DisabledPlugins")).toStringList());
       
   560 
       
   561     // Register plugins
       
   562     updateRegisteredPlugins();
       
   563 
       
   564     if (debugPluginManager)
       
   565         qDebug() << "QDesignerPluginManager::disabled: " <<  m_d->m_disabledPlugins << " static " << m_d->m_customWidgets.size();
       
   566 }
       
   567 
       
   568 QDesignerPluginManager::~QDesignerPluginManager()
       
   569 {
       
   570     syncSettings();
       
   571     delete m_d;
       
   572 }
       
   573 
       
   574 QDesignerFormEditorInterface *QDesignerPluginManager::core() const
       
   575 {
       
   576     return m_d->m_core;
       
   577 }
       
   578 
       
   579 QStringList QDesignerPluginManager::findPlugins(const QString &path)
       
   580 {
       
   581     if (debugPluginManager)
       
   582         qDebug() << Q_FUNC_INFO << path;
       
   583     const QDir dir(path);
       
   584     if (!dir.exists())
       
   585         return QStringList();
       
   586 
       
   587     const QFileInfoList infoList = dir.entryInfoList(QDir::Files);
       
   588     if (infoList.empty())
       
   589         return QStringList();
       
   590 
       
   591     // Load symbolic links but make sure all file names are unique as not
       
   592     // to fall for something like 'libplugin.so.1 -> libplugin.so'
       
   593     QStringList result;
       
   594     const QFileInfoList::const_iterator icend = infoList.constEnd();
       
   595     for (QFileInfoList::const_iterator it = infoList.constBegin(); it != icend; ++it) {
       
   596         QString fileName;
       
   597         if (it->isSymLink()) {
       
   598             const QFileInfo linkTarget = QFileInfo(it->symLinkTarget());
       
   599             if (linkTarget.exists() && linkTarget.isFile())
       
   600                 fileName = linkTarget.absoluteFilePath();
       
   601         } else {
       
   602             fileName = it->absoluteFilePath();
       
   603         }
       
   604         if (!fileName.isEmpty() && QLibrary::isLibrary(fileName) && !result.contains(fileName))
       
   605             result += fileName;
       
   606     }
       
   607     return result;
       
   608 }
       
   609 
       
   610 void QDesignerPluginManager::setDisabledPlugins(const QStringList &disabled_plugins)
       
   611 {
       
   612     m_d->m_disabledPlugins = disabled_plugins;
       
   613     updateRegisteredPlugins();
       
   614 }
       
   615 
       
   616 void QDesignerPluginManager::setPluginPaths(const QStringList &plugin_paths)
       
   617 {
       
   618     m_d->m_pluginPaths = plugin_paths;
       
   619     updateRegisteredPlugins();
       
   620 }
       
   621 
       
   622 QStringList QDesignerPluginManager::disabledPlugins() const
       
   623 {
       
   624     return m_d->m_disabledPlugins;
       
   625 }
       
   626 
       
   627 QStringList QDesignerPluginManager::failedPlugins() const
       
   628 {
       
   629     return m_d->m_failedPlugins.keys();
       
   630 }
       
   631 
       
   632 QString QDesignerPluginManager::failureReason(const QString &pluginName) const
       
   633 {
       
   634     return m_d->m_failedPlugins.value(pluginName);
       
   635 }
       
   636 
       
   637 QStringList QDesignerPluginManager::registeredPlugins() const
       
   638 {
       
   639     return m_d->m_registeredPlugins;
       
   640 }
       
   641 
       
   642 QStringList QDesignerPluginManager::pluginPaths() const
       
   643 {
       
   644     return m_d->m_pluginPaths;
       
   645 }
       
   646 
       
   647 QObject *QDesignerPluginManager::instance(const QString &plugin) const
       
   648 {
       
   649     if (m_d->m_disabledPlugins.contains(plugin))
       
   650         return 0;
       
   651 
       
   652     QPluginLoader loader(plugin);
       
   653     return loader.instance();
       
   654 }
       
   655 
       
   656 void QDesignerPluginManager::updateRegisteredPlugins()
       
   657 {
       
   658     if (debugPluginManager)
       
   659         qDebug() << Q_FUNC_INFO;
       
   660     m_d->m_registeredPlugins.clear();
       
   661     foreach (const QString &path,  m_d->m_pluginPaths)
       
   662         registerPath(path);
       
   663 }
       
   664 
       
   665 bool QDesignerPluginManager::registerNewPlugins()
       
   666 {
       
   667     if (debugPluginManager)
       
   668         qDebug() << Q_FUNC_INFO;
       
   669 
       
   670     const int before = m_d->m_registeredPlugins.size();
       
   671     foreach (const QString &path, m_d->m_pluginPaths)
       
   672         registerPath(path);
       
   673     const bool newPluginsFound = m_d->m_registeredPlugins.size() > before;
       
   674     // We force a re-initialize as Jambi collection might return
       
   675     // different widget lists when switching projects.
       
   676     m_d->m_initialized = false;
       
   677     ensureInitialized();
       
   678 
       
   679     return newPluginsFound;
       
   680 }
       
   681 
       
   682 void QDesignerPluginManager::registerPath(const QString &path)
       
   683 {
       
   684     if (debugPluginManager)
       
   685         qDebug() << Q_FUNC_INFO << path;
       
   686     QStringList candidates = findPlugins(path);
       
   687 
       
   688     foreach (const QString &plugin, candidates)
       
   689         registerPlugin(plugin);
       
   690 }
       
   691 
       
   692 void QDesignerPluginManager::registerPlugin(const QString &plugin)
       
   693 {
       
   694     if (debugPluginManager)
       
   695         qDebug() << Q_FUNC_INFO << plugin;
       
   696     if (m_d->m_disabledPlugins.contains(plugin))
       
   697         return;
       
   698     if (m_d->m_registeredPlugins.contains(plugin))
       
   699         return;
       
   700 
       
   701     QPluginLoader loader(plugin);
       
   702     if (loader.isLoaded() || loader.load()) {
       
   703         m_d->m_registeredPlugins += plugin;
       
   704         QDesignerPluginManagerPrivate::FailedPluginMap::iterator fit = m_d->m_failedPlugins.find(plugin);
       
   705         if (fit != m_d->m_failedPlugins.end())
       
   706             m_d->m_failedPlugins.erase(fit);
       
   707         return;
       
   708     }
       
   709 
       
   710     const QString errorMessage = loader.errorString();
       
   711     m_d->m_failedPlugins.insert(plugin, errorMessage);
       
   712 }
       
   713 
       
   714 
       
   715 
       
   716 bool QDesignerPluginManager::syncSettings()
       
   717 {
       
   718     QSettings settings(qApp->organizationName(), QDesignerQSettings::settingsApplicationName());
       
   719     settings.beginGroup(QLatin1String("PluginManager"));
       
   720     settings.setValue(QLatin1String("DisabledPlugins"), m_d->m_disabledPlugins);
       
   721     settings.endGroup();
       
   722     return settings.status() == QSettings::NoError;
       
   723 }
       
   724 
       
   725 void QDesignerPluginManager::ensureInitialized()
       
   726 {
       
   727     if (debugPluginManager)
       
   728         qDebug() << Q_FUNC_INFO <<  m_d->m_initialized << m_d->m_customWidgets.size();
       
   729 
       
   730     if (m_d->m_initialized)
       
   731         return;
       
   732 
       
   733     const QString designerLanguage = getDesignerLanguage(m_d->m_core);
       
   734 
       
   735     m_d->clearCustomWidgets();
       
   736     // Add the static custom widgets
       
   737     const QObjectList staticPluginObjects = QPluginLoader::staticInstances();
       
   738     if (!staticPluginObjects.empty()) {
       
   739         const QString staticPluginPath = QCoreApplication::applicationFilePath();
       
   740         foreach(QObject *o, staticPluginObjects)
       
   741             m_d->addCustomWidgets(o, staticPluginPath, designerLanguage);
       
   742     }
       
   743     foreach (const QString &plugin, m_d->m_registeredPlugins)
       
   744         if (QObject *o = instance(plugin))
       
   745             m_d->addCustomWidgets(o, plugin, designerLanguage);
       
   746 
       
   747     m_d->m_initialized = true;
       
   748 }
       
   749 
       
   750 QDesignerPluginManager::CustomWidgetList QDesignerPluginManager::registeredCustomWidgets() const
       
   751 {
       
   752     const_cast<QDesignerPluginManager*>(this)->ensureInitialized();
       
   753     return m_d->m_customWidgets;
       
   754 }
       
   755 
       
   756 QDesignerCustomWidgetData QDesignerPluginManager::customWidgetData(QDesignerCustomWidgetInterface *w) const
       
   757 {
       
   758     const int index = m_d->m_customWidgets.indexOf(w);
       
   759     if (index == -1)
       
   760         return QDesignerCustomWidgetData();
       
   761     return m_d->m_customWidgetData.at(index);
       
   762 }
       
   763 
       
   764 QDesignerCustomWidgetData QDesignerPluginManager::customWidgetData(const QString &name) const
       
   765 {
       
   766     const int count = m_d->m_customWidgets.size();
       
   767     for (int i = 0; i < count; i++)
       
   768         if (m_d->m_customWidgets.at(i)->name() == name)
       
   769             return m_d->m_customWidgetData.at(i);
       
   770     return QDesignerCustomWidgetData();
       
   771 }
       
   772 
       
   773 QObjectList QDesignerPluginManager::instances() const
       
   774 {
       
   775     QStringList plugins = registeredPlugins();
       
   776 
       
   777     QObjectList lst;
       
   778     foreach (const QString &plugin, plugins) {
       
   779         if (QObject *o = instance(plugin))
       
   780             lst.append(o);
       
   781     }
       
   782 
       
   783     return lst;
       
   784 }
       
   785 
       
   786 QT_END_NAMESPACE