tools/designer/src/lib/shared/layout.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 "layout_p.h"
       
    43 #include "qdesigner_utils_p.h"
       
    44 #include "qlayout_widget_p.h"
       
    45 #include "spacer_widget_p.h"
       
    46 #include "layoutdecoration.h"
       
    47 #include "widgetfactory_p.h"
       
    48 #include "qdesigner_widgetitem_p.h"
       
    49 
       
    50 #include <QtDesigner/QDesignerFormEditorInterface>
       
    51 #include <QtDesigner/QDesignerFormWindowInterface>
       
    52 #include <QtDesigner/QDesignerContainerExtension>
       
    53 #include <QtDesigner/QExtensionManager>
       
    54 #include <QtDesigner/QDesignerPropertySheetExtension>
       
    55 #include <QtDesigner/QDesignerWidgetDataBaseInterface>
       
    56 #include <QtDesigner/QDesignerMetaDataBaseInterface>
       
    57 
       
    58 #include <QtCore/qdebug.h>
       
    59 #include <QtCore/QVector>
       
    60 
       
    61 #include <QtGui/qevent.h>
       
    62 #include <QtGui/QGridLayout>
       
    63 #include <QtGui/QPainter>
       
    64 #include <QtGui/QBitmap>
       
    65 #include <QtGui/QSplitter>
       
    66 #include <QtGui/QMainWindow>
       
    67 #include <QtGui/QApplication>
       
    68 #include <QtGui/QScrollArea>
       
    69 #include <QtGui/QFormLayout>
       
    70 #include <QtGui/QLabel>
       
    71 #include <QtGui/QWizardPage>
       
    72 #include <QtGui/QWizard>
       
    73 #include <QtCore/QDebug>
       
    74 #include <QtCore/QSet>
       
    75 
       
    76 QT_BEGIN_NAMESPACE
       
    77 
       
    78 enum { FormLayoutColumns = 2 };
       
    79 
       
    80 namespace qdesigner_internal {
       
    81 
       
    82 /* The wizard has a policy of setting a size policy of its external children
       
    83  * according to the page being expanding or not (in the latter case, the
       
    84  * page will be pushed to the top). When setting/breaking layouts, this needs
       
    85  * to be updated, which happens via a fake style change event. */
       
    86 
       
    87 void updateWizardLayout(QWidget *layoutBase);
       
    88 
       
    89 class FriendlyWizardPage : public  QWizardPage {
       
    90     friend void updateWizardLayout(QWidget *);
       
    91 };
       
    92 
       
    93 void updateWizardLayout(QWidget *layoutBase)
       
    94 {
       
    95     if (QWizardPage *wizardPage = qobject_cast<QWizardPage*>(layoutBase))
       
    96         if (QWizard *wizard = static_cast<FriendlyWizardPage*>(wizardPage)->wizard()) {
       
    97             QEvent event(QEvent::StyleChange);
       
    98             QApplication::sendEvent(wizard, &event);
       
    99         }
       
   100 }
       
   101 
       
   102 /*!
       
   103   \class Layout layout.h
       
   104   \brief Baseclass for layouting widgets in the Designer (Helper for Layout commands)
       
   105   \internal
       
   106 
       
   107   Classes derived from this abstract base class are used for layouting
       
   108   operations in the Designer (creating/breaking layouts).
       
   109 
       
   110   Instances live in the Layout/BreakLayout commands.
       
   111 */
       
   112 
       
   113 /*!  \a p specifies the parent of the layoutBase \a lb. The parent
       
   114   might be changed in setup(). If the layoutBase is a
       
   115   container, the parent and the layoutBase are the same. Also they
       
   116   always have to be a widget known to the designer (e.g. in the case
       
   117   of the tabwidget parent and layoutBase are the tabwidget and not the
       
   118   page which actually gets laid out. For actual usage the correct
       
   119   widget is found later by Layout.)
       
   120  */
       
   121 
       
   122 Layout::Layout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb, LayoutInfo::Type layoutType) :
       
   123     m_widgets(wl),
       
   124     m_parentWidget(p),
       
   125     m_layoutBase(lb),
       
   126     m_formWindow(fw),
       
   127     m_layoutType(layoutType),
       
   128     m_reparentLayoutWidget(true),
       
   129     m_isBreak(false)
       
   130 {
       
   131     if (m_layoutBase)
       
   132         m_oldGeometry = m_layoutBase->geometry();
       
   133 }
       
   134 
       
   135 Layout::~Layout()
       
   136 {
       
   137 }
       
   138 
       
   139 /*!  The widget list we got in the constructor might contain too much
       
   140   widgets (like widgets with different parents, already laid out
       
   141   widgets, etc.). Here we set up the list and so the only the "best"
       
   142   widgets get laid out.
       
   143 */
       
   144 
       
   145 void Layout::setup()
       
   146 {
       
   147     m_startPoint = QPoint(32767, 32767);
       
   148 
       
   149     // Go through all widgets of the list we got. As we can only
       
   150     // layout widgets which have the same parent, we first do some
       
   151     // sorting which means create a list for each parent containing
       
   152     // its child here. After that we keep working on the list of
       
   153     // children which has the most entries.
       
   154     // Widgets which are already laid out are thrown away here too
       
   155 
       
   156     QMultiMap<QWidget*, QWidget*> lists;
       
   157     foreach (QWidget *w, m_widgets) {
       
   158         QWidget *p = w->parentWidget();
       
   159 
       
   160         if (p && LayoutInfo::layoutType(m_formWindow->core(), p) != LayoutInfo::NoLayout
       
   161                 && m_formWindow->core()->metaDataBase()->item(p->layout()) != 0)
       
   162             continue;
       
   163 
       
   164         lists.insert(p, w);
       
   165     }
       
   166 
       
   167     QWidgetList lastList;
       
   168     QWidgetList parents = lists.keys();
       
   169     foreach (QWidget *p, parents) {
       
   170         QWidgetList children = lists.values(p);
       
   171 
       
   172         if (children.count() > lastList.count())
       
   173             lastList = children;
       
   174     }
       
   175 
       
   176 
       
   177     // If we found no list (because no widget did fit at all) or the
       
   178     // best list has only one entry and we do not layout a container,
       
   179     // we leave here.
       
   180     QDesignerWidgetDataBaseInterface *widgetDataBase = m_formWindow->core()->widgetDataBase();
       
   181     if (lastList.count() < 2 &&
       
   182                         (!m_layoutBase ||
       
   183                           (!widgetDataBase->isContainer(m_layoutBase, false) &&
       
   184                             m_layoutBase != m_formWindow->mainContainer()))
       
   185                        ) {
       
   186         m_widgets.clear();
       
   187         m_startPoint = QPoint(0, 0);
       
   188         return;
       
   189     }
       
   190 
       
   191     // Now we have a new and clean widget list, which makes sense
       
   192     // to layout
       
   193     m_widgets = lastList;
       
   194     // Also use the only correct parent later, so store it
       
   195 
       
   196     Q_ASSERT(m_widgets.isEmpty() == false);
       
   197 
       
   198     m_parentWidget = m_formWindow->core()->widgetFactory()->widgetOfContainer(m_widgets.first()->parentWidget());
       
   199     // Now calculate the position where the layout-meta-widget should
       
   200     // be placed and connect to widgetDestroyed() signals of the
       
   201     // widgets to get informed if one gets deleted to be able to
       
   202     // handle that and do not crash in this case
       
   203     foreach (QWidget *w, m_widgets) {
       
   204         connect(w, SIGNAL(destroyed()), this, SLOT(widgetDestroyed()));
       
   205         m_startPoint = QPoint(qMin(m_startPoint.x(), w->x()), qMin(m_startPoint.y(), w->y()));
       
   206         const QRect rc(w->geometry());
       
   207 
       
   208         m_geometries.insert(w, rc);
       
   209         // Change the Z-order, as saving/loading uses the Z-order for
       
   210         // writing/creating widgets and this has to be the same as in
       
   211         // the layout. Else saving + loading will give different results
       
   212         w->raise();
       
   213     }
       
   214 
       
   215     sort();
       
   216 }
       
   217 
       
   218 void Layout::widgetDestroyed()
       
   219 {
       
   220     if (QWidget *w = qobject_cast<QWidget *>(sender())) {
       
   221         m_widgets.removeAt(m_widgets.indexOf(w));
       
   222         m_geometries.remove(w);
       
   223     }
       
   224 }
       
   225 
       
   226 bool Layout::prepareLayout(bool &needMove, bool &needReparent)
       
   227 {
       
   228     foreach (QWidget *widget, m_widgets) {
       
   229         widget->raise();
       
   230     }
       
   231 
       
   232     needMove = !m_layoutBase;
       
   233     needReparent = needMove || (m_reparentLayoutWidget && qobject_cast<QLayoutWidget*>(m_layoutBase)) || qobject_cast<QSplitter*>(m_layoutBase);
       
   234 
       
   235     QDesignerWidgetFactoryInterface *widgetFactory = m_formWindow->core()->widgetFactory();
       
   236     QDesignerMetaDataBaseInterface *metaDataBase = m_formWindow->core()->metaDataBase();
       
   237 
       
   238     if (m_layoutBase == 0) {
       
   239         const bool useSplitter = m_layoutType == LayoutInfo::HSplitter || m_layoutType == LayoutInfo::VSplitter;
       
   240         const QString baseWidgetClassName = useSplitter ? QLatin1String("QSplitter") : QLatin1String("QLayoutWidget");
       
   241         m_layoutBase = widgetFactory->createWidget(baseWidgetClassName, widgetFactory->containerOfWidget(m_parentWidget));
       
   242         if (useSplitter) {
       
   243             m_layoutBase->setObjectName(QLatin1String("splitter"));
       
   244             m_formWindow->ensureUniqueObjectName(m_layoutBase);
       
   245         }
       
   246     } else {
       
   247         LayoutInfo::deleteLayout(m_formWindow->core(), m_layoutBase);
       
   248     }
       
   249 
       
   250     metaDataBase->add(m_layoutBase);
       
   251 
       
   252     Q_ASSERT(m_layoutBase->layout() == 0 || metaDataBase->item(m_layoutBase->layout()) == 0);
       
   253 
       
   254     return true;
       
   255 }
       
   256 
       
   257 static bool isMainContainer(QDesignerFormWindowInterface *fw, const QWidget *w)
       
   258 {
       
   259     return w && (w == fw || w == fw->mainContainer());
       
   260 }
       
   261 
       
   262 static bool isPageOfContainerWidget(QDesignerFormWindowInterface *fw, QWidget *widget)
       
   263 {
       
   264     QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(
       
   265             fw->core()->extensionManager(), widget->parentWidget());
       
   266 
       
   267     if (c != 0) {
       
   268         for (int i = 0; i<c->count(); ++i) {
       
   269             if (widget == c->widget(i))
       
   270                 return true;
       
   271         }
       
   272     }
       
   273 
       
   274     return false;
       
   275 }
       
   276 void Layout::finishLayout(bool needMove, QLayout *layout)
       
   277 {
       
   278     if (m_parentWidget == m_layoutBase) {
       
   279         QWidget *widget = m_layoutBase;
       
   280         m_oldGeometry = widget->geometry();
       
   281 
       
   282         bool done = false;
       
   283         while (!isMainContainer(m_formWindow, widget) && !done) {
       
   284             if (!m_formWindow->isManaged(widget)) {
       
   285                 widget = widget->parentWidget();
       
   286                 continue;
       
   287             } else if (LayoutInfo::isWidgetLaidout(m_formWindow->core(), widget)) {
       
   288                 widget = widget->parentWidget();
       
   289                 continue;
       
   290             } else if (isPageOfContainerWidget(m_formWindow, widget)) {
       
   291                 widget = widget->parentWidget();
       
   292                 continue;
       
   293             } else if (widget->parentWidget()) {
       
   294                 QScrollArea *area = qobject_cast<QScrollArea*>(widget->parentWidget()->parentWidget());
       
   295                 if (area && area->widget() == widget) {
       
   296                     widget = area;
       
   297                     continue;
       
   298                 }
       
   299             }
       
   300 
       
   301             done = true;
       
   302         }
       
   303         updateWizardLayout(m_layoutBase);
       
   304         QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
       
   305         // We don't want to resize the form window
       
   306         if (!Utils::isCentralWidget(m_formWindow, widget))
       
   307             widget->adjustSize();
       
   308 
       
   309         return;
       
   310     }
       
   311 
       
   312     if (needMove)
       
   313         m_layoutBase->move(m_startPoint);
       
   314 
       
   315     const QRect g(m_layoutBase->pos(), m_layoutBase->size());
       
   316 
       
   317     if (LayoutInfo::layoutType(m_formWindow->core(), m_layoutBase->parentWidget()) == LayoutInfo::NoLayout && !m_isBreak)
       
   318         m_layoutBase->adjustSize();
       
   319     else if (m_isBreak)
       
   320         m_layoutBase->setGeometry(m_oldGeometry);
       
   321 
       
   322     m_oldGeometry = g;
       
   323     if (layout)
       
   324         layout->invalidate();
       
   325     m_layoutBase->show();
       
   326 
       
   327     if (qobject_cast<QLayoutWidget*>(m_layoutBase) || qobject_cast<QSplitter*>(m_layoutBase)) {
       
   328         m_formWindow->clearSelection(false);
       
   329         m_formWindow->manageWidget(m_layoutBase);
       
   330         m_formWindow->selectWidget(m_layoutBase);
       
   331     }
       
   332 }
       
   333 
       
   334 void Layout::undoLayout()
       
   335 {
       
   336     if (!m_widgets.count())
       
   337         return;
       
   338 
       
   339     m_formWindow->selectWidget(m_layoutBase, false);
       
   340 
       
   341     QDesignerWidgetFactoryInterface *widgetFactory = m_formWindow->core()->widgetFactory();
       
   342     QHashIterator<QWidget *, QRect> it(m_geometries);
       
   343     while (it.hasNext()) {
       
   344         it.next();
       
   345 
       
   346         if (!it.key())
       
   347             continue;
       
   348 
       
   349         QWidget* w = it.key();
       
   350         const QRect rc = it.value();
       
   351 
       
   352         const bool showIt = w->isVisibleTo(m_formWindow);
       
   353         QWidget *container = widgetFactory->containerOfWidget(m_parentWidget);
       
   354 
       
   355         // ### remove widget here
       
   356         QWidget *parentWidget = w->parentWidget();
       
   357         QDesignerFormEditorInterface *core = m_formWindow->core();
       
   358         QDesignerLayoutDecorationExtension *deco = qt_extension<QDesignerLayoutDecorationExtension*>(core->extensionManager(), parentWidget);
       
   359 
       
   360         if (deco)
       
   361             deco->removeWidget(w);
       
   362 
       
   363         w->setParent(container);
       
   364         w->setGeometry(rc);
       
   365 
       
   366         if (showIt)
       
   367             w->show();
       
   368     }
       
   369 
       
   370     LayoutInfo::deleteLayout(m_formWindow->core(), m_layoutBase);
       
   371 
       
   372     if (m_parentWidget != m_layoutBase && !qobject_cast<QMainWindow*>(m_layoutBase)) {
       
   373         m_formWindow->unmanageWidget(m_layoutBase);
       
   374         m_layoutBase->hide();
       
   375     } else {
       
   376         QMainWindow *mw = qobject_cast<QMainWindow*>(m_formWindow->mainContainer());
       
   377         if (m_layoutBase != m_formWindow->mainContainer() &&
       
   378                     (!mw || mw->centralWidget() != m_layoutBase))
       
   379             m_layoutBase->setGeometry(m_oldGeometry);
       
   380     }
       
   381 }
       
   382 
       
   383 void Layout::breakLayout()
       
   384 {
       
   385     typedef QMap<QWidget *, QRect> WidgetRectMap;
       
   386     WidgetRectMap rects;
       
   387     /* Store the geometry of the widgets. The idea is to give the user space
       
   388      * to rearrange them, so, we do a adjustSize() on them, unless they want
       
   389      * to grow (expanding widgets like QTextEdit), in which the geometry is
       
   390      * preserved. Note that historically, geometries were re-applied
       
   391      * only after breaking splitters. */
       
   392     foreach (QWidget *w, m_widgets) {
       
   393         const QRect geom = w->geometry();
       
   394         const QSize sizeHint = w->sizeHint();
       
   395         const bool restoreGeometry = sizeHint.isEmpty() || sizeHint.width() > geom.width() || sizeHint.height() > geom.height();
       
   396         rects.insert(w, restoreGeometry ? w->geometry() : QRect(geom.topLeft(), QSize()));
       
   397     }
       
   398     const QPoint m_layoutBasePos = m_layoutBase->pos();
       
   399     QDesignerWidgetDataBaseInterface *widgetDataBase = m_formWindow->core()->widgetDataBase();
       
   400 
       
   401     LayoutInfo::deleteLayout(m_formWindow->core(), m_layoutBase);
       
   402 
       
   403     const bool needReparent = (m_reparentLayoutWidget && qobject_cast<QLayoutWidget*>(m_layoutBase)) ||
       
   404                         qobject_cast<QSplitter*>(m_layoutBase)     ||
       
   405                         (!widgetDataBase->isContainer(m_layoutBase, false) &&
       
   406                           m_layoutBase != m_formWindow->mainContainer());
       
   407     const bool add = m_geometries.isEmpty();
       
   408 
       
   409     QMapIterator<QWidget*, QRect> it(rects);
       
   410     while (it.hasNext()) {
       
   411         it.next();
       
   412 
       
   413         QWidget *w = it.key();
       
   414         if (needReparent) {
       
   415             w->setParent(m_layoutBase->parentWidget(), 0);
       
   416             w->move(m_layoutBasePos + it.value().topLeft());
       
   417             w->show();
       
   418         }
       
   419 
       
   420         const QRect oldGeometry = it.value();
       
   421         if (oldGeometry.isEmpty()) {
       
   422             w->adjustSize();
       
   423         } else {
       
   424             w->resize(oldGeometry.size());
       
   425         }
       
   426 
       
   427         if (add)
       
   428             m_geometries.insert(w, QRect(w->pos(), w->size()));
       
   429     }
       
   430 
       
   431     if (needReparent) {
       
   432         m_layoutBase->hide();
       
   433         m_parentWidget = m_layoutBase->parentWidget();
       
   434         m_formWindow->unmanageWidget(m_layoutBase);
       
   435     } else {
       
   436         m_parentWidget = m_layoutBase;
       
   437     }
       
   438     updateWizardLayout(m_layoutBase);
       
   439 
       
   440     if (!m_widgets.isEmpty() && m_widgets.first() && m_widgets.first()->isVisibleTo(m_formWindow))
       
   441         m_formWindow->selectWidget(m_widgets.first());
       
   442     else
       
   443         m_formWindow->selectWidget(m_formWindow);
       
   444 }
       
   445 
       
   446 static QString suggestLayoutName(const char *className)
       
   447 {
       
   448     // Legacy
       
   449     if (!qstrcmp(className, "QHBoxLayout"))
       
   450         return QLatin1String("horizontalLayout");
       
   451     if (!qstrcmp(className, "QVBoxLayout"))
       
   452         return QLatin1String("verticalLayout");
       
   453     if (!qstrcmp(className, "QGridLayout"))
       
   454         return QLatin1String("gridLayout");
       
   455 
       
   456     return qtify(QString::fromUtf8(className));
       
   457 }
       
   458 QLayout *Layout::createLayout(int type)
       
   459 {
       
   460     Q_ASSERT(m_layoutType != LayoutInfo::HSplitter && m_layoutType != LayoutInfo::VSplitter);
       
   461     QLayout *layout = m_formWindow->core()->widgetFactory()->createLayout(m_layoutBase, 0, type);
       
   462     // set a name
       
   463     layout->setObjectName(suggestLayoutName(layout->metaObject()->className()));
       
   464     m_formWindow->ensureUniqueObjectName(layout);
       
   465     // QLayoutWidget
       
   466     QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(m_formWindow->core()->extensionManager(), layout);
       
   467     if (sheet && qobject_cast<QLayoutWidget*>(m_layoutBase)) {
       
   468         sheet->setProperty(sheet->indexOf(QLatin1String("leftMargin")), 0);
       
   469         sheet->setProperty(sheet->indexOf(QLatin1String("topMargin")), 0);
       
   470         sheet->setProperty(sheet->indexOf(QLatin1String("rightMargin")), 0);
       
   471         sheet->setProperty(sheet->indexOf(QLatin1String("bottomMargin")), 0);
       
   472     }
       
   473     return layout;
       
   474 }
       
   475 
       
   476 void Layout::reparentToLayoutBase(QWidget *w)
       
   477 {
       
   478     if (w->parent() != m_layoutBase) {
       
   479         w->setParent(m_layoutBase, 0);
       
   480         w->move(QPoint(0,0));
       
   481     }
       
   482 }
       
   483 
       
   484 namespace { // within qdesigner_internal
       
   485 
       
   486 // ----- PositionSortPredicate: Predicate to be usable as LessThan function to sort widgets by position
       
   487 class PositionSortPredicate {
       
   488 public:
       
   489     PositionSortPredicate(Qt::Orientation orientation) : m_orientation(orientation) {}
       
   490     bool operator()(const QWidget* w1, const QWidget* w2) {
       
   491         return m_orientation == Qt::Horizontal ? w1->x() < w2->x() : w1->y() < w2->y();
       
   492     }
       
   493     private:
       
   494     const Qt::Orientation m_orientation;
       
   495 };
       
   496 
       
   497 // -------- BoxLayout
       
   498 class BoxLayout : public Layout
       
   499 {
       
   500 public:
       
   501     BoxLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb,
       
   502               Qt::Orientation orientation);
       
   503 
       
   504     virtual void doLayout();
       
   505     virtual void sort();
       
   506 
       
   507 private:
       
   508     const Qt::Orientation m_orientation;
       
   509 };
       
   510 
       
   511 BoxLayout::BoxLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb,
       
   512                      Qt::Orientation orientation)  :
       
   513     Layout(wl, p, fw, lb, orientation == Qt::Horizontal ? LayoutInfo::HBox : LayoutInfo::VBox),
       
   514     m_orientation(orientation)
       
   515 {
       
   516 }
       
   517 
       
   518 void BoxLayout::sort()
       
   519 {
       
   520     QWidgetList wl = widgets();
       
   521     qStableSort(wl.begin(), wl.end(), PositionSortPredicate(m_orientation));
       
   522     setWidgets(wl);
       
   523 }
       
   524 
       
   525 void BoxLayout::doLayout()
       
   526 {
       
   527     bool needMove, needReparent;
       
   528     if (!prepareLayout(needMove, needReparent))
       
   529         return;
       
   530 
       
   531     QBoxLayout *layout = static_cast<QBoxLayout *>(createLayout(m_orientation == Qt::Horizontal ? LayoutInfo::HBox : LayoutInfo::VBox));
       
   532 
       
   533     QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem.
       
   534 
       
   535     const  QWidgetList::const_iterator cend = widgets().constEnd();
       
   536     for (QWidgetList::const_iterator it =  widgets().constBegin(); it != cend; ++it) {
       
   537         QWidget *w = *it;
       
   538         if (needReparent)
       
   539             reparentToLayoutBase(w);
       
   540 
       
   541         if (const Spacer *spacer = qobject_cast<const Spacer*>(w))
       
   542             layout->addWidget(w, 0, spacer->alignment());
       
   543         else
       
   544             layout->addWidget(w);
       
   545         w->show();
       
   546     }
       
   547     finishLayout(needMove, layout);
       
   548 }
       
   549 
       
   550 // --------  SplitterLayout
       
   551 class SplitterLayout : public Layout
       
   552 {
       
   553 public:
       
   554     SplitterLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb,
       
   555                    Qt::Orientation orientation);
       
   556 
       
   557     virtual void doLayout();
       
   558     virtual void sort();
       
   559 
       
   560 private:
       
   561     const Qt::Orientation m_orientation;
       
   562 };
       
   563 
       
   564 SplitterLayout::SplitterLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb,
       
   565                                Qt::Orientation orientation) :
       
   566     Layout(wl, p, fw, lb, orientation == Qt::Horizontal ? LayoutInfo::HSplitter : LayoutInfo::VSplitter),
       
   567     m_orientation(orientation)
       
   568 {
       
   569 }
       
   570 
       
   571 void SplitterLayout::sort()
       
   572 {
       
   573     QWidgetList wl = widgets();
       
   574     qStableSort(wl.begin(), wl.end(), PositionSortPredicate(m_orientation));
       
   575     setWidgets(wl);
       
   576 }
       
   577 
       
   578 void SplitterLayout::doLayout()
       
   579 {
       
   580     bool needMove, needReparent;
       
   581     if (!prepareLayout(needMove, needReparent))
       
   582         return;
       
   583 
       
   584     QSplitter *splitter = qobject_cast<QSplitter*>(layoutBaseWidget());
       
   585     Q_ASSERT(splitter != 0);
       
   586 
       
   587 
       
   588     const  QWidgetList::const_iterator cend = widgets().constEnd();
       
   589     for (QWidgetList::const_iterator it =  widgets().constBegin(); it != cend; ++it) {
       
   590         QWidget *w = *it;
       
   591         if (needReparent)
       
   592             reparentToLayoutBase(w);
       
   593         splitter->addWidget(w);
       
   594         w->show();
       
   595     }
       
   596 
       
   597     splitter->setOrientation(m_orientation);
       
   598     finishLayout(needMove);
       
   599 }
       
   600 
       
   601 //  ---------- Grid: Helper for laying out grids
       
   602 
       
   603 class Grid
       
   604 {
       
   605 public:
       
   606     enum Mode {
       
   607         GridLayout, // Arbitrary size/supports span
       
   608         FormLayout  // 2-column/no span
       
   609     };
       
   610 
       
   611     Grid(Mode mode);
       
   612     void resize(int nrows, int ncols);
       
   613 
       
   614     ~Grid();
       
   615 
       
   616     QWidget* cell(int row, int col) const { return m_cells[ row * m_ncols + col]; }
       
   617 
       
   618     void setCells(const QRect &c, QWidget* w);
       
   619 
       
   620     bool empty() const  { return m_nrows * m_ncols; }
       
   621     int numRows() const { return m_nrows; }
       
   622     int numCols() const { return m_ncols; }
       
   623 
       
   624     void simplify();
       
   625     bool locateWidget(QWidget* w, int& row, int& col, int& rowspan, int& colspan) const;
       
   626 
       
   627     QDebug debug(QDebug str) const;
       
   628 
       
   629 private:
       
   630     void setCell(int row, int col, QWidget* w) { m_cells[ row * m_ncols + col] = w; }
       
   631     void swapCells(int r1, int c1, int r2, int c2);
       
   632     void shrink();
       
   633     void reallocFormLayout();
       
   634     int countRow(int r, int c) const;
       
   635     int countCol(int r, int c) const;
       
   636     void setRow(int r, int c, QWidget* w, int count);
       
   637     void setCol(int r, int c, QWidget* w, int count);
       
   638     bool isWidgetStartCol(int c) const;
       
   639     bool isWidgetEndCol(int c) const;
       
   640     bool isWidgetStartRow(int r) const;
       
   641     bool isWidgetEndRow(int r) const;
       
   642     bool isWidgetTopLeft(int r, int c) const;
       
   643     void extendLeft();
       
   644     void extendRight();
       
   645     void extendUp();
       
   646     void extendDown();
       
   647     bool shrinkFormLayoutSpans();
       
   648 
       
   649     const Mode m_mode;
       
   650     int m_nrows;
       
   651     int m_ncols;
       
   652 
       
   653     QWidget** m_cells; // widget matrix w11, w12, w21...
       
   654 };
       
   655 
       
   656 Grid::Grid(Mode mode) :
       
   657     m_mode(mode),
       
   658     m_nrows(0),
       
   659     m_ncols(0),
       
   660     m_cells(0)
       
   661 {
       
   662 }
       
   663 
       
   664 Grid::~Grid()
       
   665 {
       
   666     delete [] m_cells;
       
   667 }
       
   668 
       
   669 void Grid::resize(int nrows, int ncols)
       
   670 {
       
   671     delete [] m_cells;
       
   672     m_cells = 0;
       
   673     m_nrows = nrows;
       
   674     m_ncols = ncols;
       
   675     if (const int allocSize = m_nrows * m_ncols) {
       
   676         m_cells = new QWidget*[allocSize];
       
   677         qFill(m_cells, m_cells + allocSize, static_cast<QWidget *>(0));
       
   678     }
       
   679 }
       
   680 
       
   681 QDebug Grid::debug(QDebug str) const
       
   682 {
       
   683     str << m_nrows << 'x' << m_ncols << '\n';
       
   684     QSet<QWidget *> widgets;
       
   685     const int cellCount = m_nrows * m_ncols;
       
   686     int row, col, rowspan, colspan;
       
   687     for (int c = 0; c < cellCount; c++)
       
   688         if (QWidget *w = m_cells[c])
       
   689             if (!widgets.contains(w)) {
       
   690                 widgets.insert(w);
       
   691                 locateWidget(w, row, col, rowspan, colspan);
       
   692                 str << w << " at " << row <<  col << rowspan << 'x' << colspan << '\n';
       
   693             }
       
   694     for (int r = 0; r < m_nrows; r++)
       
   695         for (int c = 0; c < m_ncols; c++)
       
   696             str << "At " << r << c << cell(r, c) << '\n';
       
   697 
       
   698     return str;
       
   699 }
       
   700 
       
   701 static inline QDebug operator<<(QDebug str, const Grid &g) { return g.debug(str); }
       
   702 
       
   703 void Grid::setCells(const QRect &c, QWidget* w)
       
   704 {
       
   705     const int bottom = c.top() + c.height();
       
   706     const int width =  c.width();
       
   707 
       
   708     for (int r = c.top(); r < bottom; r++) {
       
   709         QWidget **pos = m_cells + r * m_ncols + c.left();
       
   710         qFill(pos, pos + width, w);
       
   711     }
       
   712 }
       
   713 
       
   714 
       
   715 void Grid::swapCells(int r1, int c1, int r2, int c2)
       
   716 {
       
   717     QWidget *w1 = cell(r1, c1);
       
   718     setCell(r1, c1, cell(r2, c2));
       
   719     setCell(r2, c2, w1);
       
   720 }
       
   721 
       
   722 int Grid::countRow(int r, int c) const
       
   723 {
       
   724     QWidget* w = cell(r, c);
       
   725     int i = c + 1;
       
   726     while (i < m_ncols && cell(r, i) == w)
       
   727         i++;
       
   728     return i - c;
       
   729 }
       
   730 
       
   731 int Grid::countCol(int r, int c) const
       
   732 {
       
   733     QWidget* w = cell(r, c);
       
   734     int i = r + 1;
       
   735     while (i < m_nrows && cell(i, c) == w)
       
   736         i++;
       
   737     return i - r;
       
   738 }
       
   739 
       
   740 void Grid::setCol(int r, int c, QWidget* w, int count)
       
   741 {
       
   742     for (int i = 0; i < count; i++)
       
   743         setCell(r + i, c, w);
       
   744 }
       
   745 
       
   746 void Grid::setRow(int r, int c, QWidget* w, int count)
       
   747 {
       
   748     for (int i = 0; i < count; i++)
       
   749         setCell(r, c + i, w);
       
   750 }
       
   751 
       
   752 bool Grid::isWidgetStartCol(int c) const
       
   753 {
       
   754     for (int r = 0; r < m_nrows; r++) {
       
   755         if (cell(r, c) && ((c==0) || (cell(r, c)  != cell(r, c-1)))) {
       
   756             return true;
       
   757         }
       
   758     }
       
   759     return false;
       
   760 }
       
   761 
       
   762 bool Grid::isWidgetEndCol(int c) const
       
   763 {
       
   764     for (int r = 0; r < m_nrows; r++) {
       
   765         if (cell(r, c) && ((c == m_ncols-1) || (cell(r, c) != cell(r, c+1))))
       
   766             return true;
       
   767     }
       
   768     return false;
       
   769 }
       
   770 
       
   771 bool Grid::isWidgetStartRow(int r) const
       
   772 {
       
   773     for ( int c = 0; c < m_ncols; c++) {
       
   774         if (cell(r, c) && ((r==0) || (cell(r, c) != cell(r-1, c))))
       
   775             return true;
       
   776     }
       
   777     return false;
       
   778 }
       
   779 
       
   780 bool Grid::isWidgetEndRow(int r) const
       
   781 {
       
   782     for (int c = 0; c < m_ncols; c++) {
       
   783         if (cell(r, c) && ((r == m_nrows-1) || (cell(r, c) != cell(r+1, c))))
       
   784             return true;
       
   785     }
       
   786     return false;
       
   787 }
       
   788 
       
   789 
       
   790 bool Grid::isWidgetTopLeft(int r, int c) const
       
   791 {
       
   792     QWidget* w = cell(r, c);
       
   793     if (!w)
       
   794         return false;
       
   795     return (!r || cell(r-1, c) != w) && (!c || cell(r, c-1) != w);
       
   796 }
       
   797 
       
   798 void Grid::extendLeft()
       
   799 {
       
   800     for (int c = 1; c < m_ncols; c++) {
       
   801         for (int r = 0; r < m_nrows; r++) {
       
   802             QWidget* w = cell(r, c);
       
   803             if (!w)
       
   804                 continue;
       
   805 
       
   806             const int cc = countCol(r, c);
       
   807             int stretch = 0;
       
   808             for (int i = c-1; i >= 0; i--) {
       
   809                 if (cell(r, i))
       
   810                     break;
       
   811                 if (countCol(r, i) < cc)
       
   812                     break;
       
   813                 if (isWidgetEndCol(i))
       
   814                     break;
       
   815                 if (isWidgetStartCol(i)) {
       
   816                     stretch = c - i;
       
   817                     break;
       
   818                 }
       
   819             }
       
   820             if (stretch) {
       
   821                 for (int i = 0; i < stretch; i++)
       
   822                     setCol(r, c-i-1, w, cc);
       
   823             }
       
   824         }
       
   825     }
       
   826 }
       
   827 
       
   828 
       
   829 void Grid::extendRight()
       
   830 {
       
   831     for (int c = m_ncols - 2; c >= 0; c--) {
       
   832         for (int r = 0; r < m_nrows; r++) {
       
   833             QWidget* w = cell(r, c);
       
   834             if (!w)
       
   835                 continue;
       
   836             const int cc = countCol(r, c);
       
   837             int stretch = 0;
       
   838             for (int i = c+1; i < m_ncols; i++) {
       
   839                 if (cell(r, i))
       
   840                     break;
       
   841                 if (countCol(r, i) < cc)
       
   842                     break;
       
   843                 if (isWidgetStartCol(i))
       
   844                     break;
       
   845                 if (isWidgetEndCol(i)) {
       
   846                     stretch = i - c;
       
   847                     break;
       
   848                 }
       
   849             }
       
   850             if (stretch) {
       
   851                 for (int i = 0; i < stretch; i++)
       
   852                     setCol(r, c+i+1, w, cc);
       
   853             }
       
   854         }
       
   855     }
       
   856 
       
   857 }
       
   858 
       
   859 void Grid::extendUp()
       
   860 {
       
   861     for (int r = 1; r < m_nrows; r++) {
       
   862         for (int c = 0; c < m_ncols; c++) {
       
   863             QWidget* w = cell(r, c);
       
   864             if (!w)
       
   865                 continue;
       
   866             const int cr = countRow(r, c);
       
   867             int stretch = 0;
       
   868             for (int i = r-1; i >= 0; i--) {
       
   869                 if (cell(i, c))
       
   870                     break;
       
   871                 if (countRow(i, c) < cr)
       
   872                     break;
       
   873                 if (isWidgetEndRow(i))
       
   874                     break;
       
   875                 if (isWidgetStartRow(i)) {
       
   876                     stretch = r - i;
       
   877                     break;
       
   878                 }
       
   879             }
       
   880             if (stretch) {
       
   881                 for (int i = 0; i < stretch; i++)
       
   882                     setRow(r-i-1, c, w, cr);
       
   883             }
       
   884         }
       
   885     }
       
   886 }
       
   887 
       
   888 void Grid::extendDown()
       
   889 {
       
   890     for (int r = m_nrows - 2; r >= 0; r--) {
       
   891         for (int c = 0; c < m_ncols; c++) {
       
   892             QWidget* w = cell(r, c);
       
   893             if (!w)
       
   894                 continue;
       
   895             const int cr = countRow(r, c);
       
   896             int stretch = 0;
       
   897             for (int i = r+1; i < m_nrows; i++) {
       
   898                 if (cell(i, c))
       
   899                     break;
       
   900                 if (countRow(i, c) < cr)
       
   901                     break;
       
   902                 if (isWidgetStartRow(i))
       
   903                     break;
       
   904                 if (isWidgetEndRow(i)) {
       
   905                     stretch = i - r;
       
   906                     break;
       
   907                 }
       
   908             }
       
   909             if (stretch) {
       
   910                 for (int i = 0; i < stretch; i++)
       
   911                     setRow(r+i+1, c, w, cr);
       
   912             }
       
   913         }
       
   914     }
       
   915 }
       
   916 
       
   917 void Grid::simplify()
       
   918 {
       
   919     switch (m_mode) {
       
   920     case GridLayout:
       
   921         // Grid: Extend all widgets to occupy most space and delete
       
   922         // rows/columns that are not bordering on a widget
       
   923         extendLeft();
       
   924         extendRight();
       
   925         extendUp();
       
   926         extendDown();
       
   927         shrink();
       
   928         break;
       
   929     case FormLayout:
       
   930         // Form: First treat it as a grid to get the same behaviour
       
   931         // regarding spanning and shrinking. Then restrict the span to
       
   932         // the horizontal span possible in the form, simplify again
       
   933         // and spread the widgets over a 2-column layout
       
   934         extendLeft();
       
   935         extendRight();
       
   936         extendUp();
       
   937         extendDown();
       
   938         shrink();
       
   939         if (shrinkFormLayoutSpans())
       
   940             shrink();
       
   941         reallocFormLayout();
       
   942         break;
       
   943     }
       
   944 
       
   945 }
       
   946 
       
   947 void Grid::shrink()
       
   948 {
       
   949     //  tick off the occupied cols/rows (bordering on widget edges)
       
   950     QVector<bool> columns(m_ncols, false);
       
   951     QVector<bool> rows(m_nrows, false);
       
   952 
       
   953     for (int c = 0; c < m_ncols; c++)
       
   954         for (int r = 0; r < m_nrows; r++)
       
   955             if (isWidgetTopLeft(r, c))
       
   956                 rows[r] = columns[c] = true;
       
   957 
       
   958     // remove empty cols/rows
       
   959     const int simplifiedNCols = columns.count(true);
       
   960     const int simplifiedNRows = rows.count(true);
       
   961     if (simplifiedNCols ==  m_ncols && simplifiedNRows == m_nrows)
       
   962         return;
       
   963     // reallocate and copy omitting the empty cells
       
   964     QWidget **simplifiedCells = new QWidget*[simplifiedNCols * simplifiedNRows];
       
   965     qFill(simplifiedCells, simplifiedCells + simplifiedNCols * simplifiedNRows, static_cast<QWidget *>(0));
       
   966     QWidget **simplifiedPtr = simplifiedCells;
       
   967 
       
   968     for (int r = 0; r < m_nrows; r++)
       
   969         if (rows[r])
       
   970             for (int c = 0; c < m_ncols; c++)
       
   971                 if (columns[c]) {
       
   972                     if (QWidget *w = cell(r, c))
       
   973                         *simplifiedPtr = w;
       
   974                     simplifiedPtr++;
       
   975                 }
       
   976     Q_ASSERT(simplifiedPtr == simplifiedCells + simplifiedNCols * simplifiedNRows);
       
   977     delete [] m_cells;
       
   978     m_cells = simplifiedCells;
       
   979     m_nrows = simplifiedNRows;
       
   980     m_ncols = simplifiedNCols;
       
   981 }
       
   982 
       
   983 bool Grid::shrinkFormLayoutSpans()
       
   984 {
       
   985     bool shrunk = false;
       
   986     typedef  QSet<QWidget*> WidgetSet;
       
   987     // Determine unique set of widgets
       
   988     WidgetSet widgets;
       
   989     QWidget **end =  m_cells + m_ncols * m_nrows;
       
   990     for (QWidget **wptr = m_cells; wptr < end; wptr++)
       
   991         if (QWidget *w = *wptr)
       
   992             widgets.insert(w);
       
   993     // Restrict the widget span: max horizontal span at column 0: 2, anything else: 1
       
   994     const int maxRowSpan = 1;
       
   995     const WidgetSet::const_iterator cend = widgets.constEnd();
       
   996     for (WidgetSet::const_iterator it = widgets.constBegin(); it != cend ; ++it) {
       
   997         QWidget *w = *it;
       
   998         int row, col,  rowspan, colspan;
       
   999         locateWidget(w, row, col, rowspan, colspan);
       
  1000         const int maxColSpan = col == 0 ? 2 : 1;
       
  1001         const int newColSpan = qMin(colspan, maxColSpan);
       
  1002         const int newRowSpan = qMin(rowspan, maxRowSpan);
       
  1003         if (newColSpan != colspan || newRowSpan != rowspan) {
       
  1004             setCells(QRect(col, row, colspan, rowspan), 0);
       
  1005             setCells(QRect(col, row, newColSpan, newRowSpan), w);
       
  1006             shrunk = true;
       
  1007         }
       
  1008     }
       
  1009     return shrunk;
       
  1010 }
       
  1011 
       
  1012 void Grid::reallocFormLayout()
       
  1013 {
       
  1014     // Columns matching? -> happy!
       
  1015     if (m_ncols == FormLayoutColumns)
       
  1016         return;
       
  1017 
       
  1018     // If there are offset columns (starting past the field column),
       
  1019     // move them to the left and squeeze them. This also prevents the
       
  1020     // following reallocation from creating empty form rows.
       
  1021     int pastRightWidgetCount = 0;
       
  1022     if (m_ncols > FormLayoutColumns) {
       
  1023         for (int r = 0; r < m_nrows; r++) {
       
  1024             // Try to find a column where the form columns are empty and
       
  1025             // there are widgets further to the right.
       
  1026             if (cell(r, 0) == 0 && cell(r, 1) == 0) {
       
  1027                 int sourceCol = FormLayoutColumns;
       
  1028                 QWidget *firstWidget = 0;
       
  1029                 for ( ; sourceCol < m_ncols; sourceCol++)
       
  1030                     if (QWidget *w = cell(r, sourceCol)) {
       
  1031                         firstWidget = w;
       
  1032                         break;
       
  1033                     }
       
  1034                 if (firstWidget) {
       
  1035                     // Move/squeeze. Copy to beginning of column if it is a label, else field
       
  1036                     int targetCol = qobject_cast<QLabel*>(firstWidget) ? 0 : 1;
       
  1037                     for ( ; sourceCol < m_ncols; sourceCol++)
       
  1038                         if (QWidget *w = cell(r, sourceCol))
       
  1039                             setCell(r,  targetCol++, w);
       
  1040                     // Pad with zero
       
  1041                     for ( ; targetCol < m_ncols; targetCol++)
       
  1042                         setCell(r, targetCol, 0);
       
  1043                 }
       
  1044             }
       
  1045             // Any protruding widgets left on that row?
       
  1046             for (int c = FormLayoutColumns; c < m_ncols; c++)
       
  1047                 if (cell(r, c))
       
  1048                     pastRightWidgetCount++;
       
  1049         }
       
  1050     }
       
  1051     // Reallocate with 2 columns. Just insert the protruding ones as fields.
       
  1052     const int formNRows = m_nrows + pastRightWidgetCount;
       
  1053     QWidget **formCells = new QWidget*[FormLayoutColumns * formNRows];
       
  1054     qFill(formCells, formCells + FormLayoutColumns * formNRows, static_cast<QWidget *>(0));
       
  1055     QWidget **formPtr = formCells;
       
  1056     const int matchingColumns = qMin(m_ncols, static_cast<int>(FormLayoutColumns));
       
  1057     for (int r = 0; r < m_nrows; r++) {
       
  1058         int c = 0;
       
  1059         for ( ; c < matchingColumns; c++)               // Just copy over matching columns
       
  1060              *formPtr++ = cell(r, c);
       
  1061         formPtr += FormLayoutColumns - matchingColumns; // In case old format was 1 column
       
  1062         // protruding widgets: Insert as single-field rows
       
  1063         for ( ; c < m_ncols; c++)
       
  1064             if (QWidget *w = cell(r, c)) {
       
  1065                 formPtr++;
       
  1066                 *formPtr++ = w;
       
  1067             }
       
  1068     }
       
  1069     Q_ASSERT(formPtr == formCells + FormLayoutColumns * formNRows);
       
  1070     delete [] m_cells;
       
  1071     m_cells = formCells;
       
  1072     m_nrows = formNRows;
       
  1073     m_ncols = FormLayoutColumns;
       
  1074 }
       
  1075 
       
  1076 bool Grid::locateWidget(QWidget *w, int &row, int &col, int &rowspan, int &colspan) const
       
  1077 {
       
  1078     const int end = m_nrows * m_ncols;
       
  1079     const int startIndex = qFind(m_cells, m_cells + end, w) - m_cells;
       
  1080     if (startIndex == end)
       
  1081         return false;
       
  1082 
       
  1083     row = startIndex / m_ncols;
       
  1084     col = startIndex % m_ncols;
       
  1085     for (rowspan = 1; row + rowspan < m_nrows && cell(row + rowspan, col) == w; rowspan++) {}
       
  1086     for (colspan = 1; col + colspan < m_ncols && cell(row, col + colspan) == w; colspan++) {}
       
  1087     return true;
       
  1088 }
       
  1089 
       
  1090 // QGridLayout/QFormLayout Helpers: get item position/add item (overloads to make templates work)
       
  1091 
       
  1092 void getGridItemPosition(QGridLayout *gridLayout, int index, int *row, int *column, int *rowspan, int *colspan)
       
  1093 {
       
  1094     gridLayout->getItemPosition(index, row, column, rowspan, colspan);
       
  1095 }
       
  1096 
       
  1097 void addWidgetToGrid(QGridLayout *lt, QWidget * widget, int row, int column, int rowSpan, int columnSpan, Qt::Alignment alignment)
       
  1098 {
       
  1099     lt->addWidget(widget, row, column, rowSpan, columnSpan, alignment);
       
  1100 }
       
  1101 
       
  1102 inline void getGridItemPosition(QFormLayout *formLayout, int index, int *row, int *column, int *rowspan, int *colspan)
       
  1103 {
       
  1104     getFormLayoutItemPosition(formLayout, index, row, column, rowspan, colspan);
       
  1105 }
       
  1106 
       
  1107 inline void addWidgetToGrid(QFormLayout *lt, QWidget * widget, int row, int column, int, int columnSpan, Qt::Alignment)
       
  1108 {
       
  1109     formLayoutAddWidget(lt, widget, QRect(column, row,  columnSpan, 1), false);
       
  1110 }
       
  1111 
       
  1112 // ----------- Base template for grid like layouts
       
  1113 template <class GridLikeLayout, int LayoutType, int GridMode>
       
  1114 class GridLayout : public Layout
       
  1115 {
       
  1116 public:
       
  1117     GridLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb);
       
  1118 
       
  1119     virtual void doLayout();
       
  1120     virtual void sort()                  { setWidgets(buildGrid(widgets())); }
       
  1121 
       
  1122 protected:
       
  1123     QWidget *widgetAt(GridLikeLayout *layout, int row, int column) const;
       
  1124 
       
  1125 protected:
       
  1126     QWidgetList buildGrid(const QWidgetList &);
       
  1127     Grid m_grid;
       
  1128 };
       
  1129 
       
  1130 template <class GridLikeLayout, int LayoutType, int GridMode>
       
  1131 GridLayout<GridLikeLayout, LayoutType, GridMode>::GridLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb) :
       
  1132     Layout(wl, p, fw, lb, LayoutInfo::Grid),
       
  1133     m_grid(static_cast<Grid::Mode>(GridMode))
       
  1134 {
       
  1135 }
       
  1136 
       
  1137 template <class GridLikeLayout, int LayoutType, int GridMode>
       
  1138 QWidget *GridLayout<GridLikeLayout, LayoutType, GridMode>::widgetAt(GridLikeLayout *layout, int row, int column) const
       
  1139 {
       
  1140     int index = 0;
       
  1141     while (QLayoutItem *item = layout->itemAt(index)) {
       
  1142         if (item->widget()) {
       
  1143             int r, c, rowspan, colspan;
       
  1144             getGridItemPosition(layout, index, &r, &c, &rowspan, &colspan);
       
  1145             if (row == r && column == c)
       
  1146                 return item->widget();
       
  1147         }
       
  1148         ++index;
       
  1149     }
       
  1150     return 0;
       
  1151 }
       
  1152 
       
  1153 template <class GridLikeLayout, int LayoutType, int GridMode>
       
  1154 void GridLayout<GridLikeLayout, LayoutType, GridMode>::doLayout()
       
  1155 {
       
  1156     bool needMove, needReparent;
       
  1157     if (!prepareLayout(needMove, needReparent))
       
  1158         return;
       
  1159 
       
  1160     GridLikeLayout *layout =  static_cast<GridLikeLayout *>(createLayout(LayoutType));
       
  1161 
       
  1162     if (m_grid.empty())
       
  1163         sort();
       
  1164 
       
  1165     QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem.
       
  1166 
       
  1167     const  QWidgetList::const_iterator cend = widgets().constEnd();
       
  1168     for (QWidgetList::const_iterator it =  widgets().constBegin(); it != cend; ++it) {
       
  1169         QWidget *w = *it;
       
  1170         int r = 0, c = 0, rs = 0, cs = 0;
       
  1171 
       
  1172         if (m_grid.locateWidget(w, r, c, rs, cs)) {
       
  1173             if (needReparent)
       
  1174                 reparentToLayoutBase(w);
       
  1175 
       
  1176             Qt::Alignment alignment = Qt::Alignment(0);
       
  1177             if (const Spacer *spacer = qobject_cast<const Spacer*>(w))
       
  1178                 alignment = spacer->alignment();
       
  1179 
       
  1180             if (rs * cs == 1) {
       
  1181                 addWidgetToGrid(layout, w, r, c, 1, 1, alignment);
       
  1182             } else {
       
  1183                 addWidgetToGrid(layout, w, r, c, rs, cs, alignment);
       
  1184             }
       
  1185 
       
  1186             w->show();
       
  1187         } else {
       
  1188             qDebug("ooops, widget '%s' does not fit in layout", w->objectName().toUtf8().constData());
       
  1189         }
       
  1190     }
       
  1191 
       
  1192     QLayoutSupport::createEmptyCells(layout);
       
  1193 
       
  1194     finishLayout(needMove, layout);
       
  1195 }
       
  1196 
       
  1197 // Remove duplicate entries (Remove next, if equal to current)
       
  1198 void removeIntVecDuplicates(QVector<int> &v)
       
  1199 {
       
  1200     if (v.size() < 2)
       
  1201         return;
       
  1202 
       
  1203     for (QVector<int>::iterator current = v.begin() ; (current != v.end()) && ((current+1) != v.end()) ; )
       
  1204         if ( (*current == *(current+1)) )
       
  1205             v.erase(current+1);
       
  1206         else
       
  1207             ++current;
       
  1208 }
       
  1209 
       
  1210 // Ensure a non-zero size for a widget geometry (squeezed spacers)
       
  1211 inline QRect expandGeometry(const QRect &rect)
       
  1212 {
       
  1213     return rect.isEmpty() ? QRect(rect.topLeft(), rect.size().expandedTo(QSize(1, 1))) : rect;
       
  1214 }
       
  1215 
       
  1216 template <class GridLikeLayout, int LayoutType, int GridMode>
       
  1217 QWidgetList GridLayout<GridLikeLayout, LayoutType, GridMode>::buildGrid(const QWidgetList &widgetList)
       
  1218 {
       
  1219     if (widgetList.empty())
       
  1220         return QWidgetList();
       
  1221 
       
  1222     // Pixel to cell conversion:
       
  1223     // By keeping a list of start'n'stop values (x & y) for each widget,
       
  1224     // it is possible to create a very small grid of cells to represent
       
  1225     // the widget layout.
       
  1226     // -----------------------------------------------------------------
       
  1227 
       
  1228     // We need a list of both start and stop values for x- & y-axis
       
  1229     const int widgetCount = widgetList.size();
       
  1230     QVector<int> x( widgetCount * 2 );
       
  1231     QVector<int> y( widgetCount * 2 );
       
  1232 
       
  1233     // Using push_back would look nicer, but operator[] is much faster
       
  1234     int index  = 0;
       
  1235     for (int i = 0; i < widgetCount; ++i) {
       
  1236         const QRect widgetPos = expandGeometry(widgetList.at(i)->geometry());
       
  1237         x[index]   = widgetPos.left();
       
  1238         x[index+1] = widgetPos.right();
       
  1239         y[index]   = widgetPos.top();
       
  1240         y[index+1] = widgetPos.bottom();
       
  1241         index += 2;
       
  1242     }
       
  1243 
       
  1244     qSort(x);
       
  1245     qSort(y);
       
  1246 
       
  1247     // Remove duplicate x entries (Remove next, if equal to current)
       
  1248     removeIntVecDuplicates(x);
       
  1249     removeIntVecDuplicates(y);
       
  1250 
       
  1251     // Note that left == right and top == bottom for size 1 items; reserve
       
  1252     // enough space
       
  1253     m_grid.resize(y.size(), x.size());
       
  1254 
       
  1255     const  QWidgetList::const_iterator cend = widgetList.constEnd();
       
  1256     for (QWidgetList::const_iterator it = widgetList.constBegin(); it != cend; ++it) {
       
  1257         QWidget *w = *it;
       
  1258         // Mark the cells in the grid that contains a widget
       
  1259         const QRect widgetPos = expandGeometry(w->geometry());
       
  1260         QRect c(0, 0, 0, 0); // rect of columns/rows
       
  1261 
       
  1262         // From left til right (not including)
       
  1263         const int leftIdx = x.indexOf(widgetPos.left());
       
  1264         Q_ASSERT(leftIdx != -1);
       
  1265         c.setLeft(leftIdx);
       
  1266         c.setRight(leftIdx);
       
  1267         for (int cw=leftIdx; cw<x.size(); cw++)
       
  1268             if (x[cw] <  widgetPos.right())
       
  1269                 c.setRight(cw);
       
  1270             else
       
  1271                 break;
       
  1272         // From top til bottom (not including)
       
  1273         const int topIdx = y.indexOf(widgetPos.top());
       
  1274         Q_ASSERT(topIdx != -1);
       
  1275         c.setTop(topIdx);
       
  1276         c.setBottom(topIdx);
       
  1277         for (int ch=topIdx; ch<y.size(); ch++)
       
  1278             if (y[ch] <  widgetPos.bottom())
       
  1279                 c.setBottom(ch);
       
  1280             else
       
  1281                 break;
       
  1282         m_grid.setCells(c, w); // Mark cellblock
       
  1283     }
       
  1284 
       
  1285     m_grid.simplify();
       
  1286 
       
  1287     QWidgetList ordered;
       
  1288     for (int i = 0; i < m_grid.numRows(); i++)
       
  1289         for (int j = 0; j < m_grid.numCols(); j++) {
       
  1290             QWidget *w = m_grid.cell(i, j);
       
  1291             if (w && !ordered.contains(w))
       
  1292                 ordered.append(w);
       
  1293         }
       
  1294     return ordered;
       
  1295 }
       
  1296 } // anonymous
       
  1297 
       
  1298 Layout* Layout::createLayout(const QWidgetList &widgets,  QWidget *parentWidget,
       
  1299                              QDesignerFormWindowInterface *fw,
       
  1300                              QWidget *layoutBase, LayoutInfo::Type layoutType)
       
  1301 {
       
  1302     switch (layoutType) {
       
  1303     case LayoutInfo::Grid:
       
  1304         return new GridLayout<QGridLayout, LayoutInfo::Grid, Grid::GridLayout>(widgets, parentWidget, fw, layoutBase);
       
  1305     case LayoutInfo::HBox:
       
  1306     case LayoutInfo::VBox: {
       
  1307         const Qt::Orientation orientation = layoutType == LayoutInfo::HBox ? Qt::Horizontal : Qt::Vertical;
       
  1308         return new BoxLayout(widgets, parentWidget, fw, layoutBase, orientation);
       
  1309     }
       
  1310     case LayoutInfo::HSplitter:
       
  1311     case LayoutInfo::VSplitter: {
       
  1312         const Qt::Orientation orientation = layoutType == LayoutInfo::HSplitter ? Qt::Horizontal : Qt::Vertical;
       
  1313         return new SplitterLayout(widgets, parentWidget, fw, layoutBase, orientation);
       
  1314     }
       
  1315     case LayoutInfo::Form:
       
  1316         return new GridLayout<QFormLayout, LayoutInfo::Form, Grid::FormLayout>(widgets, parentWidget, fw, layoutBase);
       
  1317     default:
       
  1318         break;
       
  1319     }
       
  1320     Q_ASSERT(0);
       
  1321     return 0;
       
  1322 }
       
  1323 
       
  1324 } // namespace qdesigner_internal
       
  1325 
       
  1326 QT_END_NAMESPACE