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