Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
abstractintegration.cpp
Go to the documentation of this file.
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
12#include "propertysheet.h"
15
16#include <qtresourcemodel_p.h>
17
18#include <qdesigner_propertycommand_p.h>
19#include <qdesigner_propertyeditor_p.h>
20#include <qdesigner_objectinspector_p.h>
21#include <qdesigner_utils_p.h>
22#include <widgetdatabase_p.h>
23#include <pluginmanager_p.h>
24#include <widgetfactory_p.h>
25#include <qdesigner_widgetbox_p.h>
26#include <qtgradientmanager_p.h>
27#include <qtgradientutils_p.h>
28#include <qtresourcemodel_p.h>
29
30#include <QtCore/qvariant.h>
31#include <QtCore/qfile.h>
32#include <QtCore/qdir.h>
33#include <QtCore/qlibraryinfo.h>
34
35#include <QtCore/qdebug.h>
36
38
39using namespace Qt::StringLiterals;
40
41/*!
42 \class QDesignerIntegrationInterface
43
44 \brief The QDesignerIntegrationInterface glues together parts of \QD
45 and allows for overwriting functionality for IDE integration.
46
47 \internal
48
49 \inmodule QtDesigner
50
51 \sa QDesignerFormEditorInterface
52*/
53
54/*!
55 \enum QDesignerIntegrationInterface::ResourceFileWatcherBehaviour
56
57 \internal
58
59 This enum describes if and how resource files are watched.
60
61 \value NoResourceFileWatcher Do not watch for changes
62 \value ReloadResourceFileSilently Reload files silently.
63 \value PromptToReloadResourceFile Prompt the user to reload.
64*/
65
66/*!
67 \enum QDesignerIntegrationInterface::FeatureFlag
68
69 \internal
70
71 This enum describes the features that are available and can be
72 controlled by the integration.
73
74 \value ResourceEditorFeature The resource editor is enabled.
75 \value SlotNavigationFeature Provide context menu entry offering 'Go to slot'.
76 \value DefaultWidgetActionFeature Trigger the preferred action of
77 QDesignerTaskMenuExtension when widget is activated.
78 \value DefaultFeature Default for \QD
79
80 \sa hasFeature(), features()
81*/
82
83/*!
84 \property QDesignerIntegrationInterface::headerSuffix
85
86 Returns the suffix of the header of promoted widgets.
87*/
88
89/*!
90 \property QDesignerIntegrationInterface::headerLowercase
91
92 Returns whether headers of promoted widgets should be lower-cased (as the user types the class name).
93*/
94
95/*!
96 \property QDesignerIntegrationInterface::qtVersion
97
98 Determines the Qt version used when writing out forms.
99
100 \since 6.9
101*/
102
103/*!
104 \fn virtual void QDesignerIntegrationInterface::updateSelection()
105
106 \brief Sets the selected widget of the form window in various components.
107
108 \internal
109
110 In IDE integrations, the method can be overwritten to move the selection handles
111 of the form's main window, should it be selected.
112*/
113
114/*!
115 \fn virtual QWidget *QDesignerIntegrationInterface::containerWindow(QWidget *widget) const
116
117 \brief Returns the outer window containing a form for applying main container geometries.
118
119 \internal
120
121 Should be implemented by IDE integrations.
122*/
123
124/*!
125 \fn virtual QDesignerResourceBrowserInterface *QDesignerIntegrationInterface::createResourceBrowser(QWidget *parent = 0)
126
127 \brief Creates a resource browser depending on IDE integration.
128
129 \internal
130
131 \note Language integration takes precedence.
132*/
133
134/*!
135 \fn virtual void QDesignerIntegrationInterface::updateProperty(const QString &name, const QVariant &value, bool enableSubPropertyHandling)
136
137 \brief Triggered by the property editor to update a property value.
138
139 \internal
140
141 If a different property editor is used, it should invoke this function.
142*/
143
144/*!
145 \fn virtual void QDesignerIntegrationInterface::updateProperty(const QString &name, const QVariant &value)
146
147 \brief Triggered by the property editor to update a property value without subproperty handling.
148
149 \internal
150
151 If a different property editor is used, it should invoke this function.
152*/
153
154/*!
155 \fn virtual void QDesignerIntegrationInterface::resetProperty(const QString &name)
156
157 \brief Triggered by the property editor to reset a property value.
158
159 \internal
160
161 If a different property editor is used, it should invoke this function.
162*/
163
164/*!
165 \fn virtual void QDesignerIntegrationInterface::addDynamicProperty(const QString &name, const QVariant &value)
166
167 \brief Triggered by the property editor to add a dynamic property value.
168
169 \internal
170
171 If a different property editor is used, it should invoke this function.
172*/
173
174/*!
175 \fn virtual void QDesignerIntegrationInterface::removeDynamicProperty(const QString &name)
176
177 \brief Triggered by the property editor to remove a dynamic property.
178
179 \internal
180
181 If a different property editor is used, it should invoke this function.
182*/
183
184/*!
185 \fn virtual void QDesignerIntegrationInterface::updateActiveFormWindow(QDesignerFormWindowInterface *formWindow)
186
187 \brief Sets up the active form window.
188
189 \internal
190*/
191
192/*!
193 \fn virtual void QDesignerIntegrationInterface::setupFormWindow(QDesignerFormWindowInterface *formWindow)
194
195 \brief Sets up the new form window.
196
197 \internal
198*/
199
200/*!
201 \fn virtual void QDesignerIntegrationInterface::updateCustomWidgetPlugins()
202
203 \brief Triggers a reload of the custom widget plugins.
204
205 \internal
206*/
207
209{
210public:
211 QDesignerIntegrationInterfacePrivate(QDesignerFormEditorInterface *core) :
212 m_core(core) {}
213
214 QDesignerFormEditorInterface *m_core;
216};
217
218QDesignerIntegrationInterface::QDesignerIntegrationInterface(QDesignerFormEditorInterface *core, QObject *parent)
219 : QObject(parent), d(new QDesignerIntegrationInterfacePrivate(core))
220{
221 core->setIntegration(this);
222}
223
224QDesignerIntegrationInterface::~QDesignerIntegrationInterface() = default;
225
226QDesignerFormEditorInterface *QDesignerIntegrationInterface::core() const
227{
228 return d->m_core;
229}
230
231bool QDesignerIntegrationInterface::hasFeature(Feature f) const
232{
233 return (features() & f) != 0;
234}
235
236QVersionNumber QDesignerIntegrationInterface::qtVersion() const
237{
238 return d->qtVersion;
239}
240
241void QDesignerIntegrationInterface::setQtVersion(const QVersionNumber &qtVersion)
242{
243 d->qtVersion = qtVersion;
244}
245
246void QDesignerIntegrationInterface::emitObjectNameChanged(QDesignerFormWindowInterface *formWindow, QObject *object, const QString &newName, const QString &oldName)
247{
248 emit objectNameChanged(formWindow, object, newName, oldName);
249}
250
251void QDesignerIntegrationInterface::emitNavigateToSlot(const QString &objectName,
252 const QString &signalSignature,
253 const QStringList &parameterNames)
254{
255 emit navigateToSlot(objectName, signalSignature, parameterNames);
256}
257
258void QDesignerIntegrationInterface::emitNavigateToSlot(const QString &slotSignature)
259{
260 emit navigateToSlot(slotSignature);
261}
262
263void QDesignerIntegrationInterface::emitHelpRequested(const QString &manual, const QString &document)
264{
265 emit helpRequested(manual, document);
266}
267
268/*!
269 \class QDesignerIntegration
270
271 \brief The QDesignerIntegration class is \QD's implementation of
272 QDesignerIntegrationInterface.
273
274 \internal
275
276 \inmodule QtDesigner
277
278 IDE integrations should register classes derived from QDesignerIntegration
279 with QDesignerFormEditorInterface.
280*/
281
282namespace qdesigner_internal {
283
285public:
286 explicit QDesignerIntegrationPrivate(QDesignerIntegration *qq);
287
289
290 // Load plugins into widget database and factory.
291 static void initializePlugins(QDesignerFormEditorInterface *formEditor);
292
293 QString contextHelpId() const;
294
295 void updateProperty(const QString &name, const QVariant &value, bool enableSubPropertyHandling);
296 void resetProperty(const QString &name);
297 void addDynamicProperty(const QString &name, const QVariant &value);
298 void removeDynamicProperty(const QString &name);
299
300 void setupFormWindow(QDesignerFormWindowInterface *formWindow);
303
305 void getSelection(qdesigner_internal::Selection &s);
307
308 QDesignerIntegration *q;
315};
316
326
328{
329 //
330 // integrate the `Form Editor component'
331 //
332
333 // Extensions
334 QDesignerFormEditorInterface *core = q->core();
335 if (QDesignerPropertyEditor *designerPropertyEditor= qobject_cast<QDesignerPropertyEditor *>(core->propertyEditor())) {
336 QObject::connect(designerPropertyEditor, &QDesignerPropertyEditor::propertyValueChanged,
337 q, QOverload<const QString &, const QVariant &, bool>::of(&QDesignerIntegration::updateProperty));
338 QObject::connect(designerPropertyEditor, &QDesignerPropertyEditor::resetProperty,
339 q, &QDesignerIntegration::resetProperty);
340 QObject::connect(designerPropertyEditor, &QDesignerPropertyEditor::addDynamicProperty,
341 q, &QDesignerIntegration::addDynamicProperty);
342 QObject::connect(designerPropertyEditor, &QDesignerPropertyEditor::removeDynamicProperty,
343 q, &QDesignerIntegration::removeDynamicProperty);
344 }
345
346 QObject::connect(core->formWindowManager(), &QDesignerFormWindowManagerInterface::formWindowAdded,
347 q, &QDesignerIntegrationInterface::setupFormWindow);
348
349 QObject::connect(core->formWindowManager(), &QDesignerFormWindowManagerInterface::activeFormWindowChanged,
350 q, &QDesignerIntegrationInterface::updateActiveFormWindow);
351
352 m_gradientManager = new QtGradientManager(q);
353 core->setGradientManager(m_gradientManager);
354
355 const QString gradientsFile = u"/gradients.xml"_s;
356 m_gradientsPath = dataDirectory() + gradientsFile;
357
358 // Migrate from legacy to standard data directory in Qt 7
359 // ### FIXME Qt 8: Remove (QTBUG-96005)
360#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
361 const QString source = QFileInfo::exists(m_gradientsPath)
362 ? m_gradientsPath : legacyDataDirectory() + gradientsFile;
363#else
364 const QString source = m_gradientsPath;
365#endif
366
367 QFile f(source);
368 if (f.open(QIODevice::ReadOnly)) {
369 QtGradientUtils::restoreState(m_gradientManager, QString::fromLatin1(f.readAll()));
370 f.close();
371 } else {
372 QFile defaultGradients(u":/qt-project.org/designer/defaultgradients.xml"_s);
373 if (defaultGradients.open(QIODevice::ReadOnly)) {
374 QtGradientUtils::restoreState(m_gradientManager, QString::fromLatin1(defaultGradients.readAll()));
375 defaultGradients.close();
376 }
377 }
378
379 if (WidgetDataBase *widgetDataBase = qobject_cast<WidgetDataBase*>(core->widgetDataBase()))
380 widgetDataBase->grabStandardWidgetBoxIcons();
381}
382
383void QDesignerIntegrationPrivate::updateProperty(const QString &name, const QVariant &value, bool enableSubPropertyHandling)
384{
385 QDesignerFormWindowInterface *formWindow = q->core()->formWindowManager()->activeFormWindow();
386 if (!formWindow)
387 return;
388
389 Selection selection;
390 getSelection(selection);
391 if (selection.empty())
392 return;
393
394 SetPropertyCommand *cmd = new SetPropertyCommand(formWindow);
395 // find a reference object to compare to and to find the right group
396 if (cmd->init(selection.selection(), name, value, propertyEditorObject(), enableSubPropertyHandling)) {
397 formWindow->commandHistory()->push(cmd);
398 } else {
399 delete cmd;
400 qDebug() << "Unable to set property " << name << '.';
401 }
402}
403
405{
406 QDesignerFormWindowInterface *formWindow = q->core()->formWindowManager()->activeFormWindow();
407 if (!formWindow)
408 return;
409
410 Selection selection;
411 getSelection(selection);
412 if (selection.empty())
413 return;
414
415 ResetPropertyCommand *cmd = new ResetPropertyCommand(formWindow);
416 // find a reference object to find the right group
417 if (cmd->init(selection.selection(), name, propertyEditorObject())) {
418 formWindow->commandHistory()->push(cmd);
419 } else {
420 delete cmd;
421 qDebug() << "** WARNING Unable to reset property " << name << '.';
422 }
423}
424
425void QDesignerIntegrationPrivate::addDynamicProperty(const QString &name, const QVariant &value)
426{
427 QDesignerFormWindowInterface *formWindow = q->core()->formWindowManager()->activeFormWindow();
428 if (!formWindow)
429 return;
430
431 Selection selection;
432 getSelection(selection);
433 if (selection.empty())
434 return;
435
436 AddDynamicPropertyCommand *cmd = new AddDynamicPropertyCommand(formWindow);
437 if (cmd->init(selection.selection(), propertyEditorObject(), name, value)) {
438 formWindow->commandHistory()->push(cmd);
439 } else {
440 delete cmd;
441 qDebug() << "** WARNING Unable to add dynamic property " << name << '.';
442 }
443}
444
446{
447 QDesignerFormWindowInterface *formWindow = q->core()->formWindowManager()->activeFormWindow();
448 if (!formWindow)
449 return;
450
451 Selection selection;
452 getSelection(selection);
453 if (selection.empty())
454 return;
455
456 RemoveDynamicPropertyCommand *cmd = new RemoveDynamicPropertyCommand(formWindow);
457 if (cmd->init(selection.selection(), propertyEditorObject(), name)) {
458 formWindow->commandHistory()->push(cmd);
459 } else {
460 delete cmd;
461 qDebug() << "** WARNING Unable to remove dynamic property " << name << '.';
462 }
463
464}
465
466void QDesignerIntegrationPrivate::setupFormWindow(QDesignerFormWindowInterface *formWindow)
467{
468 QObject::connect(formWindow, &QDesignerFormWindowInterface::selectionChanged,
469 q, &QDesignerIntegrationInterface::updateSelection);
470}
471
473{
474 QDesignerFormEditorInterface *core = q->core();
475 QDesignerFormWindowInterface *formWindow = core->formWindowManager()->activeFormWindow();
476 QWidget *selection = nullptr;
477
478 if (formWindow) {
479 selection = formWindow->cursor()->current();
480 }
481
482 if (QDesignerActionEditorInterface *actionEditor = core->actionEditor())
483 actionEditor->setFormWindow(formWindow);
484
485 if (QDesignerPropertyEditorInterface *propertyEditor = core->propertyEditor())
486 propertyEditor->setObject(selection);
487
488 if (QDesignerObjectInspectorInterface *objectInspector = core->objectInspector())
489 objectInspector->setFormWindow(formWindow);
490
491}
492
494{
495 // Find the parent window to apply a geometry to.
496 while (widget) {
497 if (widget->isWindow())
498 break;
499 if (!qstrcmp(widget->metaObject()->className(), "QMdiSubWindow"))
500 break;
501
502 widget = widget->parentWidget();
503 }
504
505 return widget;
506}
507
509{
511 // Get multiselection from object inspector
514 // Action editor puts actions that are not on the form yet
515 // into the property editor only.
516 if (s.empty())
519
520 } else {
521 // Just in case someone plugs in an old-style object inspector: Emulate selection
522 s.clear();
524 if (!formWindow)
525 return;
526
528 if (object->isWidgetType()) {
529 QWidget *widget = static_cast<QWidget*>(object);
533 } else {
535 }
536 } else {
538 }
539 }
540}
541
543{
544 if (QDesignerPropertyEditorInterface *propertyEditor = q->core()->propertyEditor())
545 return propertyEditor->object();
546 return nullptr;
547}
548
549// Load plugins into widget database and factory.
550void QDesignerIntegrationPrivate::initializePlugins(QDesignerFormEditorInterface *formEditor)
551{
552 // load the plugins
553 qdesigner_internal::WidgetDataBase *widgetDataBase = qobject_cast<qdesigner_internal::WidgetDataBase*>(formEditor->widgetDataBase());
554 if (widgetDataBase) {
555 widgetDataBase->loadPlugins();
556 }
557
558 if (qdesigner_internal::WidgetFactory *widgetFactory = qobject_cast<qdesigner_internal::WidgetFactory*>(formEditor->widgetFactory())) {
559 widgetFactory->loadPlugins();
560 }
561
562 if (widgetDataBase) {
563 widgetDataBase->grabDefaultPropertyValues();
564 }
565}
566
568{
569 QDesignerFormEditorInterface *formEditor = q->core();
570 if (QDesignerPluginManager *pm = formEditor->pluginManager())
571 pm->registerNewPlugins();
572
573 initializePlugins(formEditor);
574
575 // Do not just reload the last file as the WidgetBox merges the compiled-in resources
576 // and $HOME/.designer/widgetbox.xml. This would also double the scratchpad.
577 if (QDesignerWidgetBox *wb = qobject_cast<QDesignerWidgetBox*>(formEditor->widgetBox())) {
578 const QDesignerWidgetBox::LoadMode oldLoadMode = wb->loadMode();
579 wb->setLoadMode(QDesignerWidgetBox::LoadCustomWidgetsOnly);
580 wb->load();
581 wb->setLoadMode(oldLoadMode);
582 }
583}
584
585static QString fixHelpClassName(const QString &className)
586{
587 // ### generalize using the Widget Data Base
588 if (className == "Line"_L1)
589 return u"QFrame"_s;
590 if (className == "Spacer"_L1)
591 return u"QSpacerItem"_s;
592 if (className == "QLayoutWidget"_L1)
593 return u"QLayout"_s;
594 return className;
595}
596
597// Return class in which the property is defined
598static QString classForProperty(QDesignerFormEditorInterface *core,
599 QObject *object,
600 const QString &property)
601{
602 if (const QDesignerPropertySheetExtension *ps = qt_extension<QDesignerPropertySheetExtension *>(core->extensionManager(), object)) {
603 const int index = ps->indexOf(property);
604 if (index >= 0)
605 return ps->propertyGroup(index);
606 }
607 return QString();
608}
609
611{
612 QDesignerFormEditorInterface *core = q->core();
613 QObject *currentObject = core->propertyEditor()->object();
614 if (!currentObject)
615 return QString();
616 // Return a help index id consisting of "class::property"
617 QString className;
618 QString currentPropertyName = core->propertyEditor()->currentPropertyName();
619 if (!currentPropertyName.isEmpty())
620 className = classForProperty(core, currentObject, currentPropertyName);
621 if (className.isEmpty()) {
622 currentPropertyName.clear(); // We hit on some fake property.
623 className = qdesigner_internal::WidgetFactory::classNameOf(core, currentObject);
624 }
625 QString helpId = fixHelpClassName(className);
626 if (!currentPropertyName.isEmpty()) {
627 helpId += "::"_L1;
628 helpId += currentPropertyName;
629 }
630 return helpId;
631}
632
633} // namespace qdesigner_internal
634
635// -------------- QDesignerIntegration
636// As of 4.4, the header will be distributed with the Eclipse plugin.
637
638QDesignerIntegration::QDesignerIntegration(QDesignerFormEditorInterface *core, QObject *parent) :
639 QDesignerIntegrationInterface(core, parent),
640 d(new qdesigner_internal::QDesignerIntegrationPrivate(this))
641{
642 d->initialize();
643}
644
645QDesignerIntegration::~QDesignerIntegration()
646{
647 QFile f(d->m_gradientsPath);
648 if (f.open(QIODevice::WriteOnly)) {
649 f.write(QtGradientUtils::saveState(d->m_gradientManager).toUtf8());
650 f.close();
651 }
652}
653
654QString QDesignerIntegration::headerSuffix() const
655{
656 return d->headerSuffix;
657}
658
659void QDesignerIntegration::setHeaderSuffix(const QString &headerSuffix)
660{
661 d->headerSuffix = headerSuffix;
662}
663
664bool QDesignerIntegration::isHeaderLowercase() const
665{
666 return d->headerLowercase;
667}
668
669void QDesignerIntegration::setHeaderLowercase(bool headerLowercase)
670{
671 d->headerLowercase = headerLowercase;
672}
673
674QDesignerIntegrationInterface::Feature QDesignerIntegration::features() const
675{
676 return d->m_features;
677}
678
679void QDesignerIntegration::setFeatures(Feature f)
680{
681 d->m_features = f;
682}
683
684QDesignerIntegrationInterface::ResourceFileWatcherBehaviour QDesignerIntegration::resourceFileWatcherBehaviour() const
685{
686 return d->m_resourceFileWatcherBehaviour;
687}
688
689void QDesignerIntegration::setResourceFileWatcherBehaviour(ResourceFileWatcherBehaviour behaviour)
690{
691 if (d->m_resourceFileWatcherBehaviour != behaviour) {
692 d->m_resourceFileWatcherBehaviour = behaviour;
693 core()->resourceModel()->setWatcherEnabled(behaviour != QDesignerIntegrationInterface::NoResourceFileWatcher);
694 }
695}
696
697void QDesignerIntegration::updateProperty(const QString &name, const QVariant &value, bool enableSubPropertyHandling)
698{
699 d->updateProperty(name, value, enableSubPropertyHandling);
700 emit propertyChanged(core()->formWindowManager()->activeFormWindow(), name, value);
701}
702
703void QDesignerIntegration::updateProperty(const QString &name, const QVariant &value)
704{
705 updateProperty(name, value, true);
706}
707
708void QDesignerIntegration::resetProperty(const QString &name)
709{
710 d->resetProperty(name);
711}
712
713void QDesignerIntegration::addDynamicProperty(const QString &name, const QVariant &value)
714{
715 d->addDynamicProperty(name, value);
716}
717
718void QDesignerIntegration::removeDynamicProperty(const QString &name)
719{
720 d->removeDynamicProperty(name);
721}
722
723void QDesignerIntegration::updateActiveFormWindow(QDesignerFormWindowInterface *)
724{
725 d->updateSelection();
726}
727
728void QDesignerIntegration::setupFormWindow(QDesignerFormWindowInterface *formWindow)
729{
730 d->setupFormWindow(formWindow);
731 connect(formWindow, &QDesignerFormWindowInterface::selectionChanged,
732 this, &QDesignerIntegrationInterface::updateSelection);
733}
734
735void QDesignerIntegration::updateSelection()
736{
737 d->updateSelection();
738}
739
740QWidget *QDesignerIntegration::containerWindow(QWidget *widget) const
741{
742 return d->containerWindow(widget);
743}
744
745// Load plugins into widget database and factory.
746void QDesignerIntegration::initializePlugins(QDesignerFormEditorInterface *formEditor)
747{
748 qdesigner_internal::QDesignerIntegrationPrivate::initializePlugins(formEditor);
749}
750
751void QDesignerIntegration::updateCustomWidgetPlugins()
752{
753 d->updateCustomWidgetPlugins();
754}
755
756QDesignerResourceBrowserInterface *QDesignerIntegration::createResourceBrowser(QWidget *)
757{
758 return nullptr;
759}
760
761QString QDesignerIntegration::contextHelpId() const
762{
763 return d->contextHelpId();
764}
765
766QT_END_NAMESPACE
QDesignerIntegrationInterfacePrivate(QDesignerFormEditorInterface *core)
QDesignerFormEditorInterface * m_core
virtual QString propertyGroup(int index) const =0
virtual int indexOf(const QString &name) const =0
friend class QWidget
Definition qpainter.h:421
void getSelection(qdesigner_internal::Selection &s)
static void initializePlugins(QDesignerFormEditorInterface *formEditor)
QDesignerIntegrationInterface::Feature m_features
void setupFormWindow(QDesignerFormWindowInterface *formWindow)
void addDynamicProperty(const QString &name, const QVariant &value)
void updateProperty(const QString &name, const QVariant &value, bool enableSubPropertyHandling)
QDesignerIntegrationInterface::ResourceFileWatcherBehaviour m_resourceFileWatcherBehaviour
Combined button and popup list for selecting options.
Auxiliary methods to store/retrieve settings.
static QString fixHelpClassName(const QString &className)
static QString classForProperty(QDesignerFormEditorInterface *core, QObject *object, const QString &property)