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
71 Q_DECLARE_METATYPE(QWidgetList)
72
73 QT_BEGIN_NAMESPACE
74
75 // Helpers for the dynamic properties that store Z/Widget order
76 static const char *widgetOrderPropertyC = "_q_widgetOrder";
77 static 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
114 namespace qdesigner_internal {
115
116 enum MorphCategory {
117 MorphCategoryNone, MorphSimpleContainer, MorphPageContainer, MorphItemView,
118 MorphButton, MorphSpinBox, MorphTextEdit
119 };
120
121 // Determine category of a widget
category(const QWidget * w)122 static 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*>(w))
131 return MorphItemView;
132 if (qobject_cast<const QAbstractButton *>(w))
133 return MorphButton;
134 if (qobject_cast<const QAbstractSpinBox *>(w))
135 return MorphSpinBox;
136 if (qobject_cast<const QPlainTextEdit *>(w) || qobject_cast<const QTextEdit*>(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
classesOfCategory(MorphCategory cat)147 static QStringList classesOfCategory(MorphCategory cat)
148 {
149 typedef QMap<MorphCategory, QStringList> CandidateCache;
150 static CandidateCache candidateCache;
151 CandidateCache::iterator it = candidateCache.find(cat);
152 if (it == candidateCache.end()) {
153 it = candidateCache.insert(cat, 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
childContainers(const QDesignerFormEditorInterface * core,QWidget * w)192 static QWidgetList childContainers(const QDesignerFormEditorInterface *core, QWidget *w)
193 {
194 if (const QDesignerContainerExtension *ce = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), w)) {
195 QWidgetList children;
196 if (const int count = ce->count()) {
197 for (int i = 0; i < count; i++)
198 children.push_back(ce->widget(i));
199 }
200 return children;
201 }
202 QWidgetList self;
203 self.push_back(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'
suggestObjectName(const QString & oldClassName,const QString & newClassName,const QString & oldName)209 static QString suggestObjectName(const QString &oldClassName, const QString &newClassName, const QString &oldName)
210 {
211 QString oldClassPart = oldClassName;
212 QString newClassPart = newClassName;
213 if (oldClassPart.startsWith(QLatin1Char('Q')))
214 oldClassPart.remove(0, 1);
215 if (newClassPart.startsWith(QLatin1Char('Q')))
216 newClassPart.remove(0, 1);
217
218 QString newName = oldName;
219 newName.replace(oldClassPart, newClassPart);
220 oldClassPart[0] = oldClassPart.at(0).toLower();
221 newClassPart[0] = newClassPart.at(0).toLower();
222 newName.replace(oldClassPart, newClassPart);
223 return newName;
224 }
225
226 // Find the label whose buddy the widget is.
buddyLabelOf(QDesignerFormWindowInterface * fw,QWidget * w)227 QLabel *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.
replaceWidgetListDynamicProperty(QWidget * parentWidget,QWidget * oldWidget,QWidget * newWidget,const char * name)238 static void replaceWidgetListDynamicProperty(QWidget *parentWidget,
239 QWidget *oldWidget, QWidget *newWidget,
240 const char *name)
241 {
242 QWidgetList list = qvariant_cast<QWidgetList>(parentWidget->property(name));
243 const int index = list.indexOf(oldWidget);
244 if (index != -1) {
245 list.replace(index, newWidget);
246 parentWidget->setProperty(name, QVariant::fromValue(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. */
253 class MorphWidgetCommand : public QDesignerFormWindowCommand
254 {
255 Q_DISABLE_COPY_MOVE(MorphWidgetCommand)
256 public:
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
newWidgetName() const266 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
273 private:
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
addMorphMacro(QDesignerFormWindowInterface * fw,QWidget * w,const QString & newClass)281 bool MorphWidgetCommand::addMorphMacro(QDesignerFormWindowInterface *fw, QWidget *w, const QString &newClass)
282 {
283 MorphWidgetCommand *morphCmd = new MorphWidgetCommand(fw);
284 if (!morphCmd->init(w, newClass)) {
285 qWarning("*** 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(morphCmd->text());
293 // Have the signal slot/buddy editors add their commands to delete widget
294 if (FormWindowBase *fwb = qobject_cast<FormWindowBase*>(fw))
295 fwb->emitWidgetRemoved(w);
296
297 const QString newWidgetName = morphCmd->newWidgetName();
298 us->push(morphCmd);
299
300 // restore buddy using the QByteArray name.
301 if (buddyLabel) {
302 SetPropertyCommand *buddyCmd = new SetPropertyCommand(fw);
303 buddyCmd->init(buddyLabel, QStringLiteral("buddy"), QVariant(newWidgetName.toUtf8()));
304 us->push(buddyCmd);
305 }
306 us->endMacro();
307 return true;
308 }
309
MorphWidgetCommand(QDesignerFormWindowInterface * formWindow)310 MorphWidgetCommand::MorphWidgetCommand(QDesignerFormWindowInterface *formWindow) :
311 QDesignerFormWindowCommand(QString(), formWindow),
312 m_beforeWidget(nullptr),
313 m_afterWidget(nullptr)
314 {
315 }
316
317 MorphWidgetCommand::~MorphWidgetCommand() = default;
318
init(QWidget * widget,const QString & newClassName)319 bool MorphWidgetCommand::init(QWidget *widget, const QString &newClassName)
320 {
321 QDesignerFormWindowInterface *fw = formWindow();
322 QDesignerFormEditorInterface *core = fw->core();
323
324 if (!canMorph(fw, widget))
325 return false;
326
327 const QString oldClassName = WidgetFactory::classNameOf(core, widget);
328 const QString oldName = widget->objectName();
329 //: MorphWidgetCommand description
330 setText(QApplication::translate("Command", "Morph %1/'%2' into %3").arg(oldClassName, oldName, newClassName));
331
332 m_beforeWidget = widget;
333 m_afterWidget = core->widgetFactory()->createWidget(newClassName, 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*>(core->extensionManager(), m_afterWidget)) {
343 if (const int pageCount = childContainers(core, 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(qWidget);
351 page->setObjectName(name);
352 fw->ensureUniqueObjectName(page);
353 c->addWidget(page);
354 core->metaDataBase()->add(page);
355 }
356 }
357 }
358
359 // Copy over applicable properties
360 const QDesignerPropertySheetExtension *beforeSheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), widget);
361 QDesignerPropertySheetExtension *afterSheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), 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(i) && beforeSheet->isChanged(i)) {
366 const QString name = beforeSheet->propertyName(i);
367 if (name != objectNameProperty) {
368 const int afterIndex = afterSheet->indexOf(name);
369 if (afterIndex != -1 && afterSheet->isVisible(afterIndex) && afterSheet->propertyGroup(afterIndex) == beforeSheet->propertyGroup(i)) {
370 afterSheet->setProperty(i, beforeSheet->property(i));
371 afterSheet->setChanged(i, true);
372 } else {
373 // Some mismatch. The rest won't match, either
374 break;
375 }
376 }
377 }
378 return true;
379 }
380
redo()381 void MorphWidgetCommand::redo()
382 {
383 morph(m_beforeWidget, m_afterWidget);
384 }
385
undo()386 void MorphWidgetCommand::undo()
387 {
388 morph(m_afterWidget, m_beforeWidget);
389 }
390
morph(QWidget * before,QWidget * after)391 void MorphWidgetCommand::morph(QWidget *before, QWidget *after)
392 {
393 QDesignerFormWindowInterface *fw = formWindow();
394
395 fw->unmanageWidget(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(fw->core(), before);
406 QWidgetList afterChildContainers = childContainers(fw->core(), 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(w)) {
421 const QRect geom = w->geometry();
422 w->setParent(afterChildContainer);
423 w->setGeometry(geom);
424 }
425 }
426 }
427 }
428 afterChildContainer->setProperty(widgetOrderPropertyC, beforeChildContainer->property(widgetOrderPropertyC));
429 afterChildContainer->setProperty(zOrderPropertyC, beforeChildContainer->property(zOrderPropertyC));
430 }
431
432 // 2) Replace the actual widget in the parent layout
433 after->setGeometry(oldGeom);
434 if (QLayout *containingLayout = LayoutInfo::managedLayout(fw->core(), parent)) {
435 LayoutHelper *lh = LayoutHelper::createLayoutHelper(LayoutInfo::layoutType(fw->core(), containingLayout));
436 Q_ASSERT(lh);
437 lh->replaceWidget(containingLayout, before, after);
438 delete lh;
439 } else if (QSplitter *splitter = qobject_cast<QSplitter *>(parent)) {
440 const int index = splitter->indexOf(before);
441 before->hide();
442 before->setParent(nullptr);
443 splitter->insertWidget(index, 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(parent, before, after, widgetOrderPropertyC);
455 replaceWidgetListDynamicProperty(parent, before, after, zOrderPropertyC);
456
457 QDesignerMetaDataBaseItemInterface *formItem = fw->core()->metaDataBase()->item(fw);
458 QWidgetList tabOrder = formItem->tabOrder();
459 const int tabIndex = tabOrder.indexOf(before);
460 if (tabIndex != -1) {
461 tabOrder.replace(tabIndex, after);
462 formItem->setTabOrder(tabOrder);
463 }
464
465 after->show();
466 fw->manageWidget(after);
467
468 fw->clearSelection(false);
469 fw->selectWidget(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. */
canMorph(QDesignerFormWindowInterface * fw,QWidget * w,int * ptrToChildContainerCount,MorphCategory * ptrToCat)475 bool 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 *>(core->extensionManager(), core))
488 return false;
489 if (!fw->isManaged(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, parent))
497 if (pl->indexOf(w) < 0 || !core->metaDataBase()->item(pl))
498 return false;
499 // Check Widget database
500 const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase();
501 const int wdbindex = wdb->indexOfObject(w);
502 if (wdbindex == -1)
503 return false;
504 const bool isContainer = wdb->item(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(cl))
516 return false;
517 }
518 return true;
519 }
520
candidateClasses(QDesignerFormWindowInterface * fw,QWidget * w)521 QStringList MorphWidgetCommand::candidateClasses(QDesignerFormWindowInterface *fw, QWidget *w)
522 {
523 int childContainerCount;
524 MorphCategory cat;
525 if (!canMorph(fw, w, &childContainerCount, &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(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(MorphSimpleContainer);
539 break;
540 default:
541 break;
542 }
543 return rc;
544 }
545
546 // MorphMenu
MorphMenu(QObject * parent)547 MorphMenu::MorphMenu(QObject *parent) :
548 QObject(parent)
549 {
550 }
551
populate(QWidget * w,QDesignerFormWindowInterface * fw,ActionList & al)552 void MorphMenu::populate(QWidget *w, QDesignerFormWindowInterface *fw, ActionList& al)
553 {
554 if (populateMenu(w, fw))
555 al.push_back(m_subMenuAction);
556 }
557
populate(QWidget * w,QDesignerFormWindowInterface * fw,QMenu & m)558 void MorphMenu::populate(QWidget *w, QDesignerFormWindowInterface *fw, QMenu& m)
559 {
560 if (populateMenu(w, fw))
561 m.addAction(m_subMenuAction);
562 }
563
slotMorph(const QString & newClassName)564 void MorphMenu::slotMorph(const QString &newClassName)
565 {
566 MorphWidgetCommand::addMorphMacro(m_formWindow, m_widget, newClassName);
567 }
568
populateMenu(QWidget * w,QDesignerFormWindowInterface * fw)569 bool 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(fw->core(), w);
592
593 if (!m_subMenuAction) {
594 m_subMenuAction = new QAction(tr("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(className,
605 this, [this, className] { this->slotMorph(className); });
606 }
607 }
608 m_subMenuAction->setVisible(true);
609 return true;
610 }
611
612 } // namespace qdesigner_internal
613
614 QT_END_NAMESPACE
615