1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** All rights reserved.
|
---|
5 | ** Contact: Nokia Corporation (qt-info@nokia.com)
|
---|
6 | **
|
---|
7 | ** This file is part of the Qt Designer of the Qt Toolkit.
|
---|
8 | **
|
---|
9 | ** $QT_BEGIN_LICENSE:LGPL$
|
---|
10 | ** Commercial Usage
|
---|
11 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
12 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
13 | ** Software or, alternatively, in accordance with the terms contained in
|
---|
14 | ** a written agreement between you and Nokia.
|
---|
15 | **
|
---|
16 | ** GNU Lesser General Public License Usage
|
---|
17 | ** Alternatively, this file may be used under the terms of the GNU Lesser
|
---|
18 | ** General Public License version 2.1 as published by the Free Software
|
---|
19 | ** Foundation and appearing in the file LICENSE.LGPL included in the
|
---|
20 | ** packaging of this file. Please review the following information to
|
---|
21 | ** ensure the GNU Lesser General Public License version 2.1 requirements
|
---|
22 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
---|
23 | **
|
---|
24 | ** In addition, as a special exception, Nokia gives you certain additional
|
---|
25 | ** rights. These rights are described in the Nokia Qt LGPL Exception
|
---|
26 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
---|
27 | **
|
---|
28 | ** GNU General Public License Usage
|
---|
29 | ** Alternatively, this file may be used under the terms of the GNU
|
---|
30 | ** General Public License version 3.0 as published by the Free Software
|
---|
31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
32 | ** packaging of this file. Please review the following information to
|
---|
33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
---|
34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
---|
35 | **
|
---|
36 | ** If you have questions regarding the use of this file, please contact
|
---|
37 | ** Nokia at qt-info@nokia.com.
|
---|
38 | ** $QT_END_LICENSE$
|
---|
39 | **
|
---|
40 | ****************************************************************************/
|
---|
41 |
|
---|
42 | #include "widgetdatabase_p.h"
|
---|
43 | #include "widgetfactory_p.h"
|
---|
44 | #include "spacer_widget_p.h"
|
---|
45 | #include "abstractlanguage.h"
|
---|
46 | #include "pluginmanager_p.h"
|
---|
47 | #include "qdesigner_widgetbox_p.h"
|
---|
48 | #include "qdesigner_utils_p.h"
|
---|
49 | #include <ui4_p.h>
|
---|
50 |
|
---|
51 | #include <QtDesigner/customwidget.h>
|
---|
52 | #include <QtDesigner/propertysheet.h>
|
---|
53 | #include <QtDesigner/QExtensionManager>
|
---|
54 | #include <QtDesigner/QDesignerFormEditorInterface>
|
---|
55 |
|
---|
56 | #include <QtXml/QXmlStreamWriter>
|
---|
57 | #include <QtCore/QtAlgorithms>
|
---|
58 | #include <QtCore/qdebug.h>
|
---|
59 | #include <QtCore/QMetaProperty>
|
---|
60 | #include <QtCore/QTextStream>
|
---|
61 | #include <QtCore/QRegExp>
|
---|
62 | #include <QtCore/QCoreApplication>
|
---|
63 |
|
---|
64 | QT_BEGIN_NAMESPACE
|
---|
65 |
|
---|
66 | namespace {
|
---|
67 | enum { debugWidgetDataBase = 0 };
|
---|
68 | }
|
---|
69 |
|
---|
70 | namespace qdesigner_internal {
|
---|
71 |
|
---|
72 | // ----------------------------------------------------------
|
---|
73 | WidgetDataBaseItem::WidgetDataBaseItem(const QString &name, const QString &group)
|
---|
74 | : m_name(name),
|
---|
75 | m_group(group),
|
---|
76 | m_compat(0),
|
---|
77 | m_container(0),
|
---|
78 | m_form(0),
|
---|
79 | m_custom(0),
|
---|
80 | m_promoted(0)
|
---|
81 | {
|
---|
82 | }
|
---|
83 |
|
---|
84 | QString WidgetDataBaseItem::name() const
|
---|
85 | {
|
---|
86 | return m_name;
|
---|
87 | }
|
---|
88 |
|
---|
89 | void WidgetDataBaseItem::setName(const QString &name)
|
---|
90 | {
|
---|
91 | m_name = name;
|
---|
92 | }
|
---|
93 |
|
---|
94 | QString WidgetDataBaseItem::group() const
|
---|
95 | {
|
---|
96 | return m_group;
|
---|
97 | }
|
---|
98 |
|
---|
99 | void WidgetDataBaseItem::setGroup(const QString &group)
|
---|
100 | {
|
---|
101 | m_group = group;
|
---|
102 | }
|
---|
103 |
|
---|
104 | QString WidgetDataBaseItem::toolTip() const
|
---|
105 | {
|
---|
106 | return m_toolTip;
|
---|
107 | }
|
---|
108 |
|
---|
109 | void WidgetDataBaseItem::setToolTip(const QString &toolTip)
|
---|
110 | {
|
---|
111 | m_toolTip = toolTip;
|
---|
112 | }
|
---|
113 |
|
---|
114 | QString WidgetDataBaseItem::whatsThis() const
|
---|
115 | {
|
---|
116 | return m_whatsThis;
|
---|
117 | }
|
---|
118 |
|
---|
119 | void WidgetDataBaseItem::setWhatsThis(const QString &whatsThis)
|
---|
120 | {
|
---|
121 | m_whatsThis = whatsThis;
|
---|
122 | }
|
---|
123 |
|
---|
124 | QString WidgetDataBaseItem::includeFile() const
|
---|
125 | {
|
---|
126 | return m_includeFile;
|
---|
127 | }
|
---|
128 |
|
---|
129 | void WidgetDataBaseItem::setIncludeFile(const QString &includeFile)
|
---|
130 | {
|
---|
131 | m_includeFile = includeFile;
|
---|
132 | }
|
---|
133 |
|
---|
134 | QIcon WidgetDataBaseItem::icon() const
|
---|
135 | {
|
---|
136 | return m_icon;
|
---|
137 | }
|
---|
138 |
|
---|
139 | void WidgetDataBaseItem::setIcon(const QIcon &icon)
|
---|
140 | {
|
---|
141 | m_icon = icon;
|
---|
142 | }
|
---|
143 |
|
---|
144 | bool WidgetDataBaseItem::isCompat() const
|
---|
145 | {
|
---|
146 | return m_compat;
|
---|
147 | }
|
---|
148 |
|
---|
149 | void WidgetDataBaseItem::setCompat(bool b)
|
---|
150 | {
|
---|
151 | m_compat = b;
|
---|
152 | }
|
---|
153 |
|
---|
154 | bool WidgetDataBaseItem::isContainer() const
|
---|
155 | {
|
---|
156 | return m_container;
|
---|
157 | }
|
---|
158 |
|
---|
159 | void WidgetDataBaseItem::setContainer(bool b)
|
---|
160 | {
|
---|
161 | m_container = b;
|
---|
162 | }
|
---|
163 |
|
---|
164 | bool WidgetDataBaseItem::isCustom() const
|
---|
165 | {
|
---|
166 | return m_custom;
|
---|
167 | }
|
---|
168 |
|
---|
169 | void WidgetDataBaseItem::setCustom(bool b)
|
---|
170 | {
|
---|
171 | m_custom = b;
|
---|
172 | }
|
---|
173 |
|
---|
174 | QString WidgetDataBaseItem::pluginPath() const
|
---|
175 | {
|
---|
176 | return m_pluginPath;
|
---|
177 | }
|
---|
178 |
|
---|
179 | void WidgetDataBaseItem::setPluginPath(const QString &path)
|
---|
180 | {
|
---|
181 | m_pluginPath = path;
|
---|
182 | }
|
---|
183 |
|
---|
184 | bool WidgetDataBaseItem::isPromoted() const
|
---|
185 | {
|
---|
186 | return m_promoted;
|
---|
187 | }
|
---|
188 |
|
---|
189 | void WidgetDataBaseItem::setPromoted(bool b)
|
---|
190 | {
|
---|
191 | m_promoted = b;
|
---|
192 | }
|
---|
193 |
|
---|
194 | QString WidgetDataBaseItem::extends() const
|
---|
195 | {
|
---|
196 | return m_extends;
|
---|
197 | }
|
---|
198 |
|
---|
199 | void WidgetDataBaseItem::setExtends(const QString &s)
|
---|
200 | {
|
---|
201 | m_extends = s;
|
---|
202 | }
|
---|
203 |
|
---|
204 | void WidgetDataBaseItem::setDefaultPropertyValues(const QList<QVariant> &list)
|
---|
205 | {
|
---|
206 | m_defaultPropertyValues = list;
|
---|
207 | }
|
---|
208 |
|
---|
209 | QList<QVariant> WidgetDataBaseItem::defaultPropertyValues() const
|
---|
210 | {
|
---|
211 | return m_defaultPropertyValues;
|
---|
212 | }
|
---|
213 |
|
---|
214 | QStringList WidgetDataBaseItem::fakeSlots() const
|
---|
215 | {
|
---|
216 | return m_fakeSlots;
|
---|
217 | }
|
---|
218 |
|
---|
219 | void WidgetDataBaseItem::setFakeSlots(const QStringList &fs)
|
---|
220 | {
|
---|
221 | m_fakeSlots = fs;
|
---|
222 | }
|
---|
223 |
|
---|
224 | QStringList WidgetDataBaseItem::fakeSignals() const
|
---|
225 | {
|
---|
226 | return m_fakeSignals;
|
---|
227 | }
|
---|
228 |
|
---|
229 | void WidgetDataBaseItem::setFakeSignals(const QStringList &fs)
|
---|
230 | {
|
---|
231 | m_fakeSignals = fs;
|
---|
232 | }
|
---|
233 |
|
---|
234 | QString WidgetDataBaseItem::addPageMethod() const
|
---|
235 | {
|
---|
236 | return m_addPageMethod;
|
---|
237 | }
|
---|
238 |
|
---|
239 | void WidgetDataBaseItem::setAddPageMethod(const QString &m)
|
---|
240 | {
|
---|
241 | m_addPageMethod = m;
|
---|
242 | }
|
---|
243 |
|
---|
244 | WidgetDataBaseItem *WidgetDataBaseItem::clone(const QDesignerWidgetDataBaseItemInterface *item)
|
---|
245 | {
|
---|
246 | WidgetDataBaseItem *rc = new WidgetDataBaseItem(item->name(), item->group());
|
---|
247 |
|
---|
248 | rc->setToolTip(item->toolTip());
|
---|
249 | rc->setWhatsThis(item->whatsThis());
|
---|
250 | rc->setIncludeFile(item->includeFile());
|
---|
251 | rc->setIcon(item->icon());
|
---|
252 | rc->setCompat(item->isCompat());
|
---|
253 | rc->setContainer(item->isContainer());
|
---|
254 | rc->setCustom(item->isCustom() );
|
---|
255 | rc->setPluginPath(item->pluginPath());
|
---|
256 | rc->setPromoted(item->isPromoted());
|
---|
257 | rc->setExtends(item->extends());
|
---|
258 | rc->setDefaultPropertyValues(item->defaultPropertyValues());
|
---|
259 | // container page method, fake slots and signals ignored here.y
|
---|
260 | return rc;
|
---|
261 | }
|
---|
262 |
|
---|
263 | // ----------------------------------------------------------
|
---|
264 | WidgetDataBase::WidgetDataBase(QDesignerFormEditorInterface *core, QObject *parent)
|
---|
265 | : QDesignerWidgetDataBaseInterface(parent),
|
---|
266 | m_core(core)
|
---|
267 | {
|
---|
268 | #define DECLARE_LAYOUT(L, C)
|
---|
269 | #define DECLARE_COMPAT_WIDGET(W, C) DECLARE_WIDGET(W, C)
|
---|
270 | #define DECLARE_WIDGET(W, C) append(new WidgetDataBaseItem(QString::fromUtf8(#W)));
|
---|
271 |
|
---|
272 | #include "widgets.table"
|
---|
273 |
|
---|
274 | #undef DECLARE_COMPAT_WIDGET
|
---|
275 | #undef DECLARE_LAYOUT
|
---|
276 | #undef DECLARE_WIDGET
|
---|
277 | #undef DECLARE_WIDGET_1
|
---|
278 |
|
---|
279 | append(new WidgetDataBaseItem(QString::fromUtf8("Line")));
|
---|
280 | append(new WidgetDataBaseItem(QString::fromUtf8("Spacer")));
|
---|
281 | append(new WidgetDataBaseItem(QString::fromUtf8("QSplitter")));
|
---|
282 | append(new WidgetDataBaseItem(QString::fromUtf8("QLayoutWidget")));
|
---|
283 | // QDesignerWidget is used as central widget and as container for tab widgets, etc.
|
---|
284 | WidgetDataBaseItem *designerWidgetItem = new WidgetDataBaseItem(QString::fromUtf8("QDesignerWidget"));
|
---|
285 | designerWidgetItem->setContainer(true);
|
---|
286 | append(designerWidgetItem);
|
---|
287 | append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerDialog")));
|
---|
288 | append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerMenu")));
|
---|
289 | append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerMenuBar")));
|
---|
290 | append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerDockWidget")));
|
---|
291 | append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerQ3WidgetStack")));
|
---|
292 | append(new WidgetDataBaseItem(QString::fromUtf8("QAction")));
|
---|
293 | append(new WidgetDataBaseItem(QString::fromUtf8("QButtonGroup")));
|
---|
294 |
|
---|
295 | // ### remove me
|
---|
296 | // ### check the casts
|
---|
297 |
|
---|
298 | #if 0 // ### enable me after 4.1
|
---|
299 | item(indexOfClassName(QLatin1String("QToolBar")))->setContainer(true);
|
---|
300 | #endif
|
---|
301 |
|
---|
302 | item(indexOfClassName(QLatin1String("QTabWidget")))->setContainer(true);
|
---|
303 | item(indexOfClassName(QLatin1String("QGroupBox")))->setContainer(true);
|
---|
304 | item(indexOfClassName(QLatin1String("QScrollArea")))->setContainer(true);
|
---|
305 | item(indexOfClassName(QLatin1String("QStackedWidget")))->setContainer(true);
|
---|
306 | item(indexOfClassName(QLatin1String("QToolBox")))->setContainer(true);
|
---|
307 | item(indexOfClassName(QLatin1String("QFrame")))->setContainer(true);
|
---|
308 | item(indexOfClassName(QLatin1String("QLayoutWidget")))->setContainer(true);
|
---|
309 | item(indexOfClassName(QLatin1String("QDesignerWidget")))->setContainer(true);
|
---|
310 | item(indexOfClassName(QLatin1String("QDesignerDialog")))->setContainer(true);
|
---|
311 | item(indexOfClassName(QLatin1String("QSplitter")))->setContainer(true);
|
---|
312 | item(indexOfClassName(QLatin1String("QMainWindow")))->setContainer(true);
|
---|
313 | item(indexOfClassName(QLatin1String("QDockWidget")))->setContainer(true);
|
---|
314 | item(indexOfClassName(QLatin1String("QDesignerDockWidget")))->setContainer(true);
|
---|
315 | item(indexOfClassName(QLatin1String("QDesignerQ3WidgetStack")))->setContainer(true);
|
---|
316 | item(indexOfClassName(QLatin1String("QMdiArea")))->setContainer(true);
|
---|
317 | item(indexOfClassName(QLatin1String("QWorkspace")))->setContainer(true);
|
---|
318 | item(indexOfClassName(QLatin1String("QWizard")))->setContainer(true);
|
---|
319 | item(indexOfClassName(QLatin1String("QWizardPage")))->setContainer(true);
|
---|
320 |
|
---|
321 | item(indexOfClassName(QLatin1String("QWidget")))->setContainer(true);
|
---|
322 | item(indexOfClassName(QLatin1String("QDialog")))->setContainer(true);
|
---|
323 | }
|
---|
324 |
|
---|
325 | WidgetDataBase::~WidgetDataBase()
|
---|
326 | {
|
---|
327 | }
|
---|
328 |
|
---|
329 | QDesignerFormEditorInterface *WidgetDataBase::core() const
|
---|
330 | {
|
---|
331 | return m_core;
|
---|
332 | }
|
---|
333 |
|
---|
334 | int WidgetDataBase::indexOfObject(QObject *object, bool /*resolveName*/) const
|
---|
335 | {
|
---|
336 | QExtensionManager *mgr = m_core->extensionManager();
|
---|
337 | QDesignerLanguageExtension *lang = qt_extension<QDesignerLanguageExtension*> (mgr, m_core);
|
---|
338 |
|
---|
339 | QString id;
|
---|
340 |
|
---|
341 | if (lang)
|
---|
342 | id = lang->classNameOf(object);
|
---|
343 |
|
---|
344 | if (id.isEmpty())
|
---|
345 | id = WidgetFactory::classNameOf(m_core,object);
|
---|
346 |
|
---|
347 | return QDesignerWidgetDataBaseInterface::indexOfClassName(id);
|
---|
348 | }
|
---|
349 |
|
---|
350 | static WidgetDataBaseItem *createCustomWidgetItem(const QDesignerCustomWidgetInterface *c,
|
---|
351 | const QDesignerCustomWidgetData &data)
|
---|
352 | {
|
---|
353 | WidgetDataBaseItem *item = new WidgetDataBaseItem(c->name(), c->group());
|
---|
354 | item->setContainer(c->isContainer());
|
---|
355 | item->setCustom(true);
|
---|
356 | item->setIcon(c->icon());
|
---|
357 | item->setIncludeFile(c->includeFile());
|
---|
358 | item->setToolTip(c->toolTip());
|
---|
359 | item->setWhatsThis(c->whatsThis());
|
---|
360 | item->setPluginPath(data.pluginPath());
|
---|
361 | item->setAddPageMethod(data.xmlAddPageMethod());
|
---|
362 | item->setExtends(data.xmlExtends());
|
---|
363 | return item;
|
---|
364 | }
|
---|
365 |
|
---|
366 | void WidgetDataBase::loadPlugins()
|
---|
367 | {
|
---|
368 | typedef QMap<QString, int> NameIndexMap;
|
---|
369 | typedef QList<QDesignerWidgetDataBaseItemInterface*> ItemList;
|
---|
370 | typedef QMap<QString, QDesignerWidgetDataBaseItemInterface*> NameItemMap;
|
---|
371 | typedef QSet<QString> NameSet;
|
---|
372 | // 1) create a map of existing custom classes
|
---|
373 | NameIndexMap existingCustomClasses;
|
---|
374 | NameSet nonCustomClasses;
|
---|
375 | const int count = m_items.size();
|
---|
376 | for (int i = 0; i < count; i++) {
|
---|
377 | const QDesignerWidgetDataBaseItemInterface* item = m_items[i];
|
---|
378 | if (item->isCustom() && !item->isPromoted())
|
---|
379 | existingCustomClasses.insert(item->name(), i);
|
---|
380 | else
|
---|
381 | nonCustomClasses.insert(item->name());
|
---|
382 | }
|
---|
383 | // 2) create a list plugins
|
---|
384 | ItemList pluginList;
|
---|
385 | const QDesignerPluginManager *pm = m_core->pluginManager();
|
---|
386 | foreach(QDesignerCustomWidgetInterface* c, pm->registeredCustomWidgets())
|
---|
387 | pluginList += createCustomWidgetItem(c, pm->customWidgetData(c));
|
---|
388 |
|
---|
389 | // 3) replace custom classes or add new ones, remove them from existingCustomClasses,
|
---|
390 | // leaving behind deleted items
|
---|
391 | unsigned replacedPlugins = 0;
|
---|
392 | unsigned addedPlugins = 0;
|
---|
393 | unsigned removedPlugins = 0;
|
---|
394 | if (!pluginList.empty()) {
|
---|
395 | ItemList::const_iterator cend = pluginList.constEnd();
|
---|
396 | for (ItemList::const_iterator it = pluginList.constBegin();it != cend; ++it ) {
|
---|
397 | QDesignerWidgetDataBaseItemInterface* pluginItem = *it;
|
---|
398 | const QString pluginName = pluginItem->name();
|
---|
399 | NameIndexMap::iterator existingIt = existingCustomClasses.find(pluginName);
|
---|
400 | if (existingIt == existingCustomClasses.end()) {
|
---|
401 | // Add new class.
|
---|
402 | if (nonCustomClasses.contains(pluginName)) {
|
---|
403 | designerWarning(tr("A custom widget plugin whose class name (%1) matches that of an existing class has been found.").arg(pluginName));
|
---|
404 | } else {
|
---|
405 | append(pluginItem);
|
---|
406 | addedPlugins++;
|
---|
407 | }
|
---|
408 | } else {
|
---|
409 | // replace existing info
|
---|
410 | const int existingIndex = existingIt.value();
|
---|
411 | delete m_items[existingIndex];
|
---|
412 | m_items[existingIndex] = pluginItem;
|
---|
413 | existingCustomClasses.erase(existingIt);
|
---|
414 | replacedPlugins++;
|
---|
415 |
|
---|
416 | }
|
---|
417 | }
|
---|
418 | }
|
---|
419 | // 4) remove classes that have not been matched. The stored indexes become invalid while deleting.
|
---|
420 | if (!existingCustomClasses.empty()) {
|
---|
421 | NameIndexMap::const_iterator cend = existingCustomClasses.constEnd();
|
---|
422 | for (NameIndexMap::const_iterator it = existingCustomClasses.constBegin();it != cend; ++it ) {
|
---|
423 | const int index = indexOfClassName(it.key());
|
---|
424 | if (index != -1) {
|
---|
425 | remove(index);
|
---|
426 | removedPlugins++;
|
---|
427 | }
|
---|
428 | }
|
---|
429 | }
|
---|
430 | if (debugWidgetDataBase)
|
---|
431 | qDebug() << "WidgetDataBase::loadPlugins(): " << addedPlugins << " added, " << replacedPlugins << " replaced, " << removedPlugins << "deleted.";
|
---|
432 | }
|
---|
433 |
|
---|
434 | void WidgetDataBase::remove(int index)
|
---|
435 | {
|
---|
436 | Q_ASSERT(index < m_items.size());
|
---|
437 | delete m_items.takeAt(index);
|
---|
438 | }
|
---|
439 |
|
---|
440 | QList<QVariant> WidgetDataBase::defaultPropertyValues(const QString &name)
|
---|
441 | {
|
---|
442 | WidgetFactory *factory = qobject_cast<WidgetFactory *>(m_core->widgetFactory());
|
---|
443 | Q_ASSERT(factory);
|
---|
444 | // Create non-widgets, widgets in order
|
---|
445 | QObject* object = factory->createObject(name, 0);
|
---|
446 | if (!object)
|
---|
447 | object = factory->createWidget(name, 0);
|
---|
448 | if (!object) {
|
---|
449 | qDebug() << "** WARNING Factory failed to create " << name;
|
---|
450 | return QList<QVariant>();
|
---|
451 | }
|
---|
452 | // Get properties from sheet.
|
---|
453 | QList<QVariant> result;
|
---|
454 | if (const QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(m_core->extensionManager(), object)) {
|
---|
455 | const int propertyCount = sheet->count();
|
---|
456 | for (int i = 0; i < propertyCount; ++i) {
|
---|
457 | result.append(sheet->property(i));
|
---|
458 | }
|
---|
459 | }
|
---|
460 | delete object;
|
---|
461 | return result;
|
---|
462 | }
|
---|
463 |
|
---|
464 | void WidgetDataBase::grabDefaultPropertyValues()
|
---|
465 | {
|
---|
466 | const int itemCount = count();
|
---|
467 | for (int i = 0; i < itemCount; ++i) {
|
---|
468 | QDesignerWidgetDataBaseItemInterface *dbItem = item(i);
|
---|
469 | const QList<QVariant> default_prop_values = defaultPropertyValues(dbItem->name());
|
---|
470 | dbItem->setDefaultPropertyValues(default_prop_values);
|
---|
471 | }
|
---|
472 | }
|
---|
473 |
|
---|
474 | void WidgetDataBase::grabStandardWidgetBoxIcons()
|
---|
475 | {
|
---|
476 | // At this point, grab the default icons for the non-custom widgets from
|
---|
477 | // the widget box. They will show up in the object inspector.
|
---|
478 | if (const QDesignerWidgetBox *wb = qobject_cast<const QDesignerWidgetBox *>(m_core->widgetBox())) {
|
---|
479 | const QString qWidgetClass = QLatin1String("QWidget");
|
---|
480 | const int itemCount = count();
|
---|
481 | for (int i = 0; i < itemCount; ++i) {
|
---|
482 | QDesignerWidgetDataBaseItemInterface *dbItem = item(i);
|
---|
483 | if (!dbItem->isCustom() && dbItem->icon().isNull()) {
|
---|
484 | // Careful not to catch the layout icons when looking for
|
---|
485 | // QWidget
|
---|
486 | const QString name = dbItem->name();
|
---|
487 | if (name == qWidgetClass) {
|
---|
488 | dbItem->setIcon(wb->iconForWidget(name, QLatin1String("Containers")));
|
---|
489 | } else {
|
---|
490 | dbItem->setIcon(wb->iconForWidget(name));
|
---|
491 | }
|
---|
492 | }
|
---|
493 | }
|
---|
494 | }
|
---|
495 | }
|
---|
496 |
|
---|
497 | // --------------------- Functions relevant generation of new forms based on widgets (apart from the standard templates)
|
---|
498 |
|
---|
499 | enum { NewFormWidth = 400, NewFormHeight = 300 };
|
---|
500 |
|
---|
501 | // Check if class is suitable to generate a form from
|
---|
502 | static inline bool isExistingTemplate(const QString &className)
|
---|
503 | {
|
---|
504 | return className == QLatin1String("QWidget") || className == QLatin1String("QDialog") || className == QLatin1String("QMainWindow");
|
---|
505 | }
|
---|
506 |
|
---|
507 | // Check if class is suitable to generate a form from
|
---|
508 | static inline bool suitableForNewForm(const QString &className)
|
---|
509 | {
|
---|
510 | if (className.isEmpty()) // Missing custom widget information
|
---|
511 | return false;
|
---|
512 | if (className == QLatin1String("QWorkspace"))
|
---|
513 | return false;
|
---|
514 | if (className == QLatin1String("QSplitter"))
|
---|
515 | return false;
|
---|
516 | if (className.startsWith(QLatin1String("QDesigner")) || className.startsWith(QLatin1String("Q3")) || className.startsWith(QLatin1String("QLayout")))
|
---|
517 | return false;
|
---|
518 | return true;
|
---|
519 | }
|
---|
520 |
|
---|
521 | // Return a list of widget classes from which new forms can be generated.
|
---|
522 | // Suitable for 'New form' wizards in integrations.
|
---|
523 | QStringList WidgetDataBase::formWidgetClasses(const QDesignerFormEditorInterface *core)
|
---|
524 | {
|
---|
525 | static QStringList rc;
|
---|
526 | if (rc.empty()) {
|
---|
527 | const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase();
|
---|
528 | const int widgetCount = wdb->count();
|
---|
529 | for (int i = 0; i < widgetCount; i++) {
|
---|
530 | const QDesignerWidgetDataBaseItemInterface *item = wdb->item(i);
|
---|
531 | if (item->isContainer() && !item->isCustom() && !item->isPromoted()) {
|
---|
532 | const QString name = item->name(); // Standard Widgets: no existing templates
|
---|
533 | if (!isExistingTemplate(name) && suitableForNewForm(name))
|
---|
534 | rc += name;
|
---|
535 | }
|
---|
536 | }
|
---|
537 | }
|
---|
538 | return rc;
|
---|
539 | }
|
---|
540 |
|
---|
541 | // Return a list of custom widget classes from which new forms can be generated.
|
---|
542 | // Suitable for 'New form' wizards in integrations.
|
---|
543 | QStringList WidgetDataBase::customFormWidgetClasses(const QDesignerFormEditorInterface *core)
|
---|
544 | {
|
---|
545 | QStringList rc;
|
---|
546 | const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase();
|
---|
547 | const int widgetCount = wdb->count();
|
---|
548 | for (int i = 0; i < widgetCount; i++) { // Custom widgets: check name and base class.
|
---|
549 | const QDesignerWidgetDataBaseItemInterface *item = wdb->item(i);
|
---|
550 | if (item->isContainer() && item->isCustom() && !item->isPromoted()) {
|
---|
551 | if (suitableForNewForm(item->name()) && suitableForNewForm(item->extends()))
|
---|
552 | rc += item->name();
|
---|
553 | }
|
---|
554 | }
|
---|
555 | return rc;
|
---|
556 | }
|
---|
557 |
|
---|
558 | // Get XML for a new form from the widget box. Change objectName/geometry
|
---|
559 | // properties to be suitable for new forms
|
---|
560 | static QString xmlFromWidgetBox(const QDesignerFormEditorInterface *core, const QString &className, const QString &objectName)
|
---|
561 | {
|
---|
562 | typedef QList<DomProperty*> PropertyList;
|
---|
563 |
|
---|
564 | QDesignerWidgetBoxInterface::Widget widget;
|
---|
565 | const bool found = QDesignerWidgetBox::findWidget(core->widgetBox(), className, QString(), &widget);
|
---|
566 | if (!found)
|
---|
567 | return QString();
|
---|
568 | DomUI *domUI = QDesignerWidgetBox::xmlToUi(className, widget.domXml(), false);
|
---|
569 | domUI->setAttributeVersion(QLatin1String("4.0"));
|
---|
570 | if (!domUI)
|
---|
571 | return QString();
|
---|
572 | DomWidget *domWidget = domUI->elementWidget();
|
---|
573 | if (!domWidget)
|
---|
574 | return QString();
|
---|
575 | // Properties: Remove the "objectName" property in favour of the name attribute and check geometry.
|
---|
576 | domWidget->setAttributeName(objectName);
|
---|
577 | const QString geometryProperty = QLatin1String("geometry");
|
---|
578 | const QString objectNameProperty = QLatin1String("objectName");
|
---|
579 | PropertyList properties = domWidget->elementProperty();
|
---|
580 | for (PropertyList::iterator it = properties.begin(); it != properties.end(); ) {
|
---|
581 | DomProperty *property = *it;
|
---|
582 | if (property->attributeName() == objectNameProperty) { // remove "objectName"
|
---|
583 | it = properties.erase(it);
|
---|
584 | delete property;
|
---|
585 | } else {
|
---|
586 | if (property->attributeName() == geometryProperty) { // Make sure form is at least 400, 300
|
---|
587 | if (DomRect *geom = property->elementRect()) {
|
---|
588 | if (geom->elementWidth() < NewFormWidth)
|
---|
589 | geom->setElementWidth(NewFormWidth);
|
---|
590 | if (geom->elementHeight() < NewFormHeight)
|
---|
591 | geom->setElementHeight(NewFormHeight);
|
---|
592 | }
|
---|
593 | }
|
---|
594 | ++it;
|
---|
595 | }
|
---|
596 | }
|
---|
597 | // Add a window title property
|
---|
598 | DomString *windowTitleString = new DomString;
|
---|
599 | windowTitleString->setText(objectName);
|
---|
600 | DomProperty *windowTitleProperty = new DomProperty;
|
---|
601 | windowTitleProperty->setAttributeName(QLatin1String("windowTitle"));
|
---|
602 | windowTitleProperty->setElementString(windowTitleString);
|
---|
603 | properties.push_back(windowTitleProperty);
|
---|
604 | // ------
|
---|
605 | domWidget->setElementProperty(properties);
|
---|
606 | // Embed in in DomUI and get string. Omit the version number.
|
---|
607 | domUI->setElementClass(objectName);
|
---|
608 |
|
---|
609 | QString rc;
|
---|
610 | { // Serialize domUI
|
---|
611 | QXmlStreamWriter writer(&rc);
|
---|
612 | writer.setAutoFormatting(true);
|
---|
613 | writer.setAutoFormattingIndent(1);
|
---|
614 | writer.writeStartDocument();
|
---|
615 | domUI->write(writer);
|
---|
616 | writer.writeEndDocument();
|
---|
617 | }
|
---|
618 | delete domUI;
|
---|
619 | return rc;
|
---|
620 | }
|
---|
621 |
|
---|
622 | // Generate default standard ui new form xml based on the class passed on as similarClassName.
|
---|
623 | static QString generateNewFormXML(const QString &className, const QString &similarClassName, const QString &name)
|
---|
624 | {
|
---|
625 | QString rc; {
|
---|
626 | QTextStream str(&rc);
|
---|
627 | str << QLatin1String("<ui version=\"4.0\" >\n<class>") << name << QLatin1String("</class>\n")
|
---|
628 | << QLatin1String("<widget class=\"") << className << QLatin1String("\" name=\"") << name << QLatin1String("\" >\n")
|
---|
629 | << QLatin1String("<property name=\"geometry\" >\n<rect><x>0</x><y>0</y><width>")
|
---|
630 | << NewFormWidth << QLatin1String("</width><height>") << NewFormHeight << QLatin1String("</height></rect>\n</property>\n");
|
---|
631 | str << QLatin1String("<property name=\"windowTitle\" >\n<string>") << name << QLatin1String("</string>\n</property>\n");
|
---|
632 |
|
---|
633 | if (similarClassName == QLatin1String("QMainWindow")) {
|
---|
634 | str << QLatin1String("<widget class=\"QWidget\" name=\"centralwidget\" />\n");
|
---|
635 | } else {
|
---|
636 | if (similarClassName == QLatin1String("QWizard"))
|
---|
637 | str << QLatin1String("<widget class=\"QWizardPage\" name=\"wizardPage1\" /><widget class=\"QWizardPage\" name=\"wizardPage2\" />\n");
|
---|
638 | }
|
---|
639 | str << QLatin1String("</widget>\n</ui>\n");
|
---|
640 | }
|
---|
641 | return rc;
|
---|
642 | }
|
---|
643 |
|
---|
644 | // Generate a form template using a class name obtained from formWidgetClasses(), customFormWidgetClasses().
|
---|
645 | QString WidgetDataBase::formTemplate(const QDesignerFormEditorInterface *core, const QString &className, const QString &objectName)
|
---|
646 | {
|
---|
647 | // How to find suitable XML for a class:
|
---|
648 | // 1) Look in widget box (as all the required centralwidgets, tab widget pages, etc. should be there).
|
---|
649 | const QString widgetBoxXml = xmlFromWidgetBox(core, className, objectName);
|
---|
650 | if (!widgetBoxXml.isEmpty())
|
---|
651 | return widgetBoxXml;
|
---|
652 | // 2) If that fails, only custom main windows, custom dialogs and unsupported Qt Widgets should
|
---|
653 | // be left over. Generate something that is similar to the default templates. Find a similar class.
|
---|
654 | const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase();
|
---|
655 | QString similarClass = QLatin1String("QWidget");
|
---|
656 | const int index = wdb->indexOfClassName(className);
|
---|
657 | if (index != -1) {
|
---|
658 | const QDesignerWidgetDataBaseItemInterface *item = wdb->item(index);
|
---|
659 | similarClass = item->isCustom() ? item->extends() : item->name();
|
---|
660 | }
|
---|
661 | // Generate standard ui based on the class passed on as baseClassName.
|
---|
662 | const QString rc = generateNewFormXML(className, similarClass, objectName);
|
---|
663 | return rc;
|
---|
664 | }
|
---|
665 |
|
---|
666 | // Set a fixed size on a XML template
|
---|
667 | QString WidgetDataBase::scaleFormTemplate(const QString &xml, const QSize &size, bool fixed)
|
---|
668 | {
|
---|
669 | typedef QList<DomProperty*> PropertyList;
|
---|
670 | DomUI *domUI = QDesignerWidgetBox::xmlToUi(QLatin1String("Form"), xml, false);
|
---|
671 | if (!domUI)
|
---|
672 | return QString();
|
---|
673 | DomWidget *domWidget = domUI->elementWidget();
|
---|
674 | if (!domWidget)
|
---|
675 | return QString();
|
---|
676 | // Properties: Find/Ensure the geometry, minimum and maximum sizes properties
|
---|
677 | const QString geometryPropertyName = QLatin1String("geometry");
|
---|
678 | const QString minimumSizePropertyName = QLatin1String("minimumSize");
|
---|
679 | const QString maximumSizePropertyName = QLatin1String("maximumSize");
|
---|
680 | DomProperty *geomProperty = 0;
|
---|
681 | DomProperty *minimumSizeProperty = 0;
|
---|
682 | DomProperty *maximumSizeProperty = 0;
|
---|
683 |
|
---|
684 | PropertyList properties = domWidget->elementProperty();
|
---|
685 | const PropertyList::const_iterator cend = properties.constEnd();
|
---|
686 | for (PropertyList::const_iterator it = properties.constBegin(); it != cend; ++it) {
|
---|
687 | const QString name = (*it)->attributeName();
|
---|
688 | if (name == geometryPropertyName) {
|
---|
689 | geomProperty = *it;
|
---|
690 | } else {
|
---|
691 | if (name == minimumSizePropertyName) {
|
---|
692 | minimumSizeProperty = *it;
|
---|
693 | } else {
|
---|
694 | if (name == maximumSizePropertyName)
|
---|
695 | maximumSizeProperty = *it;
|
---|
696 | }
|
---|
697 | }
|
---|
698 | }
|
---|
699 | if (!geomProperty) {
|
---|
700 | geomProperty = new DomProperty;
|
---|
701 | geomProperty->setAttributeName(geometryPropertyName);
|
---|
702 | geomProperty->setElementRect(new DomRect);
|
---|
703 | properties.push_front(geomProperty);
|
---|
704 | }
|
---|
705 | if (fixed) {
|
---|
706 | if (!minimumSizeProperty) {
|
---|
707 | minimumSizeProperty = new DomProperty;
|
---|
708 | minimumSizeProperty->setAttributeName(minimumSizePropertyName);
|
---|
709 | minimumSizeProperty->setElementSize(new DomSize);
|
---|
710 | properties.push_back(minimumSizeProperty);
|
---|
711 | }
|
---|
712 | if (!maximumSizeProperty) {
|
---|
713 | maximumSizeProperty = new DomProperty;
|
---|
714 | maximumSizeProperty->setAttributeName(maximumSizePropertyName);
|
---|
715 | maximumSizeProperty->setElementSize(new DomSize);
|
---|
716 | properties.push_back(maximumSizeProperty);
|
---|
717 | }
|
---|
718 | }
|
---|
719 | // Set values of geometry, minimum and maximum sizes properties
|
---|
720 | const int width = size.width();
|
---|
721 | const int height = size.height();
|
---|
722 | if (DomRect *geom = geomProperty->elementRect()) {
|
---|
723 | geom->setElementWidth(width);
|
---|
724 | geom->setElementHeight(height);
|
---|
725 | }
|
---|
726 | if (fixed) {
|
---|
727 | if (DomSize *s = minimumSizeProperty->elementSize()) {
|
---|
728 | s->setElementWidth(width);
|
---|
729 | s->setElementHeight(height);
|
---|
730 | }
|
---|
731 | if (DomSize *s = maximumSizeProperty->elementSize()) {
|
---|
732 | s->setElementWidth(width);
|
---|
733 | s->setElementHeight(height);
|
---|
734 | }
|
---|
735 | }
|
---|
736 | // write back
|
---|
737 | domWidget->setElementProperty(properties);
|
---|
738 |
|
---|
739 | QString rc;
|
---|
740 | { // serialize domUI
|
---|
741 | QXmlStreamWriter writer(&rc);
|
---|
742 | writer.setAutoFormatting(true);
|
---|
743 | writer.setAutoFormattingIndent(1);
|
---|
744 | writer.writeStartDocument();
|
---|
745 | domUI->write(writer);
|
---|
746 | writer.writeEndDocument();
|
---|
747 | }
|
---|
748 |
|
---|
749 | delete domUI;
|
---|
750 | return rc;
|
---|
751 | }
|
---|
752 |
|
---|
753 | // ---- free functions
|
---|
754 | QDESIGNER_SHARED_EXPORT IncludeSpecification includeSpecification(QString includeFile)
|
---|
755 | {
|
---|
756 | const bool global = !includeFile.isEmpty() &&
|
---|
757 | includeFile[0] == QLatin1Char('<') &&
|
---|
758 | includeFile[includeFile.size() - 1] == QLatin1Char('>');
|
---|
759 | if (global) {
|
---|
760 | includeFile.remove(includeFile.size() - 1, 1);
|
---|
761 | includeFile.remove(0, 1);
|
---|
762 | }
|
---|
763 | return IncludeSpecification(includeFile, global ? IncludeGlobal : IncludeLocal);
|
---|
764 | }
|
---|
765 |
|
---|
766 | QDESIGNER_SHARED_EXPORT QString buildIncludeFile(QString includeFile, IncludeType includeType) {
|
---|
767 | if (includeType == IncludeGlobal && !includeFile.isEmpty()) {
|
---|
768 | includeFile.append(QLatin1Char('>'));
|
---|
769 | includeFile.insert(0, QLatin1Char('<'));
|
---|
770 | }
|
---|
771 | return includeFile;
|
---|
772 | }
|
---|
773 |
|
---|
774 |
|
---|
775 | /* Appends a derived class to the database inheriting the data of the base class. Used
|
---|
776 | for custom and promoted widgets.
|
---|
777 |
|
---|
778 | Depending on whether an entry exists, the existing or a newly created entry is
|
---|
779 | returned. A return value of 0 indicates that the base class could not be found. */
|
---|
780 |
|
---|
781 | QDESIGNER_SHARED_EXPORT QDesignerWidgetDataBaseItemInterface *
|
---|
782 | appendDerived(QDesignerWidgetDataBaseInterface *db,
|
---|
783 | const QString &className, const QString &group,
|
---|
784 | const QString &baseClassName,
|
---|
785 | const QString &includeFile,
|
---|
786 | bool promoted, bool custom)
|
---|
787 | {
|
---|
788 | if (debugWidgetDataBase)
|
---|
789 | qDebug() << "appendDerived " << className << " derived from " << baseClassName;
|
---|
790 | // Check.
|
---|
791 | if (className.isEmpty() || baseClassName.isEmpty()) {
|
---|
792 | qWarning("** WARNING %s called with an empty class names: '%s' extends '%s'.",
|
---|
793 | Q_FUNC_INFO, className.toUtf8().constData(), baseClassName.toUtf8().constData());
|
---|
794 | return 0;
|
---|
795 | }
|
---|
796 | // Check whether item already exists.
|
---|
797 | QDesignerWidgetDataBaseItemInterface *derivedItem = 0;
|
---|
798 | const int existingIndex = db->indexOfClassName(className);
|
---|
799 | if ( existingIndex != -1)
|
---|
800 | derivedItem = db->item(existingIndex);
|
---|
801 | if (derivedItem) {
|
---|
802 | // Check the existing item for base class mismatch. This will likely
|
---|
803 | // happen when loading a file written by an instance with missing plugins.
|
---|
804 | // In that case, just warn and ignore the file properties.
|
---|
805 | //
|
---|
806 | // An empty base class indicates that it is not known (for example, for custom plugins).
|
---|
807 | // In this case, the widget DB is later updated once the widget is created
|
---|
808 | // by DOM (by querying the metaobject). Suppress the warning.
|
---|
809 | const QString existingBaseClass = derivedItem->extends();
|
---|
810 | if (existingBaseClass.isEmpty() || baseClassName == existingBaseClass)
|
---|
811 | return derivedItem;
|
---|
812 |
|
---|
813 | // Warn about mismatches
|
---|
814 | designerWarning(QCoreApplication::translate("WidgetDataBase",
|
---|
815 | "The file contains a custom widget '%1' whose base class (%2)"
|
---|
816 | " differs from the current entry in the widget database (%3)."
|
---|
817 | " The widget database is left unchanged.").
|
---|
818 | arg(className, baseClassName, existingBaseClass));
|
---|
819 | return derivedItem;
|
---|
820 | }
|
---|
821 | // Create this item, inheriting its base properties
|
---|
822 | const int baseIndex = db->indexOfClassName(baseClassName);
|
---|
823 | if (baseIndex == -1) {
|
---|
824 | if (debugWidgetDataBase)
|
---|
825 | qDebug() << "appendDerived failed due to missing base class";
|
---|
826 | return 0;
|
---|
827 | }
|
---|
828 | const QDesignerWidgetDataBaseItemInterface *baseItem = db->item(baseIndex);
|
---|
829 | derivedItem = WidgetDataBaseItem::clone(baseItem);
|
---|
830 | // Sort of hack: If base class is QWidget, we most likely
|
---|
831 | // do not want to inherit the container attribute.
|
---|
832 | static const QString qWidgetName = QLatin1String("QWidget");
|
---|
833 | if (baseItem->name() == qWidgetName)
|
---|
834 | derivedItem->setContainer(false);
|
---|
835 | // set new props
|
---|
836 | derivedItem->setName(className);
|
---|
837 | derivedItem->setGroup(group);
|
---|
838 | derivedItem->setCustom(custom);
|
---|
839 | derivedItem->setPromoted(promoted);
|
---|
840 | derivedItem->setExtends(baseClassName);
|
---|
841 | derivedItem->setIncludeFile(includeFile);
|
---|
842 | db->append(derivedItem);
|
---|
843 | return derivedItem;
|
---|
844 | }
|
---|
845 |
|
---|
846 | /* Return a list of database items to which a class can be promoted to. */
|
---|
847 |
|
---|
848 | QDESIGNER_SHARED_EXPORT WidgetDataBaseItemList
|
---|
849 | promotionCandidates(const QDesignerWidgetDataBaseInterface *db,
|
---|
850 | const QString &baseClassName)
|
---|
851 | {
|
---|
852 | WidgetDataBaseItemList rc;
|
---|
853 | // find existing promoted widgets deriving from base.
|
---|
854 | const int count = db->count();
|
---|
855 | for (int i = 0; i < count; ++i) {
|
---|
856 | QDesignerWidgetDataBaseItemInterface *item = db->item(i);
|
---|
857 | if (item->isPromoted() && item->extends() == baseClassName) {
|
---|
858 | rc.push_back(item);
|
---|
859 | }
|
---|
860 | }
|
---|
861 | return rc;
|
---|
862 | }
|
---|
863 | } // namespace qdesigner_internal
|
---|
864 |
|
---|
865 | QT_END_NAMESPACE
|
---|