9#include <QtDesigner/abstractformeditor.h>
10#include <QtDesigner/qextensionmanager.h>
11#include <QtDesigner/abstractlanguage.h>
13#include <QtUiPlugin/customwidget.h>
15#include <QtCore/qdir.h>
16#include <QtCore/qfile.h>
17#include <QtCore/qfileinfo.h>
18#include <QtCore/qset.h>
19#include <QtCore/qpluginloader.h>
20#include <QtCore/qlibrary.h>
21#include <QtCore/qlibraryinfo.h>
22#include <QtCore/qdebug.h>
23#include <QtCore/qmap.h>
24#include <QtCore/qsettings.h>
25#include <QtCore/qcoreapplication.h>
27#include <QtCore/qxmlstream.h>
29using namespace Qt::StringLiterals;
50
51
52
53
54
55
56
57
58
59
60
61
62
63
67static QStringList unique(
const QStringList &lst)
69 const QSet<QString> s(lst.cbegin(), lst.cend());
73QStringList QDesignerPluginManager::defaultPluginPaths()
77 const QStringList path_list = QCoreApplication::libraryPaths();
79 for (
const QString &path : path_list)
80 result.append(path +
"/designer"_L1);
82 result.append(qdesigner_internal::dataDirectory() +
"/plugins"_L1);
91 if (QDesignerLanguageExtension *lang = qt_extension<QDesignerLanguageExtension *>(core->extensionManager(), core)) {
92 if (lang->uiExtension() ==
"jui"_L1)
93 return jambiLanguageC;
123 xmlClassName.clear();
124 xmlDisplayName.clear();
126 xmlAddPageMethod.clear();
128 xmlStringPropertyTypeMap.clear();
133QDesignerCustomWidgetData::QDesignerCustomWidgetData(
const QString &pluginPath) :
134 m_d(
new QDesignerCustomWidgetSharedData(pluginPath))
138QDesignerCustomWidgetData::QDesignerCustomWidgetData(
const QDesignerCustomWidgetData &o) :
143QDesignerCustomWidgetData& QDesignerCustomWidgetData::operator=(
const QDesignerCustomWidgetData &o)
145 m_d.operator=(o.m_d);
149QDesignerCustomWidgetData::~QDesignerCustomWidgetData()
153bool QDesignerCustomWidgetData::isNull()
const
155 return m_d->xmlClassName.isEmpty() || m_d->pluginPath.isEmpty();
158QString QDesignerCustomWidgetData::xmlClassName()
const
160 return m_d->xmlClassName;
163QString QDesignerCustomWidgetData::xmlLanguage()
const
165 return m_d->xmlLanguage;
168QString QDesignerCustomWidgetData::xmlAddPageMethod()
const
170 return m_d->xmlAddPageMethod;
173QString QDesignerCustomWidgetData::xmlExtends()
const
175 return m_d->xmlExtends;
178QString QDesignerCustomWidgetData::xmlDisplayName()
const
180 return m_d->xmlDisplayName;
183QString QDesignerCustomWidgetData::pluginPath()
const
185 return m_d->pluginPath;
188bool QDesignerCustomWidgetData::xmlStringPropertyType(
const QString &name, StringPropertyType *type)
const
190 const auto it = m_d->xmlStringPropertyTypeMap.constFind(name);
191 if (it == m_d->xmlStringPropertyTypeMap.constEnd()) {
192 *type = StringPropertyType(qdesigner_internal::ValidationRichText,
true);
199QString QDesignerCustomWidgetData::propertyToolTip(
const QString &name)
const
201 return m_d->propertyToolTipMap.value(name);
207static int findElement(
const QStringList &desiredElts, QXmlStreamReader &sr)
210 switch(sr.readNext()) {
211 case QXmlStreamReader::EndDocument:
213 case QXmlStreamReader::Invalid:
215 case QXmlStreamReader::StartElement: {
216 const int index = desiredElts.indexOf(sr.name().toString().toLower());
230 return QDesignerPluginManager::tr(
"An XML error was encountered when parsing the XML of the custom widget %1: %2").arg(name, errorMessage);
235 return QDesignerPluginManager::tr(
"A required attribute ('%1') is missing.").arg(name);
241 if (v ==
"multiline"_L1)
243 if (v ==
"richtext"_L1)
245 if (v ==
"stylesheet"_L1)
247 if (v ==
"singleline"_L1)
249 if (v ==
"objectname"_L1)
251 if (v ==
"objectnamescope"_L1)
261 QString *errorMessage)
263 const QString propertySpecs = propertySpecsC;
264 const QString stringPropertySpec = stringPropertySpecC;
265 const QString propertyToolTip = propertyToolTipC;
266 const QString stringPropertyTypeAttr = stringPropertyTypeAttrC;
267 const QString stringPropertyNoTrAttr = stringPropertyNoTrAttrC;
268 const QString stringPropertyNameAttr = stringPropertyNameAttrC;
270 while (!sr.atEnd()) {
271 switch(sr.readNext()) {
272 case QXmlStreamReader::StartElement: {
273 if (sr.name() == stringPropertySpec) {
274 const QXmlStreamAttributes atts = sr.attributes();
275 const QString name = atts.value(stringPropertyNameAttr).toString();
276 const QString type = atts.value(stringPropertyTypeAttr).toString();
277 const QString notrS = atts.value(stringPropertyNoTrAttr).toString();
279 if (type.isEmpty()) {
280 *errorMessage = msgAttributeMissing(stringPropertyTypeAttr);
283 if (name.isEmpty()) {
284 *errorMessage = msgAttributeMissing(stringPropertyNameAttr);
288 const bool noTr = notrS ==
"true"_L1 || notrS ==
"1"_L1;
291 *errorMessage = QDesignerPluginManager::tr(
"'%1' is not a valid string property specification.").arg(type);
294 data->xmlStringPropertyTypeMap.insert(name, v);
295 }
else if (sr.name() == propertyToolTip) {
296 const QString name = sr.attributes().value(stringPropertyNameAttr).toString();
297 if (name.isEmpty()) {
298 *errorMessage = msgAttributeMissing(stringPropertyNameAttr);
301 data->propertyToolTipMap.insert(name, sr.readElementText().trimmed());
303 *errorMessage = QDesignerPluginManager::tr(
"An invalid property specification ('%1') was encountered. Supported types: %2").arg(sr.name().toString(), stringPropertySpec);
308 case QXmlStreamReader::EndElement:
309 if (sr.name() == propertySpecs)
319QDesignerCustomWidgetData::ParseResult
320 QDesignerCustomWidgetData::parseXml(
const QString &xml,
const QString &name, QString *errorMessage)
322 if (debugPluginManager)
323 qDebug() << Q_FUNC_INFO << name;
325 QDesignerCustomWidgetSharedData &data = *m_d;
328 QXmlStreamReader sr(xml);
330 bool foundUI =
false;
331 bool foundWidget =
false;
332 ParseResult rc = ParseOk;
334 QStringList elements;
335 elements.push_back(uiElementC);
336 elements.push_back(widgetElementC);
337 for (
int i = 0; i < 2 && !foundWidget; i++) {
338 switch (findElement(elements, sr)) {
340 *errorMessage = msgXmlError(name, sr.errorString());
342 case ElementNotFound:
343 *errorMessage = QDesignerPluginManager::tr(
"The XML of the custom widget %1 does not contain any of the elements <widget> or <ui>.").arg(name);
346 const QXmlStreamAttributes attributes = sr.attributes();
347 data.xmlLanguage = attributes.value(languageAttributeC).toString();
348 data.xmlDisplayName = attributes.value(displayNameAttributeC).toString();
353 data.xmlClassName = sr.attributes().value(classAttributeC).toString();
354 if (data.xmlClassName.isEmpty()) {
355 *errorMessage = QDesignerPluginManager::tr(
"The class attribute for the class %1 is missing.").arg(name);
358 if (data.xmlClassName != name) {
359 *errorMessage = QDesignerPluginManager::tr(
"The class attribute for the class %1 does not match the class name %2.").arg(data.xmlClassName, name);
371 elements.push_back(customwidgetElementC);
372 switch (findElement(elements, sr)) {
374 *errorMessage = msgXmlError(name, sr.errorString());
376 case ElementNotFound:
382 elements = {extendsElementC, addPageMethodC, propertySpecsC};
384 switch (findElement(elements, sr)) {
386 *errorMessage = msgXmlError(name, sr.errorString());
388 case ElementNotFound:
391 data.xmlExtends = sr.readElementText();
392 if (sr.tokenType() != QXmlStreamReader::EndElement) {
393 *errorMessage = msgXmlError(name, sr.errorString());
398 data.xmlAddPageMethod = sr.readElementText();
399 if (sr.tokenType() != QXmlStreamReader::EndElement) {
400 *errorMessage = msgXmlError(name, sr.errorString());
405 if (!parsePropertySpecs(sr, m_d.data(), errorMessage)) {
406 *errorMessage = msgXmlError(name, *errorMessage);
425 const QString &pluginPath,
426 const QString &designerLanguage);
428 const QString &pluginPath,
429 const QString &designerLanguage);
457 m_customWidgets.clear();
458 m_customWidgetData.clear();
464 const QString &pluginPath,
465 const QString &designerLanguage)
467 if (debugPluginManager)
468 qDebug() << Q_FUNC_INFO << c->name();
470 if (!c->isInitialized())
474 const QString domXml = c->domXml();
475 if (!domXml.isEmpty()) {
476 QString errorMessage;
477 const QDesignerCustomWidgetData::ParseResult pr = data.parseXml(domXml, c->name(), &errorMessage);
479 case QDesignerCustomWidgetData::ParseOk:
481 case QDesignerCustomWidgetData::ParseWarning:
484 case QDesignerCustomWidgetData::ParseError:
489 const QString pluginLanguage = data.xmlLanguage();
490 if (!pluginLanguage.isEmpty() && pluginLanguage.compare(designerLanguage, Qt::CaseInsensitive))
493 m_customWidgets.push_back(c);
494 m_customWidgetData.push_back(data);
501 const QString &pluginPath,
502 const QString &designerLanguage)
505 addCustomWidget(c, pluginPath, designerLanguage);
508 if (QDesignerCustomWidgetCollectionInterface *coll = qobject_cast<QDesignerCustomWidgetCollectionInterface*>(o)) {
509 const auto &collCustomWidgets = coll->customWidgets();
510 for (QDesignerCustomWidgetInterface *c : collCustomWidgets)
511 addCustomWidget(c, pluginPath, designerLanguage);
519QDesignerPluginManager::QDesignerPluginManager(QDesignerFormEditorInterface *core) :
520 QDesignerPluginManager(QStringList{}, core)
524QDesignerPluginManager::QDesignerPluginManager(
const QStringList &pluginPaths,
525 QDesignerFormEditorInterface *core) :
527 m_d(
new QDesignerPluginManagerPrivate(core))
529 m_d->m_pluginPaths = pluginPaths.isEmpty() ? defaultPluginPaths() : pluginPaths;
530 const QSettings settings(qApp->organizationName(), QDesignerQSettings::settingsApplicationName());
531 m_d->m_disabledPlugins = unique(settings.value(
"PluginManager/DisabledPlugins").toStringList());
534 updateRegisteredPlugins();
536 if (debugPluginManager)
537 qDebug() <<
"QDesignerPluginManager::disabled: " << m_d->m_disabledPlugins <<
" static " << m_d->m_customWidgets.size();
540QDesignerPluginManager::~QDesignerPluginManager()
546QDesignerFormEditorInterface *QDesignerPluginManager::core()
const
551QStringList QDesignerPluginManager::findPlugins(
const QString &path)
553 if (debugPluginManager)
554 qDebug() << Q_FUNC_INFO << path;
555 const QDir dir(path);
557 return QStringList();
559 const QFileInfoList infoList = dir.entryInfoList(QDir::Files);
560 if (infoList.isEmpty())
561 return QStringList();
566 for (
const auto &fi : infoList) {
568 if (fi.isSymLink()) {
569 const QFileInfo linkTarget = QFileInfo(fi.symLinkTarget());
570 if (linkTarget.exists() && linkTarget.isFile())
571 fileName = linkTarget.absoluteFilePath();
573 fileName = fi.absoluteFilePath();
575 if (!fileName.isEmpty() && QLibrary::isLibrary(fileName) && !result.contains(fileName))
581void QDesignerPluginManager::setDisabledPlugins(
const QStringList &disabled_plugins)
583 m_d->m_disabledPlugins = disabled_plugins;
584 updateRegisteredPlugins();
587void QDesignerPluginManager::setPluginPaths(
const QStringList &plugin_paths)
589 m_d->m_pluginPaths = plugin_paths;
590 updateRegisteredPlugins();
593QStringList QDesignerPluginManager::disabledPlugins()
const
595 return m_d->m_disabledPlugins;
598QStringList QDesignerPluginManager::failedPlugins()
const
600 return m_d->m_failedPlugins.keys();
603QString QDesignerPluginManager::failureReason(
const QString &pluginName)
const
605 return m_d->m_failedPlugins.value(pluginName);
608QStringList QDesignerPluginManager::registeredPlugins()
const
610 return m_d->m_registeredPlugins;
613QStringList QDesignerPluginManager::pluginPaths()
const
615 return m_d->m_pluginPaths;
618QObject *QDesignerPluginManager::instance(
const QString &plugin)
const
620 if (m_d->m_disabledPlugins.contains(plugin))
623 QPluginLoader loader(plugin);
624 return loader.instance();
627void QDesignerPluginManager::updateRegisteredPlugins()
629 if (debugPluginManager)
630 qDebug() << Q_FUNC_INFO;
631 m_d->m_registeredPlugins.clear();
632 for (
const QString &path : std::as_const(m_d->m_pluginPaths))
636bool QDesignerPluginManager::registerNewPlugins()
638 if (debugPluginManager)
639 qDebug() << Q_FUNC_INFO;
641 const int before = m_d->m_registeredPlugins.size();
642 for (
const QString &path : std::as_const(m_d->m_pluginPaths))
644 const bool newPluginsFound = m_d->m_registeredPlugins.size() > before;
647 m_d->m_initialized =
false;
650 return newPluginsFound;
653void QDesignerPluginManager::registerPath(
const QString &path)
655 if (debugPluginManager)
656 qDebug() << Q_FUNC_INFO << path;
657 const QStringList &candidates = findPlugins(path);
658 for (
const QString &plugin : candidates)
659 registerPlugin(plugin);
662void QDesignerPluginManager::registerPlugin(
const QString &plugin)
664 if (debugPluginManager)
665 qDebug() << Q_FUNC_INFO << plugin;
666 if (m_d->m_disabledPlugins.contains(plugin))
668 if (m_d->m_registeredPlugins.contains(plugin))
671 QPluginLoader loader(plugin);
672 if (loader.isLoaded() || loader.load()) {
673 m_d->m_registeredPlugins += plugin;
674 const auto fit = m_d->m_failedPlugins.find(plugin);
675 if (fit != m_d->m_failedPlugins.end())
676 m_d->m_failedPlugins.erase(fit);
680 const QString errorMessage = loader.errorString();
681 m_d->m_failedPlugins.insert(plugin, errorMessage);
686bool QDesignerPluginManager::syncSettings()
688 QSettings settings(qApp->organizationName(), QDesignerQSettings::settingsApplicationName());
689 settings.beginGroup(
"PluginManager");
690 settings.setValue(
"DisabledPlugins", m_d->m_disabledPlugins);
692 return settings.status() == QSettings::NoError;
695void QDesignerPluginManager::ensureInitialized()
697 if (debugPluginManager)
698 qDebug() << Q_FUNC_INFO << m_d->m_initialized << m_d->m_customWidgets.size();
700 if (m_d->m_initialized)
703 const QString designerLanguage = getDesignerLanguage(m_d->m_core);
705 m_d->clearCustomWidgets();
707 const QObjectList staticPluginObjects = QPluginLoader::staticInstances();
708 if (!staticPluginObjects.isEmpty()) {
709 const QString staticPluginPath = QCoreApplication::applicationFilePath();
710 for (QObject *o : staticPluginObjects)
711 m_d->addCustomWidgets(o, staticPluginPath, designerLanguage);
713 for (
const QString &plugin : std::as_const(m_d->m_registeredPlugins)) {
714 if (QObject *o = instance(plugin))
715 m_d->addCustomWidgets(o, plugin, designerLanguage);
718 m_d->m_initialized =
true;
721QDesignerPluginManager::CustomWidgetList QDesignerPluginManager::registeredCustomWidgets()
const
723 const_cast<QDesignerPluginManager*>(
this)->ensureInitialized();
724 return m_d->m_customWidgets;
727QDesignerCustomWidgetData QDesignerPluginManager::customWidgetData(QDesignerCustomWidgetInterface *w)
const
729 const int index = m_d->m_customWidgets.indexOf(w);
731 return QDesignerCustomWidgetData();
732 return m_d->m_customWidgetData.at(index);
735QDesignerCustomWidgetData QDesignerPluginManager::customWidgetData(
const QString &name)
const
737 for (qsizetype i = 0, count = m_d->m_customWidgets.size(); i < count; ++i)
738 if (m_d->m_customWidgets.at(i)->name() == name)
739 return m_d->m_customWidgetData.at(i);
740 return QDesignerCustomWidgetData();
743QObjectList QDesignerPluginManager::instances()
const
745 const QStringList &plugins = registeredPlugins();
748 for (
const QString &plugin : plugins) {
749 if (QObject *o = instance(plugin))
QStringList m_registeredPlugins
QDesignerFormEditorInterface * m_core
QStringList m_disabledPlugins
bool addCustomWidget(QDesignerCustomWidgetInterface *c, const QString &pluginPath, const QString &designerLanguage)
QList< QDesignerCustomWidgetInterface * > m_customWidgets
QMap< QString, QString > m_failedPlugins
QList< QDesignerCustomWidgetData > m_customWidgetData
QStringList m_pluginPaths
QDesignerPluginManagerPrivate(QDesignerFormEditorInterface *core)
void clearCustomWidgets()
void addCustomWidgets(QObject *o, const QString &pluginPath, const QString &designerLanguage)
QStringList defaultPluginPaths() const
Auxiliary methods to store/retrieve settings.
TextPropertyValidationMode
@ ValidationObjectNameScope
static qdesigner_internal::TextPropertyValidationMode typeStringToType(const QString &v, bool *ok)
static constexpr auto uiElementC
static constexpr auto stringPropertyNoTrAttrC
static constexpr auto propertySpecsC
static QString msgXmlError(const QString &name, const QString &errorMessage)
static constexpr auto displayNameAttributeC
static bool parsePropertySpecs(QXmlStreamReader &sr, QDesignerCustomWidgetSharedData *data, QString *errorMessage)
static constexpr auto propertyToolTipC
static constexpr auto addPageMethodC
static QString getDesignerLanguage(QDesignerFormEditorInterface *core)
static int findElement(const QStringList &desiredElts, QXmlStreamReader &sr)
static constexpr auto classAttributeC
static constexpr auto customwidgetElementC
static constexpr auto stringPropertyNameAttrC
static constexpr auto stringPropertySpecC
static constexpr auto widgetElementC
static QString msgAttributeMissing(const QString &name)
static constexpr auto stringPropertyTypeAttrC
static constexpr auto extendsElementC
static constexpr auto languageAttributeC
static constexpr auto jambiLanguageC