1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the widgets module 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
****************************************************************************/
29
#include "qdockwidgetlayout_p.h"
30
#include "qdockwidget.h"
32
#include <qapplication.h>
38
#include <private/qlayoutengine_p.h>
40
#include "qdockwidget_p.h"
41
#include "qdockwidgetseparator_p.h"
42
#include "qmainwindowlayout_p.h"
44
// #define LAYOUT_DEBUG
45
#if defined(LAYOUT_DEBUG)
48
# define DEBUG if(false)qDebug
51
// #define LAYOUT_DEBUG_VERBOSE
52
#if defined(LAYOUT_DEBUG_VERBOSE)
53
# define VDEBUG qDebug
55
# define VDEBUG if(false)qDebug
59
static inline bool canGrow(Qt::Orientation o, const QSizePolicy &sp)
60
{ return (o == Qt::Horizontal ? sp.horizontalPolicy() : sp.verticalPolicy()) & QSizePolicy::GrowFlag; }
62
enum { StateFlagVisible = 1, StateFlagFloating = 2 };
64
QDockWidgetLayout::QDockWidgetLayout(Qt::DockWidgetArea a, Qt::Orientation o)
65
: QLayout(static_cast<QWidget*>(0)), area(a), orientation(o), save_layout_info(0),
66
relayout_type(QInternal::RelayoutNormal)
67
{ connect(this, SIGNAL(emptied()), SLOT(maybeDelete()), Qt::QueuedConnection); }
69
QDockWidgetLayout::~QDockWidgetLayout()
71
for (int i = 0; i < layout_info.count(); ++i) {
72
const QDockWidgetLayoutInfo &info = layout_info.at(i);
74
info.item->widget()->deleteLater();
75
if (!info.item->layout())
80
void QDockWidgetLayout::saveState(QDataStream &stream) const
82
stream << (uchar) Marker;
83
stream << (uchar) orientation;
84
stream << (layout_info.count() + 1) / 2;
85
for (int i = 0; i < layout_info.count(); ++i) {
86
const QDockWidgetLayoutInfo &info = layout_info.at(i);
89
if (info.item->widget()) {
90
const QDockWidget * const widget =
91
qobject_cast<QDockWidget *>(info.item->widget());
92
stream << (uchar) WidgetMarker;
93
if (widget->objectName().isEmpty()) {
94
qWarning("QMainWindow::saveState(): 'objectName' not set for QDockWidget "
95
"%p '%s', using 'windowTitle' instead.",
96
widget, widget->windowTitle().toLocal8Bit().constData());
97
stream << widget->windowTitle();
99
stream << widget->objectName();
102
if (!widget->isHidden())
103
flags |= StateFlagVisible;
104
if (widget->isFloating())
105
flags |= StateFlagFloating;
107
if (widget->isFloating()) {
108
stream << widget->x();
109
stream << widget->y();
110
stream << widget->width();
111
stream << widget->height();
113
stream << info.cur_pos;
114
stream << info.cur_size;
115
stream << info.min_size;
116
stream << info.max_size;
118
} else if (info.item->layout()) {
119
stream << (uchar) Marker;
120
stream << info.cur_pos;
121
stream << info.cur_size;
122
stream << info.min_size;
123
stream << info.max_size;
124
const QDockWidgetLayout * const layout =
125
qobject_cast<const QDockWidgetLayout *>(info.item->layout());
126
layout->saveState(stream);
131
bool QDockWidgetLayout::restoreState(QDataStream &stream)
136
if (marker != Marker)
139
relayout_type = QInternal::RelayoutDropped;
143
orientation = static_cast<Qt::Orientation>(o);
145
QList<QDockWidget *> widgets = qFindChildren<QDockWidget *>(parentWidget());
146
for (int i = 0; i < size; ++i) {
148
stream >> nextMarker;
149
switch (nextMarker) {
153
stream >> objectName;
158
QDockWidget *widget = 0;
159
for (int t = 0; t < widgets.size(); ++t) {
160
if (widgets.at(t)->objectName() == objectName) {
161
widget = widgets.at(t);
166
qWarning("QMainWindow::restoreState(): cannot find a QDockWidget named "
167
"'%s', trying to match using 'windowTitle' instead.",
168
objectName.toLocal8Bit().constData());
169
// try matching the window title
170
for (int t = 0; t < widgets.size(); ++t) {
171
if (widgets.at(t)->windowTitle() == objectName) {
172
widget = widgets.at(t);
177
qWarning("QMainWindow::restoreState(): cannot find a QDockWidget with "
178
"matching 'windowTitle' (looking for '%s').",
179
objectName.toLocal8Bit().constData());
180
// discard size/position data for unknown widget
181
QDockWidgetLayoutInfo info(0);
182
stream >> info.cur_pos;
183
stream >> info.cur_size;
184
stream >> info.min_size;
185
stream >> info.max_size;
190
QDockWidgetLayoutInfo &info = insert(-1, new QWidgetItem(widget));
191
if (flags & StateFlagFloating) {
193
widget->setFloating(true);
200
widget->resize(w, h);
201
widget->setVisible(flags & StateFlagVisible);
203
stream >> info.cur_pos;
204
stream >> info.cur_size;
205
stream >> info.min_size;
206
stream >> info.max_size;
207
widget->setFloating(false);
208
widget->setVisible(flags & StateFlagVisible);
215
QDockWidgetLayout *layout = new QDockWidgetLayout(area, orientation);
216
layout->setParent(this);
217
QDockWidgetLayoutInfo &info = insert(-1, layout);
218
stream >> info.cur_pos;
219
stream >> info.cur_size;
220
stream >> info.min_size;
221
stream >> info.max_size;
222
if (!layout->restoreState(stream)) {
223
relayout_type = QInternal::RelayoutNormal;
231
relayout_type = QInternal::RelayoutNormal;
236
relayout_type = QInternal::RelayoutNormal;
240
int QDockWidgetLayout::count() const
242
qWarning("QDockWidgetLayout::count() is wrong");
243
return layout_info.count();
246
QLayoutItem *QDockWidgetLayout::itemAt(int index) const
248
VDEBUG("QDockWidgetLayout::itemAt: index %d (%d)", index, layout_info.count());
251
for (int i = 0; i < layout_info.count(); ++i) {
252
const QDockWidgetLayoutInfo &info = layout_info.at(i);
256
VDEBUG("END, widget: pos %3d index %3d", i, x);
261
VDEBUG("END, not found");
266
QLayoutItem *QDockWidgetLayout::takeAt(int index)
268
DEBUG("QDockWidgetLayout::takeAt: index %d", index);
271
for (int i = 0; i < layout_info.count(); ++i) {
272
const QDockWidgetLayoutInfo &info = layout_info.at(i);
276
QLayoutItem *layoutitem = info.item;
277
QWidget *widget = info.item->widget();
279
VDEBUG("QDockWidgetLayout::takeAt: layoutitem '%s'\n"
280
" index %3d pos %4d size %4d %s",
281
(layoutitem->widget()
282
? layoutitem->widget()->objectName().toLatin1().constData()
284
i, info.cur_pos, info.cur_size,
286
? widget->objectName().toLatin1().constData()
290
layout_info.removeAt(i);
292
// remove the separator
293
if (i == layout_info.count()) {
294
// we removed the last dockwidget, so we need to remove
295
// the separator that was above it
300
QDockWidgetLayoutInfo &sep_info = layout_info[i];
301
Q_ASSERT(sep_info.is_sep);
303
if (!save_layout_info) {
304
delete sep_info.item->widget();
305
delete sep_info.item;
308
VDEBUG(" removing separator at %d", i);
309
layout_info.removeAt(i);
312
#ifdef LAYOUT_DEBUG_VERBOSE
316
if (layout_info.isEmpty()) {
317
if (relayout_type == QInternal::RelayoutDropped) {
318
// probably splitting...
319
} else if (!save_layout_info) {
322
QLayout *parentLayout = qobject_cast<QLayout *>(parent());
324
parentLayout->removeItem(this);
328
VDEBUG("END of remove");
337
void QDockWidgetLayout::addItem(QLayoutItem *layoutitem)
339
if (relayout_type == QInternal::RelayoutDropped && layoutitem->layout()) {
340
// dropping a nested layout!
344
(void) insert(-1, layoutitem);
349
void QDockWidgetLayout::setGeometry(const QRect &rect)
351
VDEBUG("QDockWidgetLayout::setGeometry: width %4d height %4d", rect.width(), rect.height());
353
QLayout::setGeometry(rect);
355
if (relayout_type != QInternal::RelayoutDragging) {
357
for (int i = 0; i < layout_info.count(); ++i) {
358
const QDockWidgetLayoutInfo &info = layout_info.at(i);
362
const bool empty = info.item->isEmpty();
364
layout_info.at(i - 1).item->widget()->setHidden(first || empty);
370
QVector<QLayoutStruct> a(layout_info.count());
372
const int separator_extent =
373
qApp->style()->pixelMetric(QStyle::PM_DockWidgetSeparatorExtent);
375
for (x = 0; x < layout_info.count(); ++x) {
376
const QDockWidgetLayoutInfo &info = layout_info.at(x);
378
QLayoutStruct &ls = a[x];
380
ls.empty = info.item->isEmpty();
381
if (ls.empty && !info.is_dropped)
385
VDEBUG(" separator");
386
ls.sizeHint = ls.minimumSize = ls.maximumSize = separator_extent;
388
const QSizePolicy &sp =
390
? info.item->widget()->sizePolicy()
391
: QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
393
if (info.is_dropped) {
394
Q_ASSERT(relayout_type != QInternal::RelayoutNormal);
395
Q_ASSERT(info.cur_size > 0);
397
// item was just dropped into the layout
398
ls.minimumSize = info.cur_size;
399
ls.sizeHint = info.cur_size;
400
ls.maximumSize = info.cur_size;
401
// do not use stretch for dropped items... we want them in the
402
// exact size we specify
405
ls.minimumSize = pick(orientation, info.item->minimumSize());
406
ls.maximumSize = pick(orientation, info.item->maximumSize());
408
if (canGrow(orientation, sp)) {
409
ls.sizeHint = ls.minimumSize;
410
ls.stretch = info.cur_size == -1
411
? pick(orientation, info.item->sizeHint())
415
ls.sizeHint = info.cur_size == -1
416
? pick(orientation, info.item->sizeHint())
422
ls.sizeHint = qMax(ls.sizeHint, ls.minimumSize);
423
ls.minimumSize = qMin(ls.minimumSize, ls.maximumSize);
425
VDEBUG(" dockwidget cur %4d min %4d max %4d, hint %4d stretch %4d "
426
"expansive %d empty %d",
427
info.cur_size, ls.minimumSize, ls.maximumSize, ls.sizeHint, ls.stretch, ls.expansive, ls.empty);
431
qGeomCalc(a, 0, a.count(), 0, pick(orientation, rect.size()), 0);
434
VDEBUG(" final placement:");
435
for (int i = 0; i < a.count(); ++i) {
436
const QLayoutStruct &ls = a.at(i);
440
QDockWidgetLayoutInfo &info = layout_info[i];
443
VDEBUG(" separator cur %4d", ls.size);
445
VDEBUG(" dockwidget cur %4d min %4d max %4d pos %4d",
446
ls.size, ls.minimumSize, ls.maximumSize, ls.pos);
450
if (relayout_type == QInternal::RelayoutDragging) {
451
// we are testing a layout, so don't actually change
452
// anything... but make sure that we update all the dummy
453
// items with the correct geometry
457
info.cur_pos = ls.pos;
458
info.cur_size = ls.size;
459
info.min_size = ls.minimumSize;
460
info.max_size = ls.maximumSize;
462
info.item->setGeometry(((orientation == Qt::Horizontal) ?
463
QRect(rect.x() + ls.pos, rect.y(), ls.size, rect.height()) :
464
QRect(rect.x(), rect.y() + ls.pos, rect.width(), ls.size)));
471
QSize QDockWidgetLayout::minimumSize() const
473
if (!minSize.isValid()) {
474
VDEBUG("QDockWidget::minimumSize");
476
int size = 0, perp = 0;
477
const int sep_extent =
478
QApplication::style()->pixelMetric(QStyle::PM_DockWidgetSeparatorExtent);
480
for (int it = 0; it < layout_info.count(); ++it) {
481
const QDockWidgetLayoutInfo &info = layout_info.at(it);
484
s = p = (info.item->widget()->isHidden()) ? 0 : sep_extent;
486
QSize sz = info.item->minimumSize();
487
s = pick(orientation, sz);
488
p = pick_perp(orientation, sz);
491
VDEBUG(" size %d perp %d", s, p);
493
perp = qMax(perp, p);
496
VDEBUG("END: size %4d perp %4d", size, perp);
498
minSize = (orientation == Qt::Horizontal) ? QSize(size, perp) : QSize(perp, size);
504
QSize QDockWidgetLayout::sizeHint() const
506
if (!szHint.isValid()) {
507
VDEBUG("QDockWidget::sizeHint");
509
int size = 0, perp = 0;
510
const int sep_extent =
511
QApplication::style()->pixelMetric(QStyle::PM_DockWidgetSeparatorExtent);
513
for (int it = 0; it < layout_info.count(); ++it) {
514
const QDockWidgetLayoutInfo &info = layout_info.at(it);
517
s = p = (info.item->widget()->isHidden()) ? 0 : sep_extent;
519
QSize sz = info.item->sizeHint();
520
s = pick(orientation, sz);
521
p = pick_perp(orientation, sz);
524
VDEBUG(" size %d perp %d", s, p);
526
perp = qMax(perp, p);
529
VDEBUG("END: size %4d perp %4d", size, perp);
531
szHint = (orientation == Qt::Horizontal) ? QSize(size, perp) : QSize(perp, size);
536
void QDockWidgetLayout::invalidate()
538
if (relayout_type != QInternal::RelayoutDragging) {
539
QLayout::invalidate();
540
minSize = szHint = QSize();
544
bool QDockWidgetLayout::isEmpty() const
546
for (int i = 0; i < layout_info.count(); ++i) {
547
const QDockWidgetLayoutInfo &info = layout_info.at(i);
550
if (!info.item->isEmpty())
556
void QDockWidgetLayout::setOrientation(Qt::Orientation o)
564
QDockWidgetLayoutInfo &QDockWidgetLayout::insert(int index, QLayoutItem *layoutitem)
566
DEBUG("QDockWidgetLayout::insert: index %d, layoutitem '%s'",
567
index < 0 ? layout_info.count() : index,
568
((layoutitem->widget() || layoutitem->layout())
569
? (layoutitem->layout()
570
? layoutitem->layout()->objectName().toLatin1().constData()
571
: layoutitem->widget()->objectName().toLatin1().constData())
574
bool append = index < 0;
579
// skip to the specified index
580
while (it < layout_info.count()) {
582
VDEBUG("found index %d at %d", index, it);
586
if (it + 1 == layout_info.count()) {
588
++it; // ran of the end, force append
596
if (it == layout_info.count()) {
599
VDEBUG("inserting at %d (%d of %d)", it, idx, index);
601
// insert the dockwidget
602
VDEBUG(" inserting dockwidget at %d", it);
603
QDockWidgetLayoutInfo dockwidget_info(layoutitem);
605
layout_info.insert(it++, dockwidget_info);
607
// insert a separator
608
QLayoutItem *sep_item = 0;
609
Q_ASSERT(!layout_info[it].is_sep);
610
QDockWidgetSeparator *sep = new QDockWidgetSeparator(this, parentWidget());
611
sep_item = new QWidgetItem(sep);
613
QDockWidgetLayoutInfo sep_info(sep_item);
615
VDEBUG(" inserting separator at %d", it);
616
layout_info.insert(it++, sep_info);
618
return layout_info[save_it];
622
Q_ASSERT(append == true);
624
// append the dockwidget
625
if (!layout_info.isEmpty()) {
626
Q_ASSERT(!layout_info.last().is_sep);
628
// insert a separator before the dockwidget
629
QLayoutItem *sep_item = 0;
630
QDockWidgetSeparator *sep = new QDockWidgetSeparator(this, parentWidget());
631
sep_item = new QWidgetItem(sep);
633
QDockWidgetLayoutInfo sep_info(sep_item);
635
layout_info.append(sep_info);
638
QDockWidgetLayoutInfo dockwidget_info(layoutitem);
639
layout_info.append(dockwidget_info);
641
return layout_info.last();
644
void QDockWidgetLayout::dump()
646
DEBUG("QDockWidgetLayout::dump");
647
for (int i = 0; i < layout_info.count(); ++i) {
648
const QDockWidgetLayoutInfo &info = layout_info.at(i);
651
DEBUG(" index %3d pos %4d size %4d SEPARATOR", i, info.cur_pos, info.cur_size);
652
} else if (info.item->layout()) {
653
DEBUG(" index %3d pos %4d size %4d %s", i, info.cur_pos, info.cur_size,
655
? info.item->layout()->objectName().toLatin1().constData()
657
QDockWidgetLayout *l = qobject_cast<QDockWidgetLayout *>(info.item->layout());
661
DEBUG(" index %3d pos %4d size %4d %s", i, info.cur_pos, info.cur_size,
663
? info.item->widget()->objectName().toLatin1().constData()
667
DEBUG("END of dump");
670
void QDockWidgetLayout::saveLayoutInfo()
672
Q_ASSERT(save_layout_info == 0);
673
save_layout_info = new QList<QDockWidgetLayoutInfo>(layout_info);
675
for (int i = 0; i < layout_info.count(); ++i) {
676
const QDockWidgetLayoutInfo &info = layout_info.at(i);
679
if (!info.item->layout())
682
QDockWidgetLayout *l = qobject_cast<QDockWidgetLayout *>(info.item->layout());
688
void QDockWidgetLayout::resetLayoutInfo()
690
Q_ASSERT(save_layout_info != 0);
691
layout_info = *save_layout_info;
693
for (int i = 0; i < layout_info.count(); ++i) {
694
const QDockWidgetLayoutInfo &info = layout_info.at(i);
697
if (!info.item->layout())
700
QDockWidgetLayout *l = qobject_cast<QDockWidgetLayout *>(info.item->layout());
702
l->resetLayoutInfo();
706
void QDockWidgetLayout::discardLayoutInfo()
708
Q_ASSERT(save_layout_info != 0);
709
delete save_layout_info;
710
save_layout_info = 0;
712
for (int i = 0; i < layout_info.count(); ++i) {
713
const QDockWidgetLayoutInfo &info = layout_info.at(i);
716
if (!info.item->layout())
719
QDockWidgetLayout *l = qobject_cast<QDockWidgetLayout *>(info.item->layout());
721
l->discardLayoutInfo();
725
QPoint QDockWidgetLayout::constrain(QDockWidgetSeparator *sep, int delta)
727
VDEBUG("QDockWidgetLayout::constrain: delta %4d", delta);
728
QList<QDockWidgetLayoutInfo> local_list;
730
for (int pass = 0; pass < 2; ++pass) {
731
VDEBUG(" PASS %d", pass);
733
During pass 1, we compute the feedback constraint. During
734
pass 2, we update layout_info using the calculated
739
local_list = save_layout_info ? *save_layout_info : layout_info;
740
QMutableListIterator<QDockWidgetLayoutInfo> f_it(local_list), b_it(local_list);
741
while (f_it.hasNext()) {
742
const QDockWidgetLayoutInfo &info = f_it.peekNext();
743
if (info.is_sep && qobject_cast<QDockWidgetSeparator*>(info.item->widget()) == sep) break;
747
// at this point, the iterator is just before 'sep'
749
// get info for 'sep->prev' and move to just after sep->prev
750
while (b_it.hasPrevious()) {
751
if (!b_it.peekPrevious().item->isEmpty())
753
(void) b_it.previous(); // skip item
754
(void) b_it.previous(); // skip sep
756
QDockWidgetLayoutInfo &info1 = b_it.previous(); // move to just after previous separator
758
(void)f_it.next(); // move to before sep->next
760
// get info for 'sep->next' and move to just before next separator
761
while (f_it.hasNext()) {
762
if (!f_it.peekNext().item->isEmpty())
764
(void) f_it.next(); // skip sep
765
(void) f_it.next(); // skip item
767
QDockWidgetLayoutInfo &info2 = f_it.next();
769
// subtract delta to the current size of sep->next
770
int x = info2.cur_size;
771
info2.cur_size -= delta;
773
// constrain the new size according to our min/max size
774
info2.cur_size = qMax(info2.cur_size, info2.min_size);
775
info2.cur_size = qMin(info2.cur_size, info2.max_size);
776
int delta2 = x - info2.cur_size;
778
VDEBUG("next: new %4d old %4d", info2.cur_size, x);
780
if (delta2 != delta) {
781
// distribute space to widgets below if possible
782
int remain = delta - delta2;
784
VDEBUG("remaining below: %d", remain);
786
if (f_it.hasNext()) {
787
while (remain != 0) {
788
(void)f_it.next(); // skip separator
790
QDockWidgetLayoutInfo &f_info = f_it.next();
791
if (!f_info.item->isEmpty()) {
792
// subtract delta to the current size
794
f_info.cur_size -= remain;
796
// constrain the new size according to our min/max size
797
f_info.cur_size = qMax(f_info.cur_size, f_info.min_size);
798
f_info.cur_size = qMin(f_info.cur_size, f_info.max_size);
799
remain -= x - f_info.cur_size;
801
VDEBUG(" done, new %4d old %4d remaining %d", f_info.cur_size, x, remain);
804
if (!f_it.hasNext()) break; // at the end
808
// constrain delta to the absolute minimum of all windows below 'sep'
812
// add delta from current size of sep->next
814
info1.cur_size += delta;
816
// constrain the delta according to our min/max size
817
info1.cur_size = qMax(info1.cur_size, info1.min_size);
818
info1.cur_size = qMin(info1.cur_size, info1.max_size);
819
int delta1 = info1.cur_size - x;
821
VDEBUG("prev: new %4d old %4d", info1.cur_size, x);
823
if (delta1 != delta) {
824
// distribute space to widgets above if possible
825
int remain = delta - delta1;
827
VDEBUG("remaining above: %d", remain);
829
if (b_it.hasPrevious()) {
830
while (remain != 0) {
831
// (void)b_it.prev(); // skip separator
833
QDockWidgetLayoutInfo &b_info = b_it.previous();
834
if (!b_info.item->isEmpty()) {
835
// add delta from current size of sep->next
837
b_info.cur_size += remain;
839
// constrain the delta according to our min/max size
840
b_info.cur_size = qMax(b_info.cur_size, b_info.min_size);
841
b_info.cur_size = qMin(b_info.cur_size, b_info.max_size);
842
remain -= b_info.cur_size - x;
844
VDEBUG(" done, new %4d old %4d remaining %d", b_info.cur_size, x, remain);
847
if (!b_it.hasPrevious()) break; // at the beginning
851
// constrain delta to the absolute minimum of all windows above 'sep'
855
VDEBUG(" end of pass %d, delta %4d", pass, delta);
858
// save the calculated
859
layout_info = local_list;
863
return orientation == Qt::Horizontal ? QPoint(delta, 0) : QPoint(0, delta);
866
void QDockWidgetLayout::relayout(QInternal::RelayoutType type)
868
QInternal::RelayoutType save_type = relayout_type;
869
relayout_type = type;
870
setGeometry(geometry());
871
relayout_type = save_type;
876
QDockWidgetLayout::Location QDockWidgetLayout::locate(const QPoint &p) const
878
// figure out where the dockwidget goes in the layout
879
const int pos = pick(orientation, p);
880
const bool horizontal = orientation == Qt::Horizontal;
882
DEBUG() << " locate: mouse at" << p;
886
for (int i = 0; i < layout_info.count(); ++i) {
887
const QDockWidgetLayoutInfo &info = layout_info.at(i);
890
if (info.item->isEmpty())
893
if (pos < (info.cur_pos + info.cur_size - 1)) {
894
const QRect current =
896
? QRect(info.cur_pos, 0, info.cur_size, geometry().height())
897
: QRect(0, info.cur_pos, geometry().width(), info.cur_size));
899
current.topLeft() + QPoint(current.width() / 2, current.height() / 2);
900
const int dx = qAbs(p.x() - p2.x()),
901
dy = qAbs(p.y() - p2.y());
903
location.area = ((dx > dy)
905
? Qt::LeftDockWidgetArea
906
: Qt::RightDockWidgetArea)
908
? Qt::TopDockWidgetArea
909
: Qt::BottomDockWidgetArea));
910
DEBUG() << " result: index" << location.index << "area" << location.area;
915
location.index = layout_info.count() - 1;
916
location.area = (horizontal ? Qt::RightDockWidgetArea : Qt::BottomDockWidgetArea);
917
DEBUG() << " result: index" << location.index << "area" << location.area << "(off-end)";
921
static Qt::DockWidgetAreas getAllowedAreas(const QRect &r,
924
const int separatorExtent)
926
Qt::DockWidgetAreas allowedAreas = Qt::AllDockWidgetAreas;
927
if (!r.contains(QRect(r.x(),
929
sz1.width() + sz2.width() + separatorExtent,
930
qMax(sz1.height(), sz2.height())))) {
931
DEBUG() << " cannot split horizontally";
932
allowedAreas &= ~(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
934
if (!r.contains(QRect(r.x(),
936
qMax(sz1.width(), sz2.width()),
937
sz1.height() + sz2.height() +separatorExtent))) {
938
DEBUG() << " cannot split vertically";
939
allowedAreas &= ~(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
944
static QRect trySplit(Qt::Orientation orientation,
945
Qt::DockWidgetArea &area,
946
Qt::DockWidgetAreas allowedAreas,
951
if (allowedAreas == 0) {
952
// cannot split anywhere
956
if ((allowedAreas & area) != area) {
957
// cannot split in the desired location, pick another one
958
switch (orientation) {
961
case Qt::TopDockWidgetArea:
962
case Qt::BottomDockWidgetArea:
963
DEBUG() << " cannot split vertically, trying horizontally";
964
area = ((p.x() < r.center().x())
965
? Qt::LeftDockWidgetArea
966
: Qt::RightDockWidgetArea);
971
if ((allowedAreas & area) != area) {
973
case Qt::LeftDockWidgetArea:
974
area = Qt::RightDockWidgetArea;
975
DEBUG() << " cannot split left, trying right";
977
case Qt::RightDockWidgetArea:
978
area = Qt::LeftDockWidgetArea;
979
DEBUG() << " cannot split right, trying left";
985
if ((allowedAreas & area) != area) {
986
DEBUG() << " cannot split, trying vertically";
987
area = ((p.y() < r.center().y())
988
? Qt::TopDockWidgetArea
989
: Qt::BottomDockWidgetArea);
994
case Qt::LeftDockWidgetArea:
995
case Qt::RightDockWidgetArea:
996
DEBUG() << " cannot split horizontally, trying vertically";
997
area = ((p.y() < r.center().y())
998
? Qt::TopDockWidgetArea
999
: Qt::BottomDockWidgetArea);
1004
if ((allowedAreas & area) != area) {
1006
case Qt::TopDockWidgetArea:
1007
DEBUG() << " cannot split top, trying bottom";
1008
area = Qt::BottomDockWidgetArea;
1010
case Qt::BottomDockWidgetArea:
1011
DEBUG() << " cannot split bottom, trying top";
1012
area = Qt::TopDockWidgetArea;
1018
if ((allowedAreas & area) != area) {
1019
DEBUG() << " cannot split, trying horizontally";
1020
area = ((p.x() < r.center().x())
1021
? Qt::LeftDockWidgetArea
1022
: Qt::RightDockWidgetArea);
1026
Q_ASSERT_X(false, "QDockWidgetLayout", "internal error");
1030
if ((allowedAreas & area) != area) {
1031
// still cannot split, give up
1032
DEBUG() << " cannot split at all, giving up";
1038
case Qt::LeftDockWidgetArea:
1041
(r.width() - separatorExtent) / 2,
1044
case Qt::RightDockWidgetArea:
1045
rect.setRect(r.right() - (r.width() - separatorExtent - 1) / 2,
1047
(r.width() - separatorExtent + 1) / 2,
1050
case Qt::TopDockWidgetArea:
1054
(r.height() - separatorExtent) / 2);
1056
case Qt::BottomDockWidgetArea:
1058
r.bottom() - (r.height() - separatorExtent - 1) / 2,
1060
(r.height() - separatorExtent + 1) / 2);
1063
Q_ASSERT_X(false, "QDockWidgetLayout", "internal error");
1071
QRect QDockWidgetLayout::place(QDockWidget *dockwidget, const QRect &r, const QPoint &mouse)
1073
DEBUG("QDockWidgetLayout::place");
1075
const QPoint p = parentWidget()->mapFromGlobal(mouse);
1076
Location location = locate(p - geometry().topLeft());
1077
const QDockWidgetLayoutInfo &info = layout_info.at(location.index);
1078
const bool horizontal = orientation == Qt::Horizontal;
1082
if (info.item->layout()) {
1083
// forward the place to the nested layout
1084
QDockWidgetLayout *l = qobject_cast<QDockWidgetLayout *>(info.item->layout());
1086
DEBUG(" forwarding...");
1087
target = l->place(dockwidget, r, mouse);
1088
DEBUG("END of QDockWidgetLayout::place (forwarded)");
1092
const QSize sz1 = dockwidget->minimumSizeHint(),
1093
sz2 = info.item->minimumSize();
1094
const int separatorExtent =
1095
parentWidget()->style()->pixelMetric(QStyle::PM_DockWidgetSeparatorExtent);
1096
Qt::DockWidgetAreas allowedAreas =
1097
getAllowedAreas(info.item->geometry(), sz1, sz2, separatorExtent);
1100
we do in-place reordering if the dock widget is in this layout.
1102
we allow splitting into adjacent items by delaying the
1103
reordering until the mouse is closer to the center of the
1104
adjacent item then the current one
1107
for (int i = 0; which == -1 && i < layout_info.count(); ++i) {
1108
const QDockWidgetLayoutInfo &info = layout_info.at(i);
1111
if (info.item->isEmpty())
1113
if (dockwidget == info.item->widget())
1117
if (which == location.index) {
1118
target = info.item->geometry();
1119
target.moveTopLeft(parentWidget()->mapToGlobal(target.topLeft()));
1120
DEBUG() << "END of place (placed back at original position" << target << ")";
1125
pick(orientation, layout_info.at(location.index).item->geometry().center());
1126
const int pos = pick(orientation, p);
1128
if ((which > location.index && pos < center)
1129
|| (which < location.index && pos > center)) {
1130
DEBUG() << " swapping" << which << "with" << location.index;
1131
layout_info.swap(which, location.index);
1133
target = layout_info.at(location.index).item->geometry();
1134
target.moveTopLeft(parentWidget()->mapToGlobal(target.topLeft()));
1135
// make sure we don't discard the new layout information!
1136
*save_layout_info = layout_info;
1137
DEBUG() << "END of place, in-place reorder, target is" << target;
1140
DEBUG() << " cannot swap" << endl;
1142
allowedAreas &= ~(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
1144
allowedAreas &= ~(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
1148
DEBUG() << " trySplit:" << orientation << location.area
1149
<< info.item->geometry() << p << sz1 << sz2 << separatorExtent;
1150
target = ::trySplit(orientation, location.area, allowedAreas,
1151
info.item->geometry(), p, separatorExtent);
1152
if (!target.isEmpty()) {
1153
target.setSize(target.size().expandedTo(sz1));
1154
target.moveTopLeft(parentWidget()->mapToGlobal(target.topLeft()));
1156
DEBUG() << "END of place, target is" << target;
1162
void QDockWidgetLayout::drop(QDockWidget *dockwidget, const QRect &r, const QPoint &mouse)
1164
DEBUG("QDockWidgetLayout::drop");
1166
const QPoint p = parentWidget()->mapFromGlobal(mouse);
1167
Location location = locate(p - geometry().topLeft());
1168
const QDockWidgetLayoutInfo &info = layout_info.at(location.index);
1169
const bool horizontal = orientation == Qt::Horizontal;
1171
if (info.item->layout()) {
1172
// forward the drop to the nested layout
1173
QDockWidgetLayout *l = qobject_cast<QDockWidgetLayout *>(info.item->layout());
1175
DEBUG(" forwarding...");
1176
l->drop(dockwidget, r, mouse);
1177
DEBUG("END of QDockWidgetLayout::drop (forwarded)");
1181
if (dockwidget == info.item->widget()) {
1182
// placed back at original position
1183
if (dockwidget->isFloating()) {
1184
dockwidget->setFloating(false);
1187
DEBUG("END of drop (shortcut - dropped at original position)");
1191
const QSize sz1 = dockwidget->minimumSizeHint(),
1192
sz2 = info.item->minimumSize();
1193
const int separatorExtent =
1194
parentWidget()->style()->pixelMetric(QStyle::PM_DockWidgetSeparatorExtent);
1195
Qt::DockWidgetAreas allowedAreas =
1196
getAllowedAreas(info.item->geometry(), sz1, sz2, separatorExtent);
1199
we do in-place reordering if the dock widget is in this layout.
1201
we allow splitting into adjacent items by delaying the
1202
reordering until the mouse is closer to the center of the
1203
adjacent item then the current one
1207
for (int i = 0; which == -1 && i < layout_info.count(); ++i) {
1208
const QDockWidgetLayoutInfo &info = layout_info.at(i);
1211
if (dockwidget == info.item->widget()) {
1213
if (!info.item->isEmpty())
1218
DEBUG() << " drop after in-place reorder, only allowing perpendicular splits";
1220
allowedAreas &= ~(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
1222
allowedAreas &= ~(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
1225
DEBUG() << " trySplit:" << orientation << location.area
1226
<< info.item->geometry() << p << sz1 << sz2 << separatorExtent;
1227
QRect target = ::trySplit(orientation, location.area, allowedAreas,
1228
info.item->geometry(), p, separatorExtent);
1229
if (!target.isEmpty()) {
1230
QMainWindowLayout *layout =
1231
qobject_cast<QMainWindowLayout *>(parentWidget()->layout());
1232
Q_ASSERT(layout != 0);
1233
layout->removeRecursive(dockwidget);
1235
// if we removed a dock widget in this layout, adjust the
1237
if (found != -1 && found < location.index)
1238
location.index -= 2;
1240
bool nested = false;
1241
switch (orientation) {
1242
case Qt::Horizontal:
1243
switch (location.area) {
1244
case Qt::TopDockWidgetArea:
1245
case Qt::BottomDockWidgetArea:
1252
switch (location.area) {
1253
case Qt::LeftDockWidgetArea:
1254
case Qt::RightDockWidgetArea:
1261
Q_ASSERT_X(false, "QDockWidgetLayout", "internal error");
1265
split(qobject_cast<QDockWidget *>(info.item->widget()), dockwidget, location.area);
1267
int at = location.index / 2;
1268
if (location.area == Qt::RightDockWidgetArea
1269
|| location.area == Qt::BottomDockWidgetArea)
1271
const int sz = pick(orientation, target.size());
1272
const_cast<QDockWidgetLayoutInfo &>(info).cur_size -= sz + separatorExtent;
1273
QDockWidgetLayoutInfo &newInfo = insert(at, new QWidgetItem(dockwidget));
1274
newInfo.cur_size = sz;
1275
newInfo.is_dropped = true;
1276
relayout(QInternal::RelayoutDropped);
1277
newInfo.is_dropped = false;
1280
if (dockwidget->isFloating()) {
1281
// reparent the dock window into the main window
1282
dockwidget->setFloating(false);
1287
DEBUG("END of drop");
1290
void QDockWidgetLayout::extend(QDockWidget *dockwidget, Qt::Orientation direction)
1292
if (direction == orientation) {
1293
addWidget(dockwidget);
1295
Q_ASSERT(relayout_type == QInternal::RelayoutNormal);
1296
relayout_type = QInternal::RelayoutDropped;
1298
QDockWidgetLayout *nestedLayout = new QDockWidgetLayout(area, orientation);
1299
nestedLayout->setParent(this);
1300
nestedLayout->setObjectName(objectName() + QLatin1String("_nestedCopy"));
1302
for (int i = 0; i < layout_info.count(); ++i) {
1303
const QDockWidgetLayoutInfo &info = layout_info.at(i);
1305
delete info.item->widget();
1308
nestedLayout->addItem(info.item);
1312
relayout_type = QInternal::RelayoutNormal;
1314
layout_info.clear();
1315
setOrientation(direction);
1317
addItem(nestedLayout);
1318
addWidget(dockwidget);
1322
void QDockWidgetLayout::split(QDockWidget *existing, QDockWidget *with, Qt::DockWidgetArea area)
1325
for (int i = 0; which == -1 && i < layout_info.count(); ++i) {
1326
const QDockWidgetLayoutInfo &info = layout_info.at(i);
1329
if (existing == info.item->widget())
1332
Q_ASSERT(which != -1);
1333
const QDockWidgetLayoutInfo &info = layout_info.at(which);
1335
Q_ASSERT(relayout_type == QInternal::RelayoutNormal);
1336
relayout_type = QInternal::RelayoutDropped;
1338
int save_size = info.cur_size;
1339
removeWidget(existing);
1340
// note: info is invalid from now on
1342
// create a nested window dock in place of the current widget
1343
QDockWidgetLayout *nestedLayout =
1344
new QDockWidgetLayout(area, orientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal);
1345
nestedLayout->setParent(this);
1346
nestedLayout->setObjectName(objectName() + "_nestedLayout");
1347
insert(which / 2, nestedLayout).cur_size = save_size;
1351
case Qt::LeftDockWidgetArea:
1352
case Qt::TopDockWidgetArea:
1353
nestedLayout->addWidget(with);
1354
nestedLayout->addWidget(existing);
1357
case Qt::RightDockWidgetArea:
1358
case Qt::BottomDockWidgetArea:
1359
nestedLayout->addWidget(existing);
1360
nestedLayout->addWidget(with);
1364
Q_ASSERT_X(false, "QDockWidgetLayout", "internal error");
1368
relayout_type = QInternal::RelayoutNormal;
1371
void QDockWidgetLayout::maybeDelete()
1373
if (layout_info.isEmpty())