1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the designer application of the Qt Toolkit.
7
** This file may be distributed under the terms of the Q Public License
8
** as defined by Trolltech AS of Norway and appearing in the file
9
** LICENSE.QPL included in the packaging of this file.
11
** This file may be distributed and/or modified under the terms of the
12
** GNU General Public License version 2 as published by the Free Software
13
** Foundation and appearing in the file LICENSE.GPL included in the
14
** packaging of this file.
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
17
** information about Qt Commercial License Agreements.
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
21
** Contact info@trolltech.com if any conditions of this licensing are
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27
****************************************************************************/
30
#include "qdesigner_widget_p.h"
31
#include "qlayout_widget_p.h"
32
#include "spacer_widget_p.h"
33
#include "layoutdecoration.h"
35
#include <QtDesigner/QtDesigner>
37
#include <QtCore/qdebug.h>
38
#include <QtCore/QVector>
40
#include <QtGui/qevent.h>
41
#include <QtGui/QGridLayout>
42
#include <QtGui/QPainter>
43
#include <QtGui/QBitmap>
44
#include <QtGui/QSplitter>
45
#include <QtGui/QMainWindow>
47
class FriendlyBoxLayout: public QBoxLayout
50
inline FriendlyBoxLayout(Direction d) : QBoxLayout(d) { Q_ASSERT(0); }
52
friend void insert_into_box_layout(QBoxLayout *box, int index, QWidget *widget);
55
static bool operator<(const QPointer<QWidget> &p1, const QPointer<QWidget> &p2)
57
return p1.operator->() < p2.operator->();
60
void add_to_box_layout(QBoxLayout *box, QWidget *widget)
62
if (QLayoutWidget *layoutWidget = qobject_cast<QLayoutWidget*>(widget)) {
63
QLayoutWidgetItem *item = new QLayoutWidgetItem(layoutWidget);
67
box->addWidget(widget);
71
void insert_into_box_layout(QBoxLayout *box, int index, QWidget *widget)
73
if (QLayoutWidget *layoutWidget = qobject_cast<QLayoutWidget*>(widget)) {
74
QLayoutWidgetItem *item = new QLayoutWidgetItem(layoutWidget);
76
static_cast<FriendlyBoxLayout*>(box)->insertItem(index, item);
78
box->insertWidget(index, widget);
82
void add_to_grid_layout(QGridLayout *grid, QWidget *widget, int r, int c, int rs, int cs, Qt::Alignment align)
84
if (QLayoutWidget *layoutWidget = qobject_cast<QLayoutWidget*>(widget)) {
85
QLayoutWidgetItem *item = new QLayoutWidgetItem(layoutWidget);
87
grid->addItem(item, r, c, rs, cs, align);
89
grid->addWidget(widget, r, c, rs, cs, align);
94
\class Layout layout.h
95
\brief Baseclass for layouting widgets in the Designer
97
Classes derived from this abstract base class are used for layouting
98
operations in the Designer.
102
/*! \a p specifies the parent of the layoutBase \a lb. The parent
103
might be changed in setup(). If the layoutBase is a
104
container, the parent and the layoutBase are the same. Also they
105
always have to be a widget known to the designer (e.g. in the case
106
of the tabwidget parent and layoutBase are the tabwidget and not the
107
page which actually gets laid out. For actual usage the correct
108
widget is found later by Layout.)
111
Layout::Layout(const QList<QWidget*> &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb, bool splitter)
112
: m_widgets(wl), m_parentWidget(p), formWindow(fw), isBreak(false), useSplitter(splitter)
116
oldGeometry = layoutBase->geometry();
123
int Layout::margin() const
125
if (layoutBase && layoutBase->layout())
126
return layoutBase->layout()->margin();
128
qWarning("unknown margin");
132
int Layout::spacing() const
134
if (layoutBase && layoutBase->layout())
135
return layoutBase->layout()->spacing();
137
qWarning("unknown spacing");
141
/*! The widget list we got in the constructor might contain too much
142
widgets (like widgets with different parents, already laid out
143
widgets, etc.). Here we set up the list and so the only the "best"
144
widgets get laid out.
149
startPoint = QPoint(32767, 32767);
151
// Go through all widgets of the list we got. As we can only
152
// layout widgets which have the same parent, we first do some
153
// sorting which means create a list for each parent containing
154
// its child here. After that we keep working on the list of
155
// childs which has the most entries.
156
// Widgets which are already laid out are thrown away here too
158
QMultiMap<QWidget*, QWidget*> lists;
159
foreach (QWidget *w, m_widgets) {
160
QWidget *p = w->parentWidget();
162
if (p && LayoutInfo::layoutType(formWindow->core(), p) != LayoutInfo::NoLayout
163
&& formWindow->core()->metaDataBase()->item(p->layout()) != 0)
169
QList<QWidget*> lastList;
170
QList<QWidget*> parents = lists.keys();
171
foreach (QWidget *p, parents) {
172
QList<QWidget*> children = lists.values(p);
174
if (children.count() > lastList.count())
179
// If we found no list (because no widget did fit at all) or the
180
// best list has only one entry and we do not layout a container,
182
QDesignerWidgetDataBaseInterface *widgetDataBase = formWindow->core()->widgetDataBase();
183
if (lastList.count() < 2 &&
185
(!widgetDataBase->isContainer(layoutBase, false) &&
186
layoutBase != formWindow->mainContainer()))
189
startPoint = QPoint(0, 0);
193
// Now we have a new and clean widget list, which makes sense
195
m_widgets = lastList;
196
// Also use the only correct parent later, so store it
198
Q_ASSERT(m_widgets.isEmpty() == false);
200
m_parentWidget = formWindow->core()->widgetFactory()->widgetOfContainer(m_widgets.first()->parentWidget());
201
// Now calculate the position where the layout-meta-widget should
202
// be placed and connect to widgetDestroyed() signals of the
203
// widgets to get informed if one gets deleted to be able to
204
// handle that and do not crash in this case
205
foreach (QWidget *w, m_widgets) {
206
connect(w, SIGNAL(destroyed()), this, SLOT(widgetDestroyed()));
207
startPoint = QPoint(qMin(startPoint.x(), w->x()), qMin(startPoint.y(), w->y()));
208
QRect rc(w->geometry());
209
geometries.insert(w, rc);
210
// Change the Z-order, as saving/loading uses the Z-order for
211
// writing/creating widgets and this has to be the same as in
212
// the layout. Else saving + loading will give different results
219
void Layout::widgetDestroyed()
221
if (sender() && sender()->isWidgetType()) {
222
const QWidget *w = static_cast<const QWidget*>(sender());
223
m_widgets.removeAt(m_widgets.indexOf(const_cast<QWidget*>(w)));
227
bool Layout::prepareLayout(bool &needMove, bool &needReparent)
229
if (!m_widgets.count())
232
foreach (QWidget *widget, m_widgets) {
236
needMove = !layoutBase;
237
needReparent = needMove || qobject_cast<QLayoutWidget*>(layoutBase) || qobject_cast<QSplitter*>(layoutBase);
239
QDesignerWidgetFactoryInterface *widgetFactory = formWindow->core()->widgetFactory();
240
QDesignerMetaDataBaseInterface *metaDataBase = formWindow->core()->metaDataBase();
242
if (layoutBase == 0) {
243
QString baseWidgetClassName = QLatin1String("QLayoutWidget");
246
baseWidgetClassName = QLatin1String("QSplitter");
248
layoutBase = widgetFactory->createWidget(baseWidgetClassName, widgetFactory->containerOfWidget(m_parentWidget));
250
LayoutInfo::deleteLayout(formWindow->core(), layoutBase);
253
metaDataBase->add(layoutBase);
255
Q_ASSERT(layoutBase->layout() == 0 || metaDataBase->item(layoutBase->layout()) == 0);
260
void Layout::finishLayout(bool needMove, QLayout *layout)
262
if (m_parentWidget == layoutBase)
266
layoutBase->move(startPoint);
268
QRect g(layoutBase->pos(), layoutBase->size());
270
if (LayoutInfo::layoutType(formWindow->core(), layoutBase->parentWidget()) == LayoutInfo::NoLayout && !isBreak)
271
layoutBase->adjustSize();
273
layoutBase->setGeometry(oldGeometry);
276
layout->invalidate();
279
if (qobject_cast<QLayoutWidget*>(layoutBase) || qobject_cast<QSplitter*>(layoutBase)) {
280
formWindow->manageWidget(layoutBase);
281
formWindow->selectWidget(layoutBase);
285
void Layout::undoLayout()
287
if (!m_widgets.count())
290
formWindow->selectWidget(layoutBase, false);
292
QDesignerWidgetFactoryInterface *widgetFactory = formWindow->core()->widgetFactory();
293
QMapIterator<QPointer<QWidget>, QRect> it(geometries);
294
while (it.hasNext()) {
300
QWidget* w = it.key();
301
QRect rc = it.value();
303
bool showIt = w->isVisibleTo(formWindow);
304
QWidget *container = widgetFactory->containerOfWidget(m_parentWidget);
306
// ### remove widget here
307
QWidget *parentWidget = w->parentWidget();
308
QDesignerFormEditorInterface *core = formWindow->core();
309
QDesignerLayoutDecorationExtension *deco = qt_extension<QDesignerLayoutDecorationExtension*>(core->extensionManager(), parentWidget);
312
deco->removeWidget(w);
314
w->setParent(container);
321
LayoutInfo::deleteLayout(formWindow->core(), layoutBase);
323
if (m_parentWidget != layoutBase && !qobject_cast<QMainWindow*>(layoutBase)) {
324
formWindow->unmanageWidget(layoutBase);
327
layoutBase->setGeometry(oldGeometry);
330
QWidget *ww = m_widgets.size() ? m_widgets.front() : formWindow;
331
formWindow->selectWidget(ww);
334
void Layout::breakLayout()
336
QMap<QWidget*, QRect> rects;
337
foreach (QWidget *w, m_widgets) {
338
rects.insert(w, w->geometry());
341
QPoint layoutBasePos = layoutBase->pos();
342
QDesignerWidgetDataBaseInterface *widgetDataBase = formWindow->core()->widgetDataBase();
344
LayoutInfo::deleteLayout(formWindow->core(), layoutBase);
346
bool needReparent = qobject_cast<QLayoutWidget*>(layoutBase) ||
347
qobject_cast<QSplitter*>(layoutBase) ||
348
(!widgetDataBase->isContainer(layoutBase, false) &&
349
layoutBase != formWindow->mainContainer());
350
bool needResize = qobject_cast<QSplitter*>(layoutBase);
351
bool add = geometries.isEmpty();
353
QMapIterator<QWidget*, QRect> it(rects);
354
while (it.hasNext()) {
357
QWidget *w = it.key();
359
w->setParent(layoutBase->parentWidget(), 0);
360
w->move(layoutBasePos + it.value().topLeft());
365
w->resize(it.value().size());
368
geometries.insert(w, QRect(w->pos(), w->size()));
373
m_parentWidget = layoutBase->parentWidget();
374
formWindow->unmanageWidget(layoutBase);
376
m_parentWidget = layoutBase;
379
if (m_widgets.first() && m_widgets.first()->isVisibleTo(formWindow))
380
formWindow->selectWidget(m_widgets.first());
382
formWindow->selectWidget(formWindow);
385
HorizontalLayout::HorizontalLayout(const QList<QWidget*> &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb, bool splitter)
386
: Layout(wl, p, fw, lb, splitter)
390
void HorizontalLayout::sort()
392
HorizontalLayoutList l(m_widgets);
397
void HorizontalLayout::doLayout()
399
bool needMove, needReparent;
400
if (!prepareLayout(needMove, needReparent))
403
QDesignerWidgetFactoryInterface *widgetFactory = formWindow->core()->widgetFactory();
404
QHBoxLayout *layout = (QHBoxLayout*) widgetFactory->createLayout(layoutBase, 0, LayoutInfo::HBox);
406
foreach (QWidget *w, m_widgets) {
407
if (needReparent && w->parent() != layoutBase) {
408
w->setParent(layoutBase, 0);
409
w->move(QPoint(0,0));
413
QSplitter *splitter = qobject_cast<QSplitter*>(layoutBase);
414
Q_ASSERT(splitter != 0);
415
splitter->addWidget(w);
417
if (Spacer *spacer = qobject_cast<Spacer*>(w))
418
layout->addWidget(w, 0, spacer->alignment());
420
add_to_box_layout(layout, w);
425
if (QSplitter *splitter = qobject_cast<QSplitter*>(layoutBase))
426
splitter->setOrientation(Qt::Horizontal);
428
finishLayout(needMove, layout);
431
VerticalLayout::VerticalLayout(const QList<QWidget*> &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb, bool splitter)
432
: Layout(wl, p, fw, lb, splitter)
436
void VerticalLayout::sort()
438
VerticalLayoutList l(m_widgets);
443
void VerticalLayout::doLayout()
445
bool needMove, needReparent;
446
if (!prepareLayout(needMove, needReparent))
449
QDesignerWidgetFactoryInterface *widgetFactory = formWindow->core()->widgetFactory();
451
QVBoxLayout *layout = (QVBoxLayout*) widgetFactory->createLayout(layoutBase, 0, LayoutInfo::VBox);
452
Q_ASSERT(layout != 0);
454
foreach (QWidget *w, m_widgets) {
455
if (needReparent && w->parent() != layoutBase) {
456
w->setParent(layoutBase, 0);
457
w->move(QPoint(0,0));
461
QSplitter *splitter = qobject_cast<QSplitter*>(layoutBase);
462
Q_ASSERT(splitter != 0);
463
splitter->addWidget(w);
465
if (Spacer *spacer = qobject_cast<Spacer*>(w))
466
layout->addWidget(w, 0, spacer->alignment());
468
add_to_box_layout(layout, w);
473
if (QSplitter *splitter = qobject_cast<QSplitter*>(layoutBase)) { // ### useSplitter??
474
splitter->setOrientation(Qt::Vertical);
477
finishLayout(needMove, layout);
483
Grid(int rows, int cols);
486
QWidget* cell(int row, int col) const { return cells[ row * ncols + col]; }
487
void setCell(int row, int col, QWidget* w) { cells[ row*ncols + col] = w; }
488
void setCells(QRect c, QWidget* w) {
489
for (int rows = c.bottom()-c.top(); rows >= 0; rows--)
490
for (int cols = c.right()-c.left(); cols >= 0; cols--) {
491
setCell(c.top()+rows, c.left()+cols, w);
494
int numRows() const { return nrows; }
495
int numCols() const { return ncols; }
498
bool locateWidget(QWidget* w, int& row, int& col, int& rowspan, int& colspan);
502
int countRow(int r, int c) const;
503
int countCol(int r, int c) const;
504
void setRow(int r, int c, QWidget* w, int count);
505
void setCol(int r, int c, QWidget* w, int count);
506
bool isWidgetStartCol(int c) const;
507
bool isWidgetEndCol(int c) const;
508
bool isWidgetStartRow(int r) const;
509
bool isWidgetEndRow(int r) const;
510
bool isWidgetTopLeft(int r, int c) const;
522
Grid::Grid(int r, int c)
525
cells = new QWidget*[ r * c ];
526
memset(cells, 0, sizeof(cells) * r * c);
527
rows = new bool[ r ];
528
cols = new bool[ c ];
539
int Grid::countRow(int r, int c) const
541
QWidget* w = cell(r, c);
543
while (i < ncols && cell(r, i) == w)
548
int Grid::countCol(int r, int c) const
550
QWidget* w = cell(r, c);
552
while (i < nrows && cell(i, c) == w)
557
void Grid::setCol(int r, int c, QWidget* w, int count)
559
for (int i = 0; i < count; i++)
560
setCell(r + i, c, w);
563
void Grid::setRow(int r, int c, QWidget* w, int count)
565
for (int i = 0; i < count; i++)
566
setCell(r, c + i, w);
569
bool Grid::isWidgetStartCol(int c) const
572
for (r = 0; r < nrows; r++) {
573
if (cell(r, c) && ((c==0) || (cell(r, c) != cell(r, c-1)))) {
580
bool Grid::isWidgetEndCol(int c) const
583
for (r = 0; r < nrows; r++) {
584
if (cell(r, c) && ((c == ncols-1) || (cell(r, c) != cell(r, c+1))))
590
bool Grid::isWidgetStartRow(int r) const
593
for (c = 0; c < ncols; c++) {
594
if (cell(r, c) && ((r==0) || (cell(r, c) != cell(r-1, c))))
600
bool Grid::isWidgetEndRow(int r) const
603
for (c = 0; c < ncols; c++) {
604
if (cell(r, c) && ((r == nrows-1) || (cell(r, c) != cell(r+1, c))))
611
bool Grid::isWidgetTopLeft(int r, int c) const
613
QWidget* w = cell(r, c);
616
return (!r || cell(r-1, c) != w) && (!c || cell(r, c-1) != w);
619
void Grid::extendLeft()
622
for (c = 1; c < ncols; c++) {
623
for (r = 0; r < nrows; r++) {
624
QWidget* w = cell(r, c);
628
int cc = countCol(r, c);
630
for (i = c-1; i >= 0; i--) {
633
if (countCol(r, i) < cc)
635
if (isWidgetEndCol(i))
637
if (isWidgetStartCol(i)) {
643
for (i = 0; i < stretch; i++)
644
setCol(r, c-i-1, w, cc);
651
void Grid::extendRight()
654
for (c = ncols - 2; c >= 0; c--) {
655
for (r = 0; r < nrows; r++) {
656
QWidget* w = cell(r, c);
659
int cc = countCol(r, c);
661
for (i = c+1; i < ncols; i++) {
664
if (countCol(r, i) < cc)
666
if (isWidgetStartCol(i))
668
if (isWidgetEndCol(i)) {
674
for (i = 0; i < stretch; i++)
675
setCol(r, c+i+1, w, cc);
682
void Grid::extendUp()
685
for (r = 1; r < nrows; r++) {
686
for (c = 0; c < ncols; c++) {
687
QWidget* w = cell(r, c);
690
int cr = countRow(r, c);
692
for (i = r-1; i >= 0; i--) {
695
if (countRow(i, c) < cr)
697
if (isWidgetEndRow(i))
699
if (isWidgetStartRow(i)) {
705
for (i = 0; i < stretch; i++)
706
setRow(r-i-1, c, w, cr);
712
void Grid::extendDown()
715
for (r = nrows - 2; r >= 0; r--) {
716
for (c = 0; c < ncols; c++) {
717
QWidget* w = cell(r, c);
720
int cr = countRow(r, c);
722
for (i = r+1; i < nrows; i++) {
725
if (countRow(i, c) < cr)
727
if (isWidgetStartRow(i))
729
if (isWidgetEndRow(i)) {
735
for (i = 0; i < stretch; i++)
736
setRow(r+i+1, c, w, cr);
743
void Grid::simplify()
756
for (c = 0; c < ncols; c++)
759
for (r = 0; r < nrows; r++)
762
for (c = 0; c < ncols; c++) {
763
for (r = 0; r < nrows; r++) {
764
if (isWidgetTopLeft(r, c)) {
772
bool Grid::locateWidget(QWidget *w, int &row, int &col, int &rowspan, int &colspan)
776
for (c = 0; c < ncols; c++) {
777
for (r = 0; r < nrows; r++) {
778
if (cell(r, c) == w) {
780
for (r2 = 1; r2 <= r; r2++) {
785
for (c2 = 1; c2 <= c; c2++) {
790
for (r2 = r ; r2 < nrows && cell(r2, c) == w; r2++) {
795
for (c2 = c; c2 < ncols && cell(r, c2) == w; c2++) {
809
GridLayout::GridLayout(const QList<QWidget*> &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb, const QSize &res)
810
: Layout(wl, p, fw, lb), resolution(res)
815
GridLayout::~GridLayout()
820
QWidget *GridLayout::widgetAt(QGridLayout *layout, int row, int column) const
823
while (QLayoutItem *item = layout->itemAt(index)) {
824
if (item->widget()) {
825
int r, c, rowspan, colspan;
826
layout->getItemPosition(index, &r, &c, &rowspan, &colspan);
827
if (row == r && column == c)
828
return item->widget();
835
void GridLayout::doLayout()
837
bool needMove, needReparent;
838
if (!prepareLayout(needMove, needReparent))
841
QDesignerWidgetFactoryInterface *ff = formWindow->core()->widgetFactory();
842
QGridLayout *layout = static_cast<QGridLayout*>(ff->createLayout(layoutBase, 0, LayoutInfo::Grid));
847
foreach (QWidget *w, m_widgets) {
848
int r = 0, c = 0, rs = 0, cs = 0;
850
if (grid->locateWidget(w, r, c, rs, cs)) {
851
if (needReparent && w->parent() != layoutBase) {
852
w->setParent(layoutBase, 0);
853
w->move(QPoint(0,0));
856
Qt::Alignment alignment = Qt::Alignment(0);
857
if (Spacer *spacer = qobject_cast<Spacer*>(w))
858
alignment = spacer->alignment();
861
add_to_grid_layout(layout, w, r, c, 1, 1, alignment);
863
add_to_grid_layout(layout, w, r, c, rs, cs, alignment);
868
qWarning("ooops, widget '%s' does not fit in layout", w->objectName().toUtf8().constData());
872
QLayoutSupport::createEmptyCells(layout);
874
finishLayout(needMove, layout);
877
void GridLayout::sort()
882
void GridLayout::buildGrid()
884
if (!m_widgets.count())
887
QMap<int, int> x_dict;
888
QMap<int, int> y_dict;
890
foreach (QWidget *w, m_widgets) {
891
QRect g = w->geometry();
893
x_dict.insert(g.left(), g.left());
894
x_dict.insert(g.right(), g.right());
896
y_dict.insert(g.top(), g.top());
897
y_dict.insert(g.bottom(), g.bottom());
900
QList<int> x = x_dict.keys();
901
QList<int> y = y_dict.keys();
903
// Pixel to cell conversion:
904
// By keeping a list of start'n'stop values (x & y) for each widget,
905
// it is possible to create a very small grid of cells to represent
906
// the widget layout.
907
// -----------------------------------------------------------------
909
// We need a list of both start and stop values for x- & y-axis
910
QVector<int> x( m_widgets.count()*2 );
911
QVector<int> y( m_widgets.count()*2 );
913
// Using push_back would look nicer, but operator[] is much faster
916
for (int i = 0; i < m_widgets.size(); ++i) {
918
QRect widgetPos = w->geometry();
919
x[index] = widgetPos.left();
920
x[index+1] = widgetPos.right();
921
y[index] = widgetPos.top();
922
y[index+1] = widgetPos.bottom();
929
// Remove duplicate x enteries (Remove next, if equal to current)
931
for (QVector<int>::iterator current = x.begin() ;
932
(current != x.end()) && ((current+1) != x.end()) ; )
933
if ( (*current == *(current+1)) )
939
// Remove duplicate y enteries (Remove next, if equal to current)
941
for (QVector<int>::iterator current = y.begin() ;
942
(current != y.end()) && ((current+1) != y.end()) ; )
943
if ( (*current == *(current+1)) )
951
grid = new Grid(y.size() - 1, x.size() - 1);
953
// Mark the cells in the grid that contains a widget
954
foreach (QWidget *w, m_widgets) {
955
QRect widgetPos = w->geometry();
959
// From left til right (not including)
960
for (int cw=0; cw<x.size(); cw++) {
961
if (x[cw] == widgetPos.left())
963
if (x[cw] < widgetPos.right())
967
// From top til bottom (not including)
968
for (int ch=0; ch<y.size(); ch++) {
969
if (y[ch] == widgetPos.top() )
971
if (y[ch] < widgetPos.bottom())
975
grid->setCells(c, w); // Mark cellblock