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 "widgetfactory_p.h"
30#include "widgetdatabase_p.h"
31#include "metadatabase_p.h"
32#include "qlayout_widget_p.h"
33#include "qdesigner_widget_p.h"
34#include "qdesigner_tabwidget_p.h"
35#include "qdesigner_toolbox_p.h"
36#include "qdesigner_stackedbox_p.h"
37#include "qdesigner_toolbar_p.h"
38#include "qdesigner_menubar_p.h"
39#include "qdesigner_menu_p.h"
40#include "qdesigner_dockwidget_p.h"
41#include "qdesigner_utils_p.h"
42#include "formwindowbase_p.h"
43
44// shared
45#include "layoutinfo_p.h"
46#include "spacer_widget_p.h"
47#include "layout_p.h"
48#include "abstractintrospection_p.h"
49
50// sdk
51#include <QtDesigner/abstractformeditor.h>
52#include <QtDesigner/container.h>
53#include <QtDesigner/qextensionmanager.h>
54#include <QtDesigner/propertysheet.h>
55#include <QtDesigner/abstractlanguage.h>
56#include <QtDesigner/abstractformwindowmanager.h>
57#include <QtDesigner/abstractformwindowcursor.h>
58
59#include <QtUiPlugin/customwidget.h>
60
61#include <QtWidgets/QtWidgets>
62#include <QtWidgets/qscrollbar.h>
63#include <QtWidgets/qfontcombobox.h>
64#include <QtWidgets/qabstractspinbox.h>
65#include <QtWidgets/qlineedit.h>
66#include <QtWidgets/qbuttongroup.h>
67#include <QtWidgets/qstyle.h>
68#include <QtWidgets/qstylefactory.h>
69#include <QtWidgets/qwizard.h>
70#include <QtCore/qdebug.h>
71#include <QtCore/qmetaobject.h>
72#include <QtCore/qpointer.h>
73
74QT_BEGIN_NAMESPACE
75
76#ifdef Q_OS_WIN
77static inline bool isAxWidget(const QObject *o)
78{
79 // Is it one of QDesignerAxWidget/QDesignerAxPluginWidget?
80 static const char *axWidgetName = "QDesignerAx";
81 static const unsigned axWidgetNameLen = qstrlen(axWidgetName);
82 return qstrncmp(o->metaObject()->className(), axWidgetName, axWidgetNameLen) == 0;
83}
84#endif
85
86/* Dynamic boolean property indicating object was created by the factory
87 * for the form editor. */
88
89static const char *formEditorDynamicProperty = "_q_formEditorObject";
90
91namespace qdesigner_internal {
92
93// A friendly SpinBox that grants access to its QLineEdit
94class FriendlySpinBox : public QAbstractSpinBox {
95public:
96 friend class WidgetFactory;
97};
98
99// An event filter for form-combo boxes that prevents the embedded line edit
100// from getting edit focus (and drawing blue artifacts/lines). It catches the
101// ChildPolished event when the "editable" property flips to true and the
102// QLineEdit is created and turns off the LineEdit's focus policy.
103
104class ComboEventFilter : public QObject {
105public:
106 explicit ComboEventFilter(QComboBox *parent) : QObject(parent) {}
107 bool eventFilter(QObject *watched, QEvent *event) override;
108};
109
110bool ComboEventFilter::eventFilter(QObject *watched, QEvent *event)
111{
112 if (event->type() == QEvent::ChildPolished) {
113 QComboBox *cb = static_cast<QComboBox*>(watched);
114 if (QLineEdit *le = cb->lineEdit()) {
115 le->setFocusPolicy(Qt::NoFocus);
116 le->setCursor(Qt::ArrowCursor);
117 }
118 }
119 return QObject::eventFilter(watched, event);
120}
121
122/* Watch out for QWizards changing their pages and make sure that not some
123 * selected widget becomes invisible on a hidden page (causing the selection
124 * handles to shine through). Select the wizard in that case in analogy to
125 * the QTabWidget event filters, etc. */
126
127class WizardPageChangeWatcher : public QObject {
128 Q_OBJECT
129public:
130 explicit WizardPageChangeWatcher(QWizard *parent);
131
132public slots:
133 void pageChanged();
134};
135
136WizardPageChangeWatcher::WizardPageChangeWatcher(QWizard *parent) :
137 QObject(parent)
138{
139 connect(sender: parent, signal: &QWizard::currentIdChanged, receiver: this, slot: &WizardPageChangeWatcher::pageChanged);
140}
141
142void WizardPageChangeWatcher::pageChanged()
143{
144 /* Use a bit more conservative approach than that for the QTabWidget,
145 * change the selection only if a selected child becomes invisible by
146 * changing the page. */
147 QWizard *wizard = static_cast<QWizard *>(parent());
148 QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(w: wizard);
149 if (!fw)
150 return;
151 QDesignerFormWindowCursorInterface *cursor = fw->cursor();
152 const int selCount = cursor->selectedWidgetCount();
153 for (int i = 0; i < selCount; i++) {
154 if (!cursor->selectedWidget(index: i)->isVisible()) {
155 fw->clearSelection(changePropertyDisplay: false);
156 fw->selectWidget(w: wizard, select: true);
157 break;
158 }
159 }
160}
161
162// ---------------- WidgetFactory::Strings
163WidgetFactory::Strings::Strings() :
164 m_alignment(QStringLiteral("alignment")),
165 m_bottomMargin(QStringLiteral("bottomMargin")),
166 m_geometry(QStringLiteral("geometry")),
167 m_leftMargin(QStringLiteral("leftMargin")),
168 m_line(QStringLiteral("Line")),
169 m_objectName(QStringLiteral("objectName")),
170 m_spacerName(QStringLiteral("spacerName")),
171 m_orientation(QStringLiteral("orientation")),
172 m_qAction(QStringLiteral("QAction")),
173 m_qButtonGroup(QStringLiteral("QButtonGroup")),
174 m_qAxWidget(QStringLiteral("QAxWidget")),
175 m_qDialog(QStringLiteral("QDialog")),
176 m_qDockWidget(QStringLiteral("QDockWidget")),
177 m_qLayoutWidget(QStringLiteral("QLayoutWidget")),
178 m_qMenu(QStringLiteral("QMenu")),
179 m_qMenuBar(QStringLiteral("QMenuBar")),
180 m_qWidget(QStringLiteral("QWidget")),
181 m_rightMargin(QStringLiteral("rightMargin")),
182 m_sizeHint(QStringLiteral("sizeHint")),
183 m_spacer(QStringLiteral("Spacer")),
184 m_text(QStringLiteral("text")),
185 m_title(QStringLiteral("title")),
186 m_topMargin(QStringLiteral("topMargin")),
187 m_windowIcon(QStringLiteral("windowIcon")),
188 m_windowTitle(QStringLiteral("windowTitle"))
189{
190}
191// ---------------- WidgetFactory
192const char *WidgetFactory::disableStyleCustomPaintingPropertyC = "_q_custom_style_disabled";
193
194WidgetFactory::WidgetFactory(QDesignerFormEditorInterface *core, QObject *parent)
195 : QDesignerWidgetFactoryInterface(parent),
196 m_core(core),
197 m_formWindow(nullptr),
198 m_currentStyle(nullptr)
199{
200}
201
202WidgetFactory::~WidgetFactory() = default;
203
204QDesignerFormWindowInterface *WidgetFactory::currentFormWindow(QDesignerFormWindowInterface *fw)
205{
206 QDesignerFormWindowInterface *was = m_formWindow;
207 m_formWindow = fw;
208 return was;
209}
210
211void WidgetFactory::loadPlugins()
212{
213 m_customFactory.clear();
214
215 const auto &lst = m_core->pluginManager()->registeredCustomWidgets();
216 for (QDesignerCustomWidgetInterface *c : lst)
217 m_customFactory.insert(key: c->name(), value: c);
218}
219
220// Convencience to create non-widget objects. Returns 0 if unknown
221QObject* WidgetFactory::createObject(const QString &className, QObject* parent) const
222{
223 if (className.isEmpty()) {
224 qWarning(msg: "** WARNING %s called with an empty class name", Q_FUNC_INFO);
225 return nullptr;
226 }
227 if (className == m_strings.m_qAction)
228 return new QAction(parent);
229 if (className == m_strings.m_qButtonGroup)
230 return new QButtonGroup(parent);
231 return nullptr;
232}
233
234// Check for mismatched class names in plugins, which is hard to track.
235static bool classNameMatches(const QObject *created, const QString &className)
236{
237#ifdef Q_OS_WIN
238 // Perform literal comparison first for QAxWidget, for which a meta object hack is in effect.
239 if (isAxWidget(created))
240 return true;
241#endif
242 const char *createdClassNameC = created->metaObject()->className();
243 const QByteArray classNameB = className.toUtf8();
244 const char *classNameC = classNameB.constData();
245 if (qstrcmp(str1: createdClassNameC, str2: classNameC) == 0 || created->inherits(classname: classNameC))
246 return true;
247 // QTBUG-53984: QWebEngineView property dummy
248 if (classNameB == "QWebEngineView" && qstrcmp(str1: createdClassNameC, str2: "fake::QWebEngineView") == 0)
249 return true;
250 return false;
251}
252
253QWidget* WidgetFactory::createCustomWidget(const QString &className, QWidget *parentWidget, bool *creationError) const
254{
255 *creationError = false;
256 CustomWidgetFactoryMap::const_iterator it = m_customFactory.constFind(key: className);
257 if (it == m_customFactory.constEnd())
258 return nullptr;
259
260 QDesignerCustomWidgetInterface *factory = it.value();
261 QWidget *rc = factory->createWidget(parent: parentWidget);
262 // shouldn't happen
263 if (!rc) {
264 *creationError = true;
265 designerWarning(message: tr(s: "The custom widget factory registered for widgets of class %1 returned 0.").arg(a: className));
266 return nullptr;
267 }
268 // Figure out the base class unless it is known
269 static QSet<QString> knownCustomClasses;
270 if (!knownCustomClasses.contains(value: className)) {
271 QDesignerWidgetDataBaseInterface *wdb = m_core->widgetDataBase();
272 const int widgetInfoIndex = wdb->indexOfObject(object: rc, resolveName: false);
273 if (widgetInfoIndex != -1) {
274 if (wdb->item(index: widgetInfoIndex)->extends().isEmpty()) {
275 const QDesignerMetaObjectInterface *mo = core()->introspection()->metaObject(object: rc)->superClass();
276 // If we hit on a 'Q3DesignerXXWidget' that claims to be a 'Q3XXWidget', step
277 // over.
278 if (mo && mo->className() == className)
279 mo = mo->superClass();
280 while (mo != nullptr) {
281 if (core()->widgetDataBase()->indexOfClassName(className: mo->className()) != -1) {
282 wdb->item(index: widgetInfoIndex)->setExtends(mo->className());
283 break;
284 }
285 mo = mo->superClass();
286 }
287 }
288 knownCustomClasses.insert(value: className);
289 }
290 }
291 // Since a language plugin may lie about its names, like Qt Jambi
292 // does, return immediately here...
293 QDesignerLanguageExtension *lang =
294 qt_extension<QDesignerLanguageExtension *>(manager: m_core->extensionManager(), object: m_core);
295 if (lang)
296 return rc;
297
298 // Check for mismatched class names which is hard to track.
299 if (!classNameMatches(created: rc, className)) {
300 designerWarning(message: tr(s: "A class name mismatch occurred when creating a widget using the custom widget factory registered for widgets of class %1."
301 " It returned a widget of class %2.")
302 .arg(args: className, args: QString::fromUtf8(str: rc->metaObject()->className())));
303 }
304 return rc;
305}
306
307
308QWidget *WidgetFactory::createWidget(const QString &widgetName, QWidget *parentWidget) const
309{
310 if (widgetName.isEmpty()) {
311 qWarning(msg: "** WARNING %s called with an empty class name", Q_FUNC_INFO);
312 return nullptr;
313 }
314 // Preview or for form window?
315 QDesignerFormWindowInterface *fw = m_formWindow;
316 if (! fw)
317 fw = QDesignerFormWindowInterface::findFormWindow(w: parentWidget);
318
319 QWidget *w = nullptr;
320 do {
321 // 1) custom. If there is an explicit failure(factory wants to indicate something is wrong),
322 // return 0, do not try to find fallback, which might be worse in the case of Q3 widget.
323 bool customWidgetCreationError;
324 w = createCustomWidget(className: widgetName, parentWidget, creationError: &customWidgetCreationError);
325 if (w)
326 break;
327 if (customWidgetCreationError)
328 return nullptr;
329
330 // 2) Special widgets
331 if (widgetName == m_strings.m_line) {
332 w = new Line(parentWidget);
333 } else if (widgetName == m_strings.m_qDockWidget) {
334 w = new QDesignerDockWidget(parentWidget);
335 } else if (widgetName == m_strings.m_qMenuBar) {
336 w = new QDesignerMenuBar(parentWidget);
337 } else if (widgetName == m_strings.m_qMenu) {
338 w = new QDesignerMenu(parentWidget);
339 } else if (widgetName == m_strings.m_spacer) {
340 w = new Spacer(parentWidget);
341 } else if (widgetName == m_strings.m_qLayoutWidget) {
342 w = fw ? new QLayoutWidget(fw, parentWidget) : new QWidget(parentWidget);
343 } else if (widgetName == m_strings.m_qDialog) {
344 if (fw) {
345 w = new QDesignerDialog(fw, parentWidget);
346 } else {
347 w = new QDialog(parentWidget);
348 }
349 } else if (widgetName == m_strings.m_qWidget) {
350 /* We want a 'QDesignerWidget' that draws a grid only for widget
351 * forms and container extension pages (not for preview and not
352 * for normal QWidget children on forms (legacy) */
353 if (fw && parentWidget) {
354 if (qt_extension<QDesignerContainerExtension*>(manager: m_core->extensionManager(), object: parentWidget)) {
355 w = new QDesignerWidget(fw, parentWidget);
356 } else {
357 if (parentWidget == fw->formContainer())
358 w = new QDesignerWidget(fw, parentWidget);
359 }
360 }
361 if (!w)
362 w = new QWidget(parentWidget);
363 }
364 if (w)
365 break;
366
367 // 3) table
368 const QByteArray widgetNameBA = widgetName.toUtf8();
369 const char *widgetNameC = widgetNameBA.constData();
370
371 if (w) { // symmetry for macro
372 }
373
374#define DECLARE_LAYOUT(L, C)
375#define DECLARE_COMPAT_WIDGET(W, C) /*DECLARE_WIDGET(W, C)*/
376#define DECLARE_WIDGET(W, C) else if (!qstrcmp(widgetNameC, #W)) { Q_ASSERT(w == 0); w = new W(parentWidget); }
377#define DECLARE_WIDGET_1(W, C) else if (!qstrcmp(widgetNameC, #W)) { Q_ASSERT(w == 0); w = new W(0, parentWidget); }
378
379#include <widgets.table>
380
381#undef DECLARE_COMPAT_WIDGET
382#undef DECLARE_LAYOUT
383#undef DECLARE_WIDGET
384#undef DECLARE_WIDGET_1
385
386 if (w)
387 break;
388 // 4) fallBack
389 const QString fallBackBaseClass = m_strings.m_qWidget;
390 QDesignerWidgetDataBaseInterface *db = core()->widgetDataBase();
391 QDesignerWidgetDataBaseItemInterface *item = db->item(index: db->indexOfClassName(className: widgetName));
392 if (item == nullptr) {
393 // Emergency: Create, derived from QWidget
394 QString includeFile = widgetName.toLower();
395 includeFile += QStringLiteral(".h");
396 item = appendDerived(db,className: widgetName, group: tr(s: "%1 Widget").arg(a: widgetName),baseClassName: fallBackBaseClass,
397 includeFile, promoted: true, custom: true);
398 Q_ASSERT(item);
399 }
400 QString baseClass = item->extends();
401 if (baseClass.isEmpty()) {
402 // Currently happens in the case of Q3-Support widgets
403 baseClass =fallBackBaseClass;
404 }
405 if (QWidget *promotedWidget = createWidget(widgetName: baseClass, parentWidget)) {
406 promoteWidget(core: core(), widget: promotedWidget, customClassName: widgetName);
407 return promotedWidget; // Do not initialize twice.
408 }
409 } while (false);
410
411 Q_ASSERT(w != nullptr);
412 if (m_currentStyle)
413 w->setStyle(m_currentStyle);
414 initializeCommon(object: w);
415 if (fw) { // form editor initialization
416 initialize(object: w);
417 } else { // preview-only initialization
418 initializePreview(object: w);
419 }
420 return w;
421}
422
423QString WidgetFactory::classNameOf(QDesignerFormEditorInterface *c, const QObject* o)
424{
425 if (o == nullptr)
426 return QString();
427
428 const char *className = o->metaObject()->className();
429 if (!o->isWidgetType())
430 return QLatin1String(className);
431 const QWidget *w = static_cast<const QWidget*>(o);
432 // check promoted before designer special
433 const QString customClassName = promotedCustomClassName(core: c, w: const_cast<QWidget*>(w));
434 if (!customClassName.isEmpty())
435 return customClassName;
436 if (qobject_cast<const QDesignerMenuBar*>(object: w))
437 return QStringLiteral("QMenuBar");
438 if (qobject_cast<const QDesignerMenu*>(object: w))
439 return QStringLiteral("QMenu");
440 if (qobject_cast<const QDesignerDockWidget*>(object: w))
441 return QStringLiteral("QDockWidget");
442 if (qobject_cast<const QDesignerDialog*>(object: w))
443 return QStringLiteral("QDialog");
444 if (qobject_cast<const QDesignerWidget*>(object: w))
445 return QStringLiteral("QWidget");
446#ifdef Q_OS_WIN
447 if (isAxWidget(w))
448 return QStringLiteral("QAxWidget");
449#endif
450 return QLatin1String(className);
451}
452
453QLayout *WidgetFactory::createUnmanagedLayout(QWidget *parentWidget, int type)
454{
455 switch (type) {
456 case LayoutInfo::HBox:
457 return new QHBoxLayout(parentWidget);
458 case LayoutInfo::VBox:
459 return new QVBoxLayout(parentWidget);
460 case LayoutInfo::Grid:
461 return new QGridLayout(parentWidget);
462 case LayoutInfo::Form:
463 return new QFormLayout(parentWidget);
464 default:
465 Q_ASSERT(0);
466 break;
467 }
468 return nullptr;
469}
470
471
472/*! Creates a layout on the widget \a widget of the type \a type
473 which can be \c HBox, \c VBox or \c Grid.
474*/
475
476QLayout *WidgetFactory::createLayout(QWidget *widget, QLayout *parentLayout, int type) const // ### (sizepolicy)
477{
478 QDesignerMetaDataBaseInterface *metaDataBase = core()->metaDataBase();
479
480 if (parentLayout == nullptr) {
481 QWidget *page = containerOfWidget(widget);
482 if (page) {
483 widget = page;
484 } else {
485 const QString msg =
486 tr(s: "The current page of the container '%1' (%2) could not be determined while creating a layout."
487 "This indicates an inconsistency in the ui-file, probably a layout being constructed on a container widget.")
488 .arg(args: widget->objectName(), args: classNameOf(c: core(), o: widget));
489 designerWarning(message: msg);
490 }
491 }
492
493 Q_ASSERT(metaDataBase->item(widget) != nullptr); // ensure the widget is managed
494
495 if (parentLayout == nullptr && metaDataBase->item(object: widget->layout()) == nullptr) {
496 parentLayout = widget->layout();
497 }
498
499 QWidget *parentWidget = parentLayout != nullptr ? nullptr : widget;
500
501 QLayout *layout = createUnmanagedLayout(parentWidget, type);
502 metaDataBase->add(object: layout); // add the layout in the MetaDataBase
503
504 QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(manager: core()->extensionManager(), object: layout);
505
506 if (sheet) {
507 sheet->setChanged(index: sheet->indexOf(name: m_strings.m_objectName), changed: true);
508 if (widget->inherits(classname: "QLayoutWidget")) {
509 sheet->setProperty(index: sheet->indexOf(name: m_strings.m_leftMargin), value: 0);
510 sheet->setProperty(index: sheet->indexOf(name: m_strings.m_topMargin), value: 0);
511 sheet->setProperty(index: sheet->indexOf(name: m_strings.m_rightMargin), value: 0);
512 sheet->setProperty(index: sheet->indexOf(name: m_strings.m_bottomMargin), value: 0);
513 }
514
515 const int index = sheet->indexOf(name: m_strings.m_alignment);
516 if (index != -1)
517 sheet->setChanged(index, changed: true);
518 }
519
520 if (metaDataBase->item(object: widget->layout()) == nullptr) {
521 Q_ASSERT(layout->parent() == nullptr);
522 QBoxLayout *box = qobject_cast<QBoxLayout*>(object: widget->layout());
523 if (!box) { // we support only unmanaged box layouts
524 const QString msg = tr(s: "Attempt to add a layout to a widget '%1' (%2) which already has an unmanaged layout of type %3.\n"
525 "This indicates an inconsistency in the ui-file.").
526 arg(args: widget->objectName(), args: classNameOf(c: core(), o: widget), args: classNameOf(c: core(), o: widget->layout()));
527 designerWarning(message: msg);
528 return nullptr;
529 }
530 box->addLayout(layout);
531 }
532
533 return layout;
534}
535
536/*! Returns the widget into which children should be inserted when \a
537 w is a container known to designer.
538
539 Usually, it is \a w itself, but there are exceptions (for example, a
540 tabwidget is known to designer as a container, but the child
541 widgets should be inserted into the current page of the
542 tabwidget. In this case, the current page of
543 the tabwidget would be returned.)
544 */
545QWidget* WidgetFactory::containerOfWidget(QWidget *w) const
546{
547 if (QDesignerContainerExtension *container = qt_extension<QDesignerContainerExtension*>(manager: core()->extensionManager(), object: w))
548 return container->widget(index: container->currentIndex());
549
550 return w;
551}
552
553/*! Returns the actual designer widget of the container \a w. This is
554 normally \a w itself, but it might be a parent or grand parent of \a w
555 (for example, when working with a tabwidget and \a w is the container which
556 contains and layouts children, but the actual widget known to
557 designer is the tabwidget which is the parent of \a w. In this case,
558 the tabwidget would be returned.)
559*/
560
561QWidget* WidgetFactory::widgetOfContainer(QWidget *w) const
562{
563 // ### cleanup
564 if (!w)
565 return nullptr;
566 if (w->parentWidget() && w->parentWidget()->parentWidget() &&
567 w->parentWidget()->parentWidget()->parentWidget() &&
568 qobject_cast<QToolBox*>(object: w->parentWidget()->parentWidget()->parentWidget()))
569 return w->parentWidget()->parentWidget()->parentWidget();
570
571 while (w != nullptr) {
572 if (core()->widgetDataBase()->isContainer(object: w) ||
573 (w && qobject_cast<QDesignerFormWindowInterface*>(object: w->parentWidget())))
574 return w;
575
576 w = w->parentWidget();
577 }
578
579 return w;
580}
581
582QDesignerFormEditorInterface *WidgetFactory::core() const
583{
584 return m_core;
585}
586
587// Necessary initializations for form editor/preview objects
588void WidgetFactory::initializeCommon(QWidget *widget) const
589{
590 // Apply style
591 if (m_currentStyle)
592 widget->setStyle(m_currentStyle);
593}
594
595// Necessary initializations for preview objects
596void WidgetFactory::initializePreview(QWidget *widget) const
597{
598
599 if (QStackedWidget *stackedWidget = qobject_cast<QStackedWidget*>(object: widget)) {
600 QStackedWidgetPreviewEventFilter::install(stackedWidget); // Add browse button only.
601 return;
602 }
603}
604
605// Necessary initializations for form editor objects
606void WidgetFactory::initialize(QObject *object) const
607{
608 // Indicate that this is a form object (for QDesignerFormWindowInterface::findFormWindow)
609 object->setProperty(name: formEditorDynamicProperty, value: QVariant(true));
610 QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(manager: m_core->extensionManager(), object);
611 if (!sheet)
612 return;
613
614 sheet->setChanged(index: sheet->indexOf(name: m_strings.m_objectName), changed: true);
615
616 if (!object->isWidgetType()) {
617 if (qobject_cast<QAction*>(object))
618 sheet->setChanged(index: sheet->indexOf(name: m_strings.m_text), changed: true);
619 return;
620 }
621
622 QWidget *widget = static_cast<QWidget*>(object);
623 const bool isMenu = qobject_cast<QMenu*>(object: widget);
624 const bool isMenuBar = !isMenu && qobject_cast<QMenuBar*>(object: widget);
625
626 widget->setAttribute(Qt::WA_TransparentForMouseEvents, on: false);
627 widget->setFocusPolicy((isMenu || isMenuBar) ? Qt::StrongFocus : Qt::NoFocus);
628
629 if (!isMenu)
630 sheet->setChanged(index: sheet->indexOf(name: m_strings.m_geometry), changed: true);
631
632 if (qobject_cast<Spacer*>(object: widget)) {
633 sheet->setChanged(index: sheet->indexOf(name: m_strings.m_spacerName), changed: true);
634 return;
635 }
636
637 const int o = sheet->indexOf(name: m_strings.m_orientation);
638 if (o != -1 && widget->inherits(classname: "QSplitter"))
639 sheet->setChanged(index: o, changed: true);
640
641 if (QToolBar *toolBar = qobject_cast<QToolBar*>(object: widget)) {
642 ToolBarEventFilter::install(tb: toolBar);
643 sheet->setVisible(index: sheet->indexOf(name: m_strings.m_windowTitle), b: true);
644 toolBar->setFloatable(false); // prevent toolbars from being dragged off
645 return;
646 }
647
648 if (qobject_cast<QDockWidget*>(object: widget)) {
649 sheet->setVisible(index: sheet->indexOf(name: m_strings.m_windowTitle), b: true);
650 sheet->setVisible(index: sheet->indexOf(name: m_strings.m_windowIcon), b: true);
651 return;
652 }
653
654 if (isMenu) {
655 sheet->setChanged(index: sheet->indexOf(name: m_strings.m_title), changed: true);
656 return;
657 }
658 // helpers
659 if (QToolBox *toolBox = qobject_cast<QToolBox*>(object: widget)) {
660 QToolBoxHelper::install(toolbox: toolBox);
661 return;
662 }
663 if (QStackedWidget *stackedWidget = qobject_cast<QStackedWidget*>(object: widget)) {
664 QStackedWidgetEventFilter::install(stackedWidget);
665 return;
666 }
667 if (QTabWidget *tabWidget = qobject_cast<QTabWidget*>(object: widget)) {
668 QTabWidgetEventFilter::install(tabWidget);
669 return;
670 }
671 // Prevent embedded line edits from getting focus
672 if (QAbstractSpinBox *asb = qobject_cast<QAbstractSpinBox *>(object: widget)) {
673 if (QLineEdit *lineEdit = static_cast<FriendlySpinBox*>(asb)->lineEdit())
674 lineEdit->setFocusPolicy(Qt::NoFocus);
675 return;
676 }
677 if (QComboBox *cb = qobject_cast<QComboBox *>(object: widget)) {
678 if (QFontComboBox *fcb = qobject_cast<QFontComboBox *>(object: widget)) {
679 fcb->lineEdit()->setFocusPolicy(Qt::NoFocus); // Always present
680 return;
681 }
682 cb->installEventFilter(filterObj: new ComboEventFilter(cb));
683 return;
684 }
685 if (QWizard *wz = qobject_cast<QWizard *>(object: widget)) {
686 WizardPageChangeWatcher *pw = new WizardPageChangeWatcher(wz);
687 Q_UNUSED(pw);
688 }
689}
690
691static inline QString classNameOfStyle(const QStyle *s)
692{
693 return QLatin1String(s->metaObject()->className());
694}
695
696QString WidgetFactory::styleName() const
697{
698 return classNameOfStyle(s: style());
699}
700
701static inline bool isApplicationStyle(const QString &styleName)
702{
703 return styleName.isEmpty() || styleName == classNameOfStyle(qApp->style());
704}
705
706void WidgetFactory::setStyleName(const QString &styleName)
707{
708 m_currentStyle = isApplicationStyle(styleName) ? nullptr : getStyle(styleName);
709}
710
711QStyle *WidgetFactory::style() const
712{
713 return m_currentStyle ? m_currentStyle : qApp->style();
714}
715
716QStyle *WidgetFactory::getStyle(const QString &styleName)
717{
718 if (isApplicationStyle(styleName))
719 return qApp->style();
720
721 StyleCache::iterator it = m_styleCache.find(key: styleName);
722 if (it == m_styleCache.end()) {
723 QStyle *style = QStyleFactory::create(styleName);
724 if (!style) {
725 const QString msg = tr(s: "Cannot create style '%1'.").arg(a: styleName);
726 designerWarning(message: msg);
727 return nullptr;
728 }
729 it = m_styleCache.insert(key: styleName, value: style);
730 }
731 return it.value();
732}
733
734void WidgetFactory::applyStyleTopLevel(const QString &styleName, QWidget *w)
735{
736 if (QStyle *style = getStyle(styleName))
737 applyStyleToTopLevel(style, widget: w);
738}
739
740void WidgetFactory::applyStyleToTopLevel(QStyle *style, QWidget *widget)
741{
742 if (!style)
743 return;
744 const QPalette standardPalette = style->standardPalette();
745 if (widget->style() == style && widget->palette() == standardPalette)
746 return;
747
748 widget->setStyle(style);
749 widget->setPalette(standardPalette);
750 const QWidgetList lst = widget->findChildren<QWidget*>();
751 const QWidgetList::const_iterator cend = lst.constEnd();
752 for (QWidgetList::const_iterator it = lst.constBegin(); it != cend; ++it)
753 (*it)->setStyle(style);
754}
755
756// Check for 'interactor' click on a tab bar,
757// which can appear within a QTabWidget or as a standalone widget.
758
759static bool isTabBarInteractor(const QTabBar *tabBar)
760{
761 // Tabbar embedded in Q(Designer)TabWidget, ie, normal tab widget case
762 if (qobject_cast<const QTabWidget*>(object: tabBar->parentWidget()))
763 return true;
764
765 // Standalone tab bar on the form. Return true for tab rect areas
766 // only to allow the user to select the tab bar by clicking outside the actual tabs.
767 const int count = tabBar->count();
768 if (count == 0)
769 return false;
770
771 // click into current tab: No Interaction
772 const int currentIndex = tabBar->currentIndex();
773 const QPoint pos = tabBar->mapFromGlobal(QCursor::pos());
774 if (tabBar->tabRect(index: currentIndex).contains(p: pos))
775 return false;
776
777 // click outside: No Interaction
778 const QRect geometry = QRect(QPoint(0, 0), tabBar->size());
779 if (!geometry.contains(p: pos))
780 return false;
781 // click into another tab: Let's interact, switch tabs.
782 for (int i = 0; i < count; i++)
783 if (tabBar->tabRect(index: i).contains(p: pos))
784 return true;
785 return false;
786}
787
788static bool isPassiveInteractorHelper(const QWidget *widget)
789{
790 static const QString qtPassive = QStringLiteral("__qt__passive_");
791 static const QString qtMainWindowSplitter = QStringLiteral("qt_qmainwindow_extended_splitter");
792
793 if (qobject_cast<const QMenuBar*>(object: widget)
794#if QT_CONFIG(sizegrip)
795 || qobject_cast<const QSizeGrip*>(object: widget)
796#endif
797 || qobject_cast<const QMdiSubWindow*>(object: widget)
798 || qobject_cast<const QToolBar*>(object: widget)) {
799 return true;
800 }
801
802 if (qobject_cast<const QAbstractButton*>(object: widget)) {
803 auto parent = widget->parent();
804 if (qobject_cast<const QTabBar*>(object: parent) || qobject_cast<const QToolBox*>(object: parent))
805 return true;
806 } else if (const auto tabBar = qobject_cast<const QTabBar*>(object: widget)) {
807 if (isTabBarInteractor(tabBar))
808 return true;
809 } else if (qobject_cast<const QScrollBar*>(object: widget)) {
810 // A scroll bar is an interactor on a QAbstractScrollArea only.
811 if (auto parent = widget->parentWidget()) {
812 const QString objectName = parent->objectName();
813 static const QString scrollAreaVContainer = QStringLiteral("qt_scrollarea_vcontainer");
814 static const QString scrollAreaHContainer = QStringLiteral("qt_scrollarea_hcontainer");
815 if (objectName == scrollAreaVContainer || objectName == scrollAreaHContainer)
816 return true;
817 }
818 } else if (qstrcmp(str1: widget->metaObject()->className(), str2: "QDockWidgetTitle") == 0) {
819 return true;
820 } else if (qstrcmp(str1: widget->metaObject()->className(), str2: "QWorkspaceTitleBar") == 0) {
821 return true;
822 }
823 const QString &name = widget->objectName();
824 return name.startsWith(s: qtPassive) || name == qtMainWindowSplitter;
825}
826
827bool WidgetFactory::isPassiveInteractor(QWidget *widget)
828{
829 static bool lastWasAPassiveInteractor = false;
830 static QPointer<QWidget> lastPassiveInteractor;
831
832 if (!lastPassiveInteractor.isNull() && lastPassiveInteractor.data() == widget)
833 return lastWasAPassiveInteractor;
834
835 // if a popup is open, we have to make sure that this one is closed,
836 // else X might do funny things
837 if (QApplication::activePopupWidget() || widget == nullptr)
838 return true;
839
840 lastWasAPassiveInteractor = isPassiveInteractorHelper(widget);
841 lastPassiveInteractor = widget;
842
843 return lastWasAPassiveInteractor;
844}
845
846void WidgetFactory::formWindowAdded(QDesignerFormWindowInterface *formWindow)
847{
848 setFormWindowStyle(formWindow);
849}
850
851void WidgetFactory::activeFormWindowChanged(QDesignerFormWindowInterface *formWindow)
852{
853 setFormWindowStyle(formWindow);
854}
855
856void WidgetFactory::setFormWindowStyle(QDesignerFormWindowInterface *formWindow)
857{
858 if (FormWindowBase *fwb = qobject_cast<FormWindowBase *>(object: formWindow))
859 setStyleName(fwb->styleName());
860}
861
862bool WidgetFactory::isFormEditorObject(const QObject *object)
863{
864 return object->property(name: formEditorDynamicProperty).isValid();
865}
866} // namespace qdesigner_internal
867
868QT_END_NAMESPACE
869
870#include "widgetfactory.moc"
871

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