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