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 "morphmenu_p.h"
30#include "formwindowbase_p.h"
31#include "widgetfactory_p.h"
32#include "qdesigner_formwindowcommand_p.h"
33#include "qlayout_widget_p.h"
34#include "layoutinfo_p.h"
35#include "qdesigner_propertycommand_p.h"
36
37#include <QtDesigner/qextensionmanager.h>
38#include <QtDesigner/container.h>
39#include <QtDesigner/abstractformwindow.h>
40#include <QtDesigner/abstractformeditor.h>
41#include <QtDesigner/abstractlanguage.h>
42#include <QtDesigner/abstractwidgetdatabase.h>
43#include <QtDesigner/abstractmetadatabase.h>
44#include <QtDesigner/propertysheet.h>
45
46#include <QtWidgets/qwidget.h>
47#include <QtWidgets/qaction.h>
48#include <QtWidgets/qmenu.h>
49#include <QtWidgets/qapplication.h>
50#include <QtWidgets/qlayout.h>
51#include <QtWidgets/qundostack.h>
52#include <QtWidgets/qsplitter.h>
53
54#include <QtWidgets/qframe.h>
55#include <QtWidgets/qgroupbox.h>
56#include <QtWidgets/qtabwidget.h>
57#include <QtWidgets/qstackedwidget.h>
58#include <QtWidgets/qtoolbox.h>
59#include <QtWidgets/qabstractitemview.h>
60#include <QtWidgets/qabstractbutton.h>
61#include <QtWidgets/qabstractspinbox.h>
62#include <QtWidgets/qtextedit.h>
63#include <QtWidgets/qplaintextedit.h>
64#include <QtWidgets/qlabel.h>
65
66#include <QtCore/qstringlist.h>
67#include <QtCore/qmap.h>
68#include <QtCore/qvariant.h>
69#include <QtCore/qdebug.h>
70
71Q_DECLARE_METATYPE(QWidgetList)
72
73QT_BEGIN_NAMESPACE
74
75// Helpers for the dynamic properties that store Z/Widget order
76static const char *widgetOrderPropertyC = "_q_widgetOrder";
77static const char *zOrderPropertyC = "_q_zOrder";
78
79/* Morphing in Designer:
80 * It is possible to morph:
81 * - Non-Containers into similar widgets by category
82 * - Simple page containers into similar widgets or page-based containers with
83 * a single page (in theory also into a QLayoutWidget, but this might
84 * not always be appropriate).
85 * - Page-based containers into page-based containers or simple containers if
86 * they have just one page
87 * [Page based containers meaning here having a container extension]
88 * Morphing types are restricted to the basic Qt types. Morphing custom
89 * widgets is considered risky since they might have unmanaged layouts
90 * or the like.
91 *
92 * Requirements:
93 * - The widget must be on a non-laid out parent or in a layout managed
94 * by Designer
95 * - Its child widgets must be non-laid out or in a layout managed
96 * by Designer
97 * Note that child widgets can be
98 * - On the widget itself in the case of simple containers
99 * - On several pages in the case of page-based containers
100 * This is what is called 'childContainers' in the code (the widget itself
101 * or the list of container extension pages).
102 *
103 * The Morphing process encompasses:
104 * - Create a target widget and apply properties as far as applicable
105 * If the target widget has a container extension, add a sufficient
106 * number of pages.
107 * - Transferring the child widgets over to the new childContainers.
108 * In the case of a managed layout on a childContainer, this is simply
109 * set on the target childContainer, which is a new Qt 4.5
110 * functionality.
111 * - Replace the widget itself in the parent layout
112 */
113
114namespace qdesigner_internal {
115
116enum MorphCategory {
117 MorphCategoryNone, MorphSimpleContainer, MorphPageContainer, MorphItemView,
118 MorphButton, MorphSpinBox, MorphTextEdit
119};
120
121// Determine category of a widget
122static MorphCategory category(const QWidget *w)
123{
124 // Simple containers: Exact match
125 const QMetaObject *mo = w->metaObject();
126 if (mo == &QWidget::staticMetaObject || mo == &QFrame::staticMetaObject || mo == &QGroupBox::staticMetaObject || mo == &QLayoutWidget::staticMetaObject)
127 return MorphSimpleContainer;
128 if (mo == &QTabWidget::staticMetaObject || mo == &QStackedWidget::staticMetaObject || mo == &QToolBox::staticMetaObject)
129 return MorphPageContainer;
130 if (qobject_cast<const QAbstractItemView*>(object: w))
131 return MorphItemView;
132 if (qobject_cast<const QAbstractButton *>(object: w))
133 return MorphButton;
134 if (qobject_cast<const QAbstractSpinBox *>(object: w))
135 return MorphSpinBox;
136 if (qobject_cast<const QPlainTextEdit *>(object: w) || qobject_cast<const QTextEdit*>(object: w))
137 return MorphTextEdit;
138
139 return MorphCategoryNone;
140}
141
142/* Return the similar classes of a category. This is currently restricted
143 * to the known Qt classes with no precautions to parse the Widget Database
144 * (which is too risky, custom classes might have container extensions
145 * or non-managed layouts, etc.). */
146
147static QStringList classesOfCategory(MorphCategory cat)
148{
149 typedef QMap<MorphCategory, QStringList> CandidateCache;
150 static CandidateCache candidateCache;
151 CandidateCache::iterator it = candidateCache.find(akey: cat);
152 if (it == candidateCache.end()) {
153 it = candidateCache.insert(akey: cat, avalue: QStringList());
154 QStringList &l = it.value();
155 switch (cat) {
156 case MorphCategoryNone:
157 break;
158 case MorphSimpleContainer:
159 // Do not generally allow to morph into a layout.
160 // This can be risky in case of container pages,etc.
161 l << QStringLiteral("QWidget") << QStringLiteral("QFrame") << QStringLiteral("QGroupBox");
162 break;
163 case MorphPageContainer:
164 l << QStringLiteral("QTabWidget") << QStringLiteral("QStackedWidget") << QStringLiteral("QToolBox");
165 break;
166 case MorphItemView:
167 l << QStringLiteral("QListView") << QStringLiteral("QListWidget")
168 << QStringLiteral("QTreeView") << QStringLiteral("QTreeWidget")
169 << QStringLiteral("QTableView") << QStringLiteral("QTableWidget")
170 << QStringLiteral("QColumnView");
171 break;
172 case MorphButton:
173 l << QStringLiteral("QCheckBox") << QStringLiteral("QRadioButton")
174 << QStringLiteral("QPushButton") << QStringLiteral("QToolButton")
175 << QStringLiteral("QCommandLinkButton");
176 break;
177 case MorphSpinBox:
178 l << QStringLiteral("QDateTimeEdit") << QStringLiteral("QDateEdit")
179 << QStringLiteral("QTimeEdit")
180 << QStringLiteral("QSpinBox") << QStringLiteral("QDoubleSpinBox");
181 break;
182 case MorphTextEdit:
183 l << QStringLiteral("QTextEdit") << QStringLiteral("QPlainTextEdit") << QStringLiteral("QTextBrowser");
184 break;
185 }
186 }
187 return it.value();
188}
189
190// Return the widgets containing the children to be transferred to. This is the
191// widget itself in most cases, except for QDesignerContainerExtension cases
192static QWidgetList childContainers(const QDesignerFormEditorInterface *core, QWidget *w)
193{
194 if (const QDesignerContainerExtension *ce = qt_extension<QDesignerContainerExtension*>(manager: core->extensionManager(), object: w)) {
195 QWidgetList children;
196 if (const int count = ce->count()) {
197 for (int i = 0; i < count; i++)
198 children.push_back(t: ce->widget(index: i));
199 }
200 return children;
201 }
202 QWidgetList self;
203 self.push_back(t: w);
204 return self;
205}
206
207// Suggest a suitable objectname for the widget to be morphed into
208// Replace the class name parts: 'xxFrame' -> 'xxGroupBox', 'frame' -> 'groupBox'
209static QString suggestObjectName(const QString &oldClassName, const QString &newClassName, const QString &oldName)
210{
211 QString oldClassPart = oldClassName;
212 QString newClassPart = newClassName;
213 if (oldClassPart.startsWith(c: QLatin1Char('Q')))
214 oldClassPart.remove(i: 0, len: 1);
215 if (newClassPart.startsWith(c: QLatin1Char('Q')))
216 newClassPart.remove(i: 0, len: 1);
217
218 QString newName = oldName;
219 newName.replace(before: oldClassPart, after: newClassPart);
220 oldClassPart[0] = oldClassPart.at(i: 0).toLower();
221 newClassPart[0] = newClassPart.at(i: 0).toLower();
222 newName.replace(before: oldClassPart, after: newClassPart);
223 return newName;
224}
225
226// Find the label whose buddy the widget is.
227QLabel *buddyLabelOf(QDesignerFormWindowInterface *fw, QWidget *w)
228{
229 const auto labelList = fw->findChildren<QLabel*>();
230 for (QLabel *label : labelList)
231 if (label->buddy() == w)
232 return label;
233 return nullptr;
234}
235
236// Replace widgets in a widget-list type dynamic property of the parent
237// used for Z-order, etc.
238static void replaceWidgetListDynamicProperty(QWidget *parentWidget,
239 QWidget *oldWidget, QWidget *newWidget,
240 const char *name)
241{
242 QWidgetList list = qvariant_cast<QWidgetList>(v: parentWidget->property(name));
243 const int index = list.indexOf(t: oldWidget);
244 if (index != -1) {
245 list.replace(i: index, t: newWidget);
246 parentWidget->setProperty(name, value: QVariant::fromValue(value: list));
247 }
248}
249
250/* Morph a widget into another class. Use the static addMorphMacro() to
251 * add a respective command sequence to the undo stack as it emits signals
252 * which cause other commands to be added. */
253class MorphWidgetCommand : public QDesignerFormWindowCommand
254{
255 Q_DISABLE_COPY_MOVE(MorphWidgetCommand)
256public:
257
258 explicit MorphWidgetCommand(QDesignerFormWindowInterface *formWindow);
259 ~MorphWidgetCommand() override;
260
261 // Convenience to add a morph command sequence macro
262 static bool addMorphMacro(QDesignerFormWindowInterface *formWindow, QWidget *w, const QString &newClass);
263
264 bool init(QWidget *widget, const QString &newClassName);
265
266 QString newWidgetName() const { return m_afterWidget->objectName(); }
267
268 void redo() override;
269 void undo() override;
270
271 static QStringList candidateClasses(QDesignerFormWindowInterface *fw, QWidget *w);
272
273private:
274 static bool canMorph(QDesignerFormWindowInterface *fw, QWidget *w, int *childContainerCount = nullptr, MorphCategory *cat = nullptr);
275 void morph(QWidget *before, QWidget *after);
276
277 QWidget *m_beforeWidget;
278 QWidget *m_afterWidget;
279};
280
281bool MorphWidgetCommand::addMorphMacro(QDesignerFormWindowInterface *fw, QWidget *w, const QString &newClass)
282{
283 MorphWidgetCommand *morphCmd = new MorphWidgetCommand(fw);
284 if (!morphCmd->init(widget: w, newClassName: newClass)) {
285 qWarning(msg: "*** Unable to create a MorphWidgetCommand");
286 delete morphCmd;
287 return false;
288 }
289 QLabel *buddyLabel = buddyLabelOf(fw, w);
290 // Need a macro since it adds further commands
291 QUndoStack *us = fw->commandHistory();
292 us->beginMacro(text: morphCmd->text());
293 // Have the signal slot/buddy editors add their commands to delete widget
294 if (FormWindowBase *fwb = qobject_cast<FormWindowBase*>(object: fw))
295 fwb->emitWidgetRemoved(w);
296
297 const QString newWidgetName = morphCmd->newWidgetName();
298 us->push(cmd: morphCmd);
299
300 // restore buddy using the QByteArray name.
301 if (buddyLabel) {
302 SetPropertyCommand *buddyCmd = new SetPropertyCommand(fw);
303 buddyCmd->init(object: buddyLabel, QStringLiteral("buddy"), newValue: QVariant(newWidgetName.toUtf8()));
304 us->push(cmd: buddyCmd);
305 }
306 us->endMacro();
307 return true;
308}
309
310MorphWidgetCommand::MorphWidgetCommand(QDesignerFormWindowInterface *formWindow) :
311 QDesignerFormWindowCommand(QString(), formWindow),
312 m_beforeWidget(nullptr),
313 m_afterWidget(nullptr)
314{
315}
316
317MorphWidgetCommand::~MorphWidgetCommand() = default;
318
319bool MorphWidgetCommand::init(QWidget *widget, const QString &newClassName)
320{
321 QDesignerFormWindowInterface *fw = formWindow();
322 QDesignerFormEditorInterface *core = fw->core();
323
324 if (!canMorph(fw, w: widget))
325 return false;
326
327 const QString oldClassName = WidgetFactory::classNameOf(core, o: widget);
328 const QString oldName = widget->objectName();
329 //: MorphWidgetCommand description
330 setText(QApplication::translate(context: "Command", key: "Morph %1/'%2' into %3").arg(a1: oldClassName, a2: oldName, a3: newClassName));
331
332 m_beforeWidget = widget;
333 m_afterWidget = core->widgetFactory()->createWidget(name: newClassName, parentWidget: fw);
334 if (!m_afterWidget)
335 return false;
336
337 // Set object name. Do not unique it (as to maintain it).
338 m_afterWidget->setObjectName(suggestObjectName(oldClassName, newClassName, oldName));
339
340 // If the target has a container extension, we add enough new pages to take
341 // up the children of the before widget
342 if (QDesignerContainerExtension* c = qt_extension<QDesignerContainerExtension*>(manager: core->extensionManager(), object: m_afterWidget)) {
343 if (const int pageCount = childContainers(core, w: m_beforeWidget).size()) {
344 const QString qWidget = QStringLiteral("QWidget");
345 const QString containerName = m_afterWidget->objectName();
346 for (int i = 0; i < pageCount; i++) {
347 QString name = containerName;
348 name += QStringLiteral("Page");
349 name += QString::number(i + 1);
350 QWidget *page = core->widgetFactory()->createWidget(name: qWidget);
351 page->setObjectName(name);
352 fw->ensureUniqueObjectName(object: page);
353 c->addWidget(widget: page);
354 core->metaDataBase()->add(object: page);
355 }
356 }
357 }
358
359 // Copy over applicable properties
360 const QDesignerPropertySheetExtension *beforeSheet = qt_extension<QDesignerPropertySheetExtension*>(manager: core->extensionManager(), object: widget);
361 QDesignerPropertySheetExtension *afterSheet = qt_extension<QDesignerPropertySheetExtension*>(manager: core->extensionManager(), object: m_afterWidget);
362 const QString objectNameProperty = QStringLiteral("objectName");
363 const int count = beforeSheet->count();
364 for (int i = 0; i < count; i++)
365 if (beforeSheet->isVisible(index: i) && beforeSheet->isChanged(index: i)) {
366 const QString name = beforeSheet->propertyName(index: i);
367 if (name != objectNameProperty) {
368 const int afterIndex = afterSheet->indexOf(name);
369 if (afterIndex != -1 && afterSheet->isVisible(index: afterIndex) && afterSheet->propertyGroup(index: afterIndex) == beforeSheet->propertyGroup(index: i)) {
370 afterSheet->setProperty(index: i, value: beforeSheet->property(index: i));
371 afterSheet->setChanged(index: i, changed: true);
372 } else {
373 // Some mismatch. The rest won't match, either
374 break;
375 }
376 }
377 }
378 return true;
379}
380
381void MorphWidgetCommand::redo()
382{
383 morph(before: m_beforeWidget, after: m_afterWidget);
384}
385
386void MorphWidgetCommand::undo()
387{
388 morph(before: m_afterWidget, after: m_beforeWidget);
389}
390
391void MorphWidgetCommand::morph(QWidget *before, QWidget *after)
392{
393 QDesignerFormWindowInterface *fw = formWindow();
394
395 fw->unmanageWidget(widget: before);
396
397 const QRect oldGeom = before->geometry();
398 QWidget *parent = before->parentWidget();
399 Q_ASSERT(parent);
400 /* Morphing consists of main 2 steps
401 * 1) Move over children (laid out, non-laid out)
402 * 2) Register self with new parent (laid out, non-laid out) */
403
404 // 1) Move children. Loop over child containers
405 QWidgetList beforeChildContainers = childContainers(core: fw->core(), w: before);
406 QWidgetList afterChildContainers = childContainers(core: fw->core(), w: after);
407 Q_ASSERT(beforeChildContainers.size() == afterChildContainers.size());
408 const int childContainerCount = beforeChildContainers.size();
409 for (int i = 0; i < childContainerCount; i++) {
410 QWidget *beforeChildContainer = beforeChildContainers.at(i);
411 QWidget *afterChildContainer = afterChildContainers.at(i);
412 if (QLayout *childLayout = beforeChildContainer->layout()) {
413 // Laid-out: Move the layout (since 4.5)
414 afterChildContainer->setLayout(childLayout);
415 } else {
416 // Non-Laid-out: Reparent, move over
417 for (QObject *o : beforeChildContainer->children()) {
418 if (o->isWidgetType()) {
419 QWidget *w = static_cast<QWidget*>(o);
420 if (fw->isManaged(widget: w)) {
421 const QRect geom = w->geometry();
422 w->setParent(afterChildContainer);
423 w->setGeometry(geom);
424 }
425 }
426 }
427 }
428 afterChildContainer->setProperty(name: widgetOrderPropertyC, value: beforeChildContainer->property(name: widgetOrderPropertyC));
429 afterChildContainer->setProperty(name: zOrderPropertyC, value: beforeChildContainer->property(name: zOrderPropertyC));
430 }
431
432 // 2) Replace the actual widget in the parent layout
433 after->setGeometry(oldGeom);
434 if (QLayout *containingLayout = LayoutInfo::managedLayout(core: fw->core(), widget: parent)) {
435 LayoutHelper *lh = LayoutHelper::createLayoutHelper(type: LayoutInfo::layoutType(core: fw->core(), layout: containingLayout));
436 Q_ASSERT(lh);
437 lh->replaceWidget(lt: containingLayout, before, after);
438 delete lh;
439 } else if (QSplitter *splitter = qobject_cast<QSplitter *>(object: parent)) {
440 const int index = splitter->indexOf(w: before);
441 before->hide();
442 before->setParent(nullptr);
443 splitter->insertWidget(index, widget: after);
444 after->setParent(parent);
445 after->setGeometry(oldGeom);
446 } else {
447 before->hide();
448 before->setParent(nullptr);
449 after->setParent(parent);
450 after->setGeometry(oldGeom);
451 }
452
453 // Check various properties: Z order, form tab order
454 replaceWidgetListDynamicProperty(parentWidget: parent, oldWidget: before, newWidget: after, name: widgetOrderPropertyC);
455 replaceWidgetListDynamicProperty(parentWidget: parent, oldWidget: before, newWidget: after, name: zOrderPropertyC);
456
457 QDesignerMetaDataBaseItemInterface *formItem = fw->core()->metaDataBase()->item(object: fw);
458 QWidgetList tabOrder = formItem->tabOrder();
459 const int tabIndex = tabOrder.indexOf(t: before);
460 if (tabIndex != -1) {
461 tabOrder.replace(i: tabIndex, t: after);
462 formItem->setTabOrder(tabOrder);
463 }
464
465 after->show();
466 fw->manageWidget(widget: after);
467
468 fw->clearSelection(changePropertyDisplay: false);
469 fw->selectWidget(w: after);
470}
471
472/* Check if morphing is possible. It must be a valid category and the parent/
473 * child relationships must be either non-laidout or directly on
474 * Designer-managed layouts. */
475bool MorphWidgetCommand::canMorph(QDesignerFormWindowInterface *fw, QWidget *w, int *ptrToChildContainerCount, MorphCategory *ptrToCat)
476{
477 if (ptrToChildContainerCount)
478 *ptrToChildContainerCount = 0;
479 const MorphCategory cat = category(w);
480 if (ptrToCat)
481 *ptrToCat = cat;
482 if (cat == MorphCategoryNone)
483 return false;
484
485 QDesignerFormEditorInterface *core = fw->core();
486 // Don't know how to fiddle class names in Jambi..
487 if (qt_extension<QDesignerLanguageExtension *>(manager: core->extensionManager(), object: core))
488 return false;
489 if (!fw->isManaged(widget: w) || w == fw->mainContainer())
490 return false;
491 // Check the parent relationship. We accept only managed parent widgets
492 // with a single, managed layout in which widget is a member.
493 QWidget *parent = w->parentWidget();
494 if (parent == nullptr)
495 return false;
496 if (QLayout *pl = LayoutInfo::managedLayout(core, widget: parent))
497 if (pl->indexOf(w) < 0 || !core->metaDataBase()->item(object: pl))
498 return false;
499 // Check Widget database
500 const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase();
501 const int wdbindex = wdb->indexOfObject(object: w);
502 if (wdbindex == -1)
503 return false;
504 const bool isContainer = wdb->item(index: wdbindex)->isContainer();
505 if (!isContainer)
506 return true;
507 // Check children. All child containers must be non-laid-out or have managed layouts
508 const QWidgetList pages = childContainers(core, w);
509 const int pageCount = pages.size();
510 if (ptrToChildContainerCount)
511 *ptrToChildContainerCount = pageCount;
512 if (pageCount) {
513 for (int i = 0; i < pageCount; i++)
514 if (QLayout *cl = pages.at(i)->layout())
515 if (!core->metaDataBase()->item(object: cl))
516 return false;
517 }
518 return true;
519}
520
521QStringList MorphWidgetCommand::candidateClasses(QDesignerFormWindowInterface *fw, QWidget *w)
522{
523 int childContainerCount;
524 MorphCategory cat;
525 if (!canMorph(fw, w, ptrToChildContainerCount: &childContainerCount, ptrToCat: &cat))
526 return QStringList();
527
528 QStringList rc = classesOfCategory(cat);
529 switch (cat) {
530 // Frames, etc can always be morphed into one-page page containers
531 case MorphSimpleContainer:
532 rc += classesOfCategory(cat: MorphPageContainer);
533 break;
534 // Multipage-Containers can be morphed into simple containers if they
535 // have 1 page.
536 case MorphPageContainer:
537 if (childContainerCount == 1)
538 rc += classesOfCategory(cat: MorphSimpleContainer);
539 break;
540 default:
541 break;
542 }
543 return rc;
544}
545
546// MorphMenu
547MorphMenu::MorphMenu(QObject *parent) :
548 QObject(parent)
549{
550}
551
552void MorphMenu::populate(QWidget *w, QDesignerFormWindowInterface *fw, ActionList& al)
553{
554 if (populateMenu(w, fw))
555 al.push_back(t: m_subMenuAction);
556}
557
558void MorphMenu::populate(QWidget *w, QDesignerFormWindowInterface *fw, QMenu& m)
559{
560 if (populateMenu(w, fw))
561 m.addAction(action: m_subMenuAction);
562}
563
564void MorphMenu::slotMorph(const QString &newClassName)
565{
566 MorphWidgetCommand::addMorphMacro(fw: m_formWindow, w: m_widget, newClass: newClassName);
567}
568
569bool MorphMenu::populateMenu(QWidget *w, QDesignerFormWindowInterface *fw)
570{
571 m_widget = nullptr;
572 m_formWindow = nullptr;
573
574 // Clear menu
575 if (m_subMenuAction) {
576 m_subMenuAction->setVisible(false);
577 m_menu->clear();
578 }
579
580 // Checks: Must not be main container
581 if (w == fw->mainContainer())
582 return false;
583
584 const QStringList c = MorphWidgetCommand::candidateClasses(fw, w);
585 if (c.isEmpty())
586 return false;
587
588 // Pull up
589 m_widget = w;
590 m_formWindow = fw;
591 const QString oldClassName = WidgetFactory::classNameOf(core: fw->core(), o: w);
592
593 if (!m_subMenuAction) {
594 m_subMenuAction = new QAction(tr(s: "Morph into"), this);
595 m_menu = new QMenu;
596 m_subMenuAction->setMenu(m_menu);
597 }
598
599 // Add actions
600 const QStringList::const_iterator cend = c.constEnd();
601 for (QStringList::const_iterator it = c.constBegin(); it != cend; ++it) {
602 if (*it != oldClassName) {
603 const QString className = *it;
604 m_menu->addAction(text: className,
605 object: this, slot: [this, className] { this->slotMorph(newClassName: className); });
606 }
607 }
608 m_subMenuAction->setVisible(true);
609 return true;
610}
611
612} // namespace qdesigner_internal
613
614QT_END_NAMESPACE
615

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