1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "qdesigner_widgetbox_p.h"
5#include "qdesigner_utils_p.h"
6
7#include <QtDesigner/private/ui4_p.h>
8
9#include <QtCore/qregularexpression.h>
10#include <QtCore/qdebug.h>
11#include <QtCore/qxmlstream.h>
12#include <QtCore/qshareddata.h>
13
14QT_BEGIN_NAMESPACE
15
16using namespace Qt::StringLiterals;
17
18class QDesignerWidgetBoxWidgetData : public QSharedData
19{
20public:
21 QDesignerWidgetBoxWidgetData(const QString &aname, const QString &xml,
22 const QString &icon_name,
23 QDesignerWidgetBoxInterface::Widget::Type atype);
24 QString m_name;
25 QString m_xml;
26 QString m_icon_name;
27 QDesignerWidgetBoxInterface::Widget::Type m_type;
28};
29
30QDesignerWidgetBoxWidgetData::QDesignerWidgetBoxWidgetData(const QString &aname,
31 const QString &xml,
32 const QString &icon_name,
33 QDesignerWidgetBoxInterface::Widget::Type atype) :
34 m_name(aname), m_xml(xml), m_icon_name(icon_name), m_type(atype)
35{
36}
37
38QDesignerWidgetBoxInterface::Widget::Widget(const QString &aname, const QString &xml,
39 const QString &icon_name, Type atype) :
40 m_data(new QDesignerWidgetBoxWidgetData(aname, xml, icon_name, atype))
41{
42}
43
44QDesignerWidgetBoxInterface::Widget::~Widget() = default;
45
46QDesignerWidgetBoxInterface::Widget::Widget(const Widget &w) :
47 m_data(w.m_data)
48{
49}
50
51QDesignerWidgetBoxInterface::Widget &QDesignerWidgetBoxInterface::Widget::operator=(const Widget &rhs)
52{
53 if (this != &rhs) {
54 m_data = rhs.m_data;
55 }
56 return *this;
57}
58
59QString QDesignerWidgetBoxInterface::Widget::name() const
60{
61 return m_data->m_name;
62}
63
64void QDesignerWidgetBoxInterface::Widget::setName(const QString &aname)
65{
66 if (m_data->m_name != aname)
67 m_data->m_name = aname;
68}
69
70QString QDesignerWidgetBoxInterface::Widget::domXml() const
71{
72 return m_data->m_xml;
73}
74
75void QDesignerWidgetBoxInterface::Widget::setDomXml(const QString &xml)
76{
77 if (m_data->m_xml != xml)
78 m_data->m_xml = xml;
79}
80
81QString QDesignerWidgetBoxInterface::Widget::iconName() const
82{
83 return m_data->m_icon_name;
84}
85
86void QDesignerWidgetBoxInterface::Widget::setIconName(const QString &icon_name)
87{
88 if (m_data->m_icon_name != icon_name)
89 m_data->m_icon_name = icon_name;
90}
91
92QDesignerWidgetBoxInterface::Widget::Type QDesignerWidgetBoxInterface::Widget::type() const
93{
94 return m_data->m_type;
95}
96
97void QDesignerWidgetBoxInterface::Widget::setType(Type atype)
98{
99 if (m_data->m_type != atype)
100 m_data->m_type = atype;
101}
102
103bool QDesignerWidgetBoxInterface::Widget::isNull() const
104{
105 return m_data->m_name.isEmpty();
106}
107
108namespace qdesigner_internal {
109QDesignerWidgetBox::QDesignerWidgetBox(QWidget *parent, Qt::WindowFlags flags)
110 : QDesignerWidgetBoxInterface(parent, flags)
111{
112
113}
114
115QDesignerWidgetBox::LoadMode QDesignerWidgetBox::loadMode() const
116{
117 return m_loadMode;
118}
119
120void QDesignerWidgetBox::setLoadMode(LoadMode lm)
121{
122 m_loadMode = lm;
123}
124
125// Convenience to find a widget by class name
126bool QDesignerWidgetBox::findWidget(const QDesignerWidgetBoxInterface *wbox,
127 const QString &className,
128 const QString &category,
129 Widget *widgetData)
130{
131 // Note that entry names do not necessarily match the class name
132 // (at least, not for the standard widgets), so,
133 // look in the XML for the class name of the first widget to appear
134 QString pattern = QStringLiteral("^<widget\\s+class\\s*=\\s*\"");
135 pattern += className;
136 pattern += QStringLiteral("\".*$");
137 const QRegularExpression regexp(pattern);
138 Q_ASSERT(regexp.isValid());
139 const int catCount = wbox->categoryCount();
140 for (int c = 0; c < catCount; c++) {
141 const Category cat = wbox->category(cat_idx: c);
142 if (category.isEmpty() || cat.name() == category) {
143 const int widgetCount = cat.widgetCount();
144 for (int w = 0; w < widgetCount; w++) {
145 const Widget widget = cat.widget(idx: w);
146 QString xml = widget.domXml(); // Erase the <ui> tag that can be present starting from 4.4
147 const auto widgetTagIndex = xml.indexOf(s: "<widget"_L1);
148 if (widgetTagIndex != -1) {
149 xml.remove(i: 0, len: widgetTagIndex);
150 if (regexp.match(subject: xml).hasMatch()) {
151 *widgetData = widget;
152 return true;
153 }
154 }
155 }
156 }
157 }
158 return false;
159}
160
161// Convenience to create a Dom Widget from widget box xml code.
162DomUI *QDesignerWidgetBox::xmlToUi(const QString &name, const QString &xml, bool insertFakeTopLevel,
163 QString *errorMessage)
164{
165 QXmlStreamReader reader(xml);
166 DomUI *ui = nullptr;
167
168 // The xml description must either contain a root element "ui" with a child element "widget"
169 // or "widget" as the root element (4.3 legacy)
170
171 while (!reader.atEnd()) {
172 if (reader.readNext() == QXmlStreamReader::StartElement) {
173 const auto name = reader.name();
174 if (ui) {
175 reader.raiseError(message: tr(s: "Unexpected element <%1>").arg(a: name.toString()));
176 continue;
177 }
178
179 if (name.compare(s: "widget"_L1, cs: Qt::CaseInsensitive) == 0) { // 4.3 legacy, wrap into DomUI
180 ui = new DomUI;
181 DomWidget *widget = new DomWidget;
182 widget->read(reader);
183 ui->setElementWidget(widget);
184 } else if (name.compare(s: "ui"_L1, cs: Qt::CaseInsensitive) == 0) { // 4.4
185 ui = new DomUI;
186 ui->read(reader);
187 } else {
188 reader.raiseError(message: tr(s: "Unexpected element <%1>").arg(a: name.toString()));
189 }
190 }
191 }
192
193 if (reader.hasError()) {
194 delete ui;
195 *errorMessage = tr(s: "A parse error occurred at line %1, column %2 of the XML code "
196 "specified for the widget %3: %4\n%5")
197 .arg(a: reader.lineNumber()).arg(a: reader.columnNumber())
198 .arg(args: name, args: reader.errorString(), args: xml);
199 return nullptr;
200 }
201
202 if (!ui || !ui->elementWidget()) {
203 delete ui;
204 *errorMessage = tr(s: "The XML code specified for the widget %1 does not contain "
205 "any widget elements.\n%2").arg(args: name, args: xml);
206 return nullptr;
207 }
208
209 if (insertFakeTopLevel) {
210 DomWidget *fakeTopLevel = new DomWidget;
211 fakeTopLevel->setAttributeClass(u"QWidget"_s);
212 QList<DomWidget *> children;
213 children.push_back(t: ui->takeElementWidget());
214 fakeTopLevel->setElementWidget(children);
215 ui->setElementWidget(fakeTopLevel);
216 }
217
218 return ui;
219}
220
221// Convenience to create a Dom Widget from widget box xml code.
222DomUI *QDesignerWidgetBox::xmlToUi(const QString &name, const QString &xml, bool insertFakeTopLevel)
223{
224 QString errorMessage;
225 DomUI *rc = xmlToUi(name, xml, insertFakeTopLevel, errorMessage: &errorMessage);
226 if (!rc)
227 qdesigner_internal::designerWarning(message: errorMessage);
228 return rc;
229}
230
231} // namespace qdesigner_internal
232
233QT_END_NAMESPACE
234

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