8#include <QtDesigner/abstractformeditor.h>
9#include <QtDesigner/qextensionmanager.h>
10#include <QtDesigner/abstractlanguage.h>
12#include <QtUiPlugin/customwidget.h>
14#include <QtCore/qdir.h>
15#include <QtCore/qfile.h>
16#include <QtCore/qfileinfo.h>
17#include <QtCore/qset.h>
18#include <QtCore/qpluginloader.h>
19#include <QtCore/qlibrary.h>
20#include <QtCore/qlibraryinfo.h>
21#include <QtCore/qdebug.h>
22#include <QtCore/qmap.h>
23#include <QtCore/qsettings.h>
24#include <QtCore/qcoreapplication.h>
26#include <QtCore/qxmlstream.h>
28using namespace Qt::StringLiterals;
49
50
51
52
53
54
55
56
57
58
59
60
61
62
66static QStringList unique(
const QStringList &lst)
68 const QSet<QString> s(lst.cbegin(), lst.cend());
72QStringList QDesignerPluginManager::defaultPluginPaths()
76 const QStringList path_list = QCoreApplication::libraryPaths();
78 for (
const QString &path : path_list)
79 result.append(path +
"/designer"_L1);
81 result.append(qdesigner_internal::dataDirectory() +
"/plugins"_L1);
90 if (QDesignerLanguageExtension *lang = qt_extension<QDesignerLanguageExtension *>(core->extensionManager(), core)) {
91 if (lang->uiExtension() ==
"jui"_L1)
92 return jambiLanguageC;
122 xmlClassName.clear();
123 xmlDisplayName.clear();
125 xmlAddPageMethod.clear();
127 xmlStringPropertyTypeMap.clear();
132QDesignerCustomWidgetData::QDesignerCustomWidgetData(
const QString &pluginPath) :
133 m_d(
new QDesignerCustomWidgetSharedData(pluginPath))
137QDesignerCustomWidgetData::QDesignerCustomWidgetData(
const QDesignerCustomWidgetData &o) :
142QDesignerCustomWidgetData& QDesignerCustomWidgetData::operator=(
const QDesignerCustomWidgetData &o)
144 m_d.operator=(o.m_d);
148QDesignerCustomWidgetData::~QDesignerCustomWidgetData()
152bool QDesignerCustomWidgetData::isNull()
const
154 return m_d->xmlClassName.isEmpty() || m_d->pluginPath.isEmpty();
157QString QDesignerCustomWidgetData::xmlClassName()
const
159 return m_d->xmlClassName;
162QString QDesignerCustomWidgetData::xmlLanguage()
const
164 return m_d->xmlLanguage;
167QString QDesignerCustomWidgetData::xmlAddPageMethod()
const
169 return m_d->xmlAddPageMethod;
172QString QDesignerCustomWidgetData::xmlExtends()
const
174 return m_d->xmlExtends;
177QString QDesignerCustomWidgetData::xmlDisplayName()
const
179 return m_d->xmlDisplayName;
182QString QDesignerCustomWidgetData::pluginPath()
const
184 return m_d->pluginPath;
187bool QDesignerCustomWidgetData::xmlStringPropertyType(
const QString &name, StringPropertyType *type)
const
189 const auto it = m_d->xmlStringPropertyTypeMap.constFind(name);
190 if (it == m_d->xmlStringPropertyTypeMap.constEnd()) {
191 *type = StringPropertyType(qdesigner_internal::ValidationRichText,
true);
198QString QDesignerCustomWidgetData::propertyToolTip(
const QString &name)
const
200 return m_d->propertyToolTipMap.value(name);
206static int findElement(
const QStringList &desiredElts, QXmlStreamReader &sr)
209 switch(sr.readNext()) {
210 case QXmlStreamReader::EndDocument:
212 case QXmlStreamReader::Invalid:
214 case QXmlStreamReader::StartElement: {
215 const int index = desiredElts.indexOf(sr.name().toString().toLower());
229 return QDesignerPluginManager::tr(
"An XML error was encountered when parsing the XML of the custom widget %1: %2").arg(name, errorMessage);
234 return QDesignerPluginManager::tr(
"A required attribute ('%1') is missing.").arg(name);
240 if (v ==
"multiline"_L1)
242 if (v ==
"richtext"_L1)
244 if (v ==
"stylesheet"_L1)
246 if (v ==
"singleline"_L1)
248 if (v ==
"objectname"_L1)
250 if (v ==
"objectnamescope"_L1)
260 QString *errorMessage)
262 const QString propertySpecs = propertySpecsC;
263 const QString stringPropertySpec = stringPropertySpecC;
264 const QString propertyToolTip = propertyToolTipC;
265 const QString stringPropertyTypeAttr = stringPropertyTypeAttrC;
266 const QString stringPropertyNoTrAttr = stringPropertyNoTrAttrC;
267 const QString stringPropertyNameAttr = stringPropertyNameAttrC;
269 while (!sr.atEnd()) {
270 switch(sr.readNext()) {
271 case QXmlStreamReader::StartElement: {
272 if (sr.name() == stringPropertySpec) {
273 const QXmlStreamAttributes atts = sr.attributes();
274 const QString name = atts.value(stringPropertyNameAttr).toString();
275 const QString type = atts.value(stringPropertyTypeAttr).toString();
276 const QString notrS = atts.value(stringPropertyNoTrAttr).toString();
278 if (type.isEmpty()) {
279 *errorMessage = msgAttributeMissing(stringPropertyTypeAttr);
282 if (name.isEmpty()) {
283 *errorMessage = msgAttributeMissing(stringPropertyNameAttr);
287 const bool noTr = notrS ==
"true"_L1 || notrS ==
"1"_L1;
290 *errorMessage = QDesignerPluginManager::tr(
"'%1' is not a valid string property specification.").arg(type);
293 data->xmlStringPropertyTypeMap.insert(name, v);
294 }
else if (sr.name() == propertyToolTip) {
295 const QString name = sr.attributes().value(stringPropertyNameAttr).toString();
296 if (name.isEmpty()) {
297 *errorMessage = msgAttributeMissing(stringPropertyNameAttr);
300 data->propertyToolTipMap.insert(name, sr.readElementText().trimmed());
302 *errorMessage = QDesignerPluginManager::tr(
"An invalid property specification ('%1') was encountered. Supported types: %2").arg(sr.name().toString(), stringPropertySpec);
307 case QXmlStreamReader::EndElement:
308 if (sr.name() == propertySpecs)
318QDesignerCustomWidgetData::ParseResult
319 QDesignerCustomWidgetData::parseXml(
const QString &xml,
const QString &name, QString *errorMessage)
321 if (debugPluginManager)
322 qDebug() << Q_FUNC_INFO << name;
324 QDesignerCustomWidgetSharedData &data = *m_d;
327 QXmlStreamReader sr(xml);
329 bool foundUI =
false;
330 bool foundWidget =
false;
331 ParseResult rc = ParseOk;
333 QStringList elements;
334 elements.push_back(uiElementC);
335 elements.push_back(widgetElementC);
336 for (
int i = 0; i < 2 && !foundWidget; i++) {
337 switch (findElement(elements, sr)) {
339 *errorMessage = msgXmlError(name, sr.errorString());
341 case ElementNotFound:
342 *errorMessage = QDesignerPluginManager::tr(
"The XML of the custom widget %1 does not contain any of the elements <widget> or <ui>.").arg(name);
345 const QXmlStreamAttributes attributes = sr.attributes();
346 data.xmlLanguage = attributes.value(languageAttributeC).toString();
347 data.xmlDisplayName = attributes.value(displayNameAttributeC).toString();
352 data.xmlClassName = sr.attributes().value(classAttributeC).toString();
353 if (data.xmlClassName.isEmpty()) {
354 *errorMessage = QDesignerPluginManager::tr(
"The class attribute for the class %1 is missing.").arg(name);
357 if (data.xmlClassName != name) {
358 *errorMessage = QDesignerPluginManager::tr(
"The class attribute for the class %1 does not match the class name %2.").arg(data.xmlClassName, name);
370 elements.push_back(customwidgetElementC);
371 switch (findElement(elements, sr)) {
373 *errorMessage = msgXmlError(name, sr.errorString());
375 case ElementNotFound:
381 elements = {extendsElementC, addPageMethodC, propertySpecsC};
383 switch (findElement(elements, sr)) {
385 *errorMessage = msgXmlError(name, sr.errorString());
387 case ElementNotFound:
390 data.xmlExtends = sr.readElementText();
391 if (sr.tokenType() != QXmlStreamReader::EndElement) {
392 *errorMessage = msgXmlError(name, sr.errorString());
397 data.xmlAddPageMethod = sr.readElementText();
398 if (sr.tokenType() != QXmlStreamReader::EndElement) {
399 *errorMessage = msgXmlError(name, sr.errorString());
404 if (!parsePropertySpecs(sr, m_d.data(), errorMessage)) {
405 *errorMessage = msgXmlError(name, *errorMessage);
424 const QString &pluginPath,
425 const QString &designerLanguage);
427 const QString &pluginPath,
428 const QString &designerLanguage);
456 m_customWidgets.clear();
457 m_customWidgetData.clear();
463 const QString &pluginPath,
464 const QString &designerLanguage)
466 if (debugPluginManager)
467 qDebug() << Q_FUNC_INFO << c->name();
469 if (!c->isInitialized())
473 const QString domXml = c->domXml();
474 if (!domXml.isEmpty()) {
475 QString errorMessage;
476 const QDesignerCustomWidgetData::ParseResult pr = data.parseXml(domXml, c->name(), &errorMessage);
478 case QDesignerCustomWidgetData::ParseOk:
480 case QDesignerCustomWidgetData::ParseWarning:
483 case QDesignerCustomWidgetData::ParseError:
488 const QString pluginLanguage = data.xmlLanguage();
489 if (!pluginLanguage.isEmpty() && pluginLanguage.compare(designerLanguage, Qt::CaseInsensitive))
492 m_customWidgets.push_back(c);
493 m_customWidgetData.push_back(data);
500 const QString &pluginPath,
501 const QString &designerLanguage)
504 addCustomWidget(c, pluginPath, designerLanguage);
507 if (QDesignerCustomWidgetCollectionInterface *coll = qobject_cast<QDesignerCustomWidgetCollectionInterface*>(o)) {
508 const auto &collCustomWidgets = coll->customWidgets();
509 for (QDesignerCustomWidgetInterface *c : collCustomWidgets)
510 addCustomWidget(c, pluginPath, designerLanguage);
518QDesignerPluginManager::QDesignerPluginManager(QDesignerFormEditorInterface *core) :
519 QDesignerPluginManager(QStringList{}, core)
523QDesignerPluginManager::QDesignerPluginManager(
const QStringList &pluginPaths,
524 QDesignerFormEditorInterface *core) :
526 m_d(
new QDesignerPluginManagerPrivate(core))
528 m_d->m_pluginPaths = pluginPaths.isEmpty() ? defaultPluginPaths() : pluginPaths;
529 const QSettings settings(qApp->organizationName(), QDesignerQSettings::settingsApplicationName());
530 m_d->m_disabledPlugins = unique(settings.value(
"PluginManager/DisabledPlugins").toStringList());
533 updateRegisteredPlugins();
535 if (debugPluginManager)
536 qDebug() <<
"QDesignerPluginManager::disabled: " << m_d->m_disabledPlugins <<
" static " << m_d->m_customWidgets.size();
539QDesignerPluginManager::~QDesignerPluginManager()
545QDesignerFormEditorInterface *QDesignerPluginManager::core()
const
550QStringList QDesignerPluginManager::findPlugins(
const QString &path)
552 if (debugPluginManager)
553 qDebug() << Q_FUNC_INFO << path;
554 const QDir dir(path);
556 return QStringList();
558 const QFileInfoList infoList = dir.entryInfoList(QDir::Files);
559 if (infoList.isEmpty())
560 return QStringList();
565 for (
const auto &fi : infoList) {
567 if (fi.isSymLink()) {
568 const QFileInfo linkTarget = QFileInfo(fi.symLinkTarget());
569 if (linkTarget.exists() && linkTarget.isFile())
570 fileName = linkTarget.absoluteFilePath();
572 fileName = fi.absoluteFilePath();
574 if (!fileName.isEmpty() && QLibrary::isLibrary(fileName) && !result.contains(fileName))
580void QDesignerPluginManager::setDisabledPlugins(
const QStringList &disabled_plugins)
582 m_d->m_disabledPlugins = disabled_plugins;
583 updateRegisteredPlugins();
586void QDesignerPluginManager::setPluginPaths(
const QStringList &plugin_paths)
588 m_d->m_pluginPaths = plugin_paths;
589 updateRegisteredPlugins();
592QStringList QDesignerPluginManager::disabledPlugins()
const
594 return m_d->m_disabledPlugins;
597QStringList QDesignerPluginManager::failedPlugins()
const
599 return m_d->m_failedPlugins.keys();
602QString QDesignerPluginManager::failureReason(
const QString &pluginName)
const
604 return m_d->m_failedPlugins.value(pluginName);
607QStringList QDesignerPluginManager::registeredPlugins()
const
609 return m_d->m_registeredPlugins;
612QStringList QDesignerPluginManager::pluginPaths()
const
614 return m_d->m_pluginPaths;
617QObject *QDesignerPluginManager::instance(
const QString &plugin)
const
619 if (m_d->m_disabledPlugins.contains(plugin))
622 QPluginLoader loader(plugin);
623 return loader.instance();
626void QDesignerPluginManager::updateRegisteredPlugins()
628 if (debugPluginManager)
629 qDebug() << Q_FUNC_INFO;
630 m_d->m_registeredPlugins.clear();
631 for (
const QString &path : std::as_const(m_d->m_pluginPaths))
635bool QDesignerPluginManager::registerNewPlugins()
637 if (debugPluginManager)
638 qDebug() << Q_FUNC_INFO;
640 const int before = m_d->m_registeredPlugins.size();
641 for (
const QString &path : std::as_const(m_d->m_pluginPaths))
643 const bool newPluginsFound = m_d->m_registeredPlugins.size() > before;
646 m_d->m_initialized =
false;
649 return newPluginsFound;
652void QDesignerPluginManager::registerPath(
const QString &path)
654 if (debugPluginManager)
655 qDebug() << Q_FUNC_INFO << path;
656 const QStringList &candidates = findPlugins(path);
657 for (
const QString &plugin : candidates)
658 registerPlugin(plugin);
661void QDesignerPluginManager::registerPlugin(
const QString &plugin)
663 if (debugPluginManager)
664 qDebug() << Q_FUNC_INFO << plugin;
665 if (m_d->m_disabledPlugins.contains(plugin))
667 if (m_d->m_registeredPlugins.contains(plugin))
670 QPluginLoader loader(plugin);
671 if (loader.isLoaded() || loader.load()) {
672 m_d->m_registeredPlugins += plugin;
673 const auto fit = m_d->m_failedPlugins.find(plugin);
674 if (fit != m_d->m_failedPlugins.end())
675 m_d->m_failedPlugins.erase(fit);
679 const QString errorMessage = loader.errorString();
680 m_d->m_failedPlugins.insert(plugin, errorMessage);
685bool QDesignerPluginManager::syncSettings()
687 QSettings settings(qApp->organizationName(), QDesignerQSettings::settingsApplicationName());
688 settings.beginGroup(
"PluginManager");
689 settings.setValue(
"DisabledPlugins", m_d->m_disabledPlugins);
691 return settings.status() == QSettings::NoError;
694void QDesignerPluginManager::ensureInitialized()
696 if (debugPluginManager)
697 qDebug() << Q_FUNC_INFO << m_d->m_initialized << m_d->m_customWidgets.size();
699 if (m_d->m_initialized)
702 const QString designerLanguage = getDesignerLanguage(m_d->m_core);
704 m_d->clearCustomWidgets();
706 const QObjectList staticPluginObjects = QPluginLoader::staticInstances();
707 if (!staticPluginObjects.isEmpty()) {
708 const QString staticPluginPath = QCoreApplication::applicationFilePath();
709 for (QObject *o : staticPluginObjects)
710 m_d->addCustomWidgets(o, staticPluginPath, designerLanguage);
712 for (
const QString &plugin : std::as_const(m_d->m_registeredPlugins)) {
713 if (QObject *o = instance(plugin))
714 m_d->addCustomWidgets(o, plugin, designerLanguage);
717 m_d->m_initialized =
true;
720QDesignerPluginManager::CustomWidgetList QDesignerPluginManager::registeredCustomWidgets()
const
722 const_cast<QDesignerPluginManager*>(
this)->ensureInitialized();
723 return m_d->m_customWidgets;
726QDesignerCustomWidgetData QDesignerPluginManager::customWidgetData(QDesignerCustomWidgetInterface *w)
const
728 const int index = m_d->m_customWidgets.indexOf(w);
730 return QDesignerCustomWidgetData();
731 return m_d->m_customWidgetData.at(index);
734QDesignerCustomWidgetData QDesignerPluginManager::customWidgetData(
const QString &name)
const
736 for (qsizetype i = 0, count = m_d->m_customWidgets.size(); i < count; ++i)
737 if (m_d->m_customWidgets.at(i)->name() == name)
738 return m_d->m_customWidgetData.at(i);
739 return QDesignerCustomWidgetData();
742QObjectList QDesignerPluginManager::instances()
const
744 const QStringList &plugins = registeredPlugins();
747 for (
const QString &plugin : plugins) {
748 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