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

source code of qttools/src/designer/src/lib/shared/pluginmanager.cpp