source: trunk/src/qt3support/widgets/q3dockarea.cpp@ 605

Last change on this file since 605 was 561, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 44.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the Qt3Support module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "q3dockarea.h"
43
44#ifndef QT_NO_MAINWINDOW
45#include "qsplitter.h"
46#include "qevent.h"
47#include "qlayout.h"
48#include "qapplication.h"
49#include "qpainter.h"
50#include "qmap.h"
51#include "q3mainwindow.h"
52#include "q3toolbar.h"
53
54QT_BEGIN_NAMESPACE
55
56//#define QDOCKAREA_DEBUG
57
58struct Q3DockData
59{
60 Q3DockData() : w(0), rect() {}
61 Q3DockData(Q3DockWindow *dw, const QRect &r) : w(dw), rect(r) {}
62 Q3DockWindow *w;
63 QRect rect;
64
65 Q_DUMMY_COMPARISON_OPERATOR(Q3DockData)
66};
67
68static int fix_x(Q3DockWindow* w, int width = -1) {
69 if (QApplication::reverseLayout()) {
70 if (width < 0)
71 width = w->width();
72 return w->parentWidget()->width() - w->x() - width;
73 }
74 return w->x();
75}
76static int fix_x(Q3DockWindow* w, int x, int width = -1) {
77 if (QApplication::reverseLayout()) {
78 if (width < 0)
79 width = w->width();
80 return w->parentWidget()->width() - x - width;
81 }
82 return x;
83}
84
85static QPoint fix_pos(Q3DockWindow* w) {
86 if (QApplication::reverseLayout()) {
87 QPoint p = w->pos();
88 p.rx() = w->parentWidget()->width() - p.x() - w->width();
89 return p;
90 }
91 return w->pos();
92}
93
94
95void Q3DockAreaLayout::setGeometry(const QRect &r)
96{
97 QLayout::setGeometry(r);
98 layoutItems(r);
99}
100
101QLayoutItem *Q3DockAreaLayout::itemAt(int) const
102{
103 return 0; //###
104}
105
106QLayoutItem *Q3DockAreaLayout::takeAt(int)
107{
108 return 0; //###
109}
110
111int Q3DockAreaLayout::count() const
112{
113 return 0; //###
114}
115
116
117QSize Q3DockAreaLayout::sizeHint() const
118{
119 if (dockWindows->isEmpty())
120 return QSize(0, 0);
121
122 if (dirty) {
123 Q3DockAreaLayout *that = (Q3DockAreaLayout *) this;
124 that->layoutItems(geometry());
125 }
126
127 int w = 0;
128 int h = 0;
129 int y = -1;
130 int x = -1;
131 int ph = 0;
132 int pw = 0;
133 for (int i = 0; i < dockWindows->size(); ++i) {
134 Q3DockWindow *dw = dockWindows->at(i);
135 int plush = 0, plusw = 0;
136 if (dw->isHidden())
137 continue;
138 if (hasHeightForWidth()) {
139 if (y != dw->y())
140 plush = ph;
141 y = dw->y();
142 ph = dw->height();
143 } else {
144 if (x != dw->x())
145 plusw = pw;
146 x = dw->x();
147 pw = dw->width();
148 }
149 h = qMax(h, dw->height() + plush);
150 w = qMax(w, dw->width() + plusw);
151 }
152
153 if (hasHeightForWidth())
154 return QSize(0, h);
155 return QSize(w, 0);
156}
157
158bool Q3DockAreaLayout::hasHeightForWidth() const
159{
160 return orient == Qt::Horizontal;
161}
162
163void Q3DockAreaLayout::init()
164{
165 dirty = true;
166 cached_width = 0;
167 cached_height = 0;
168 cached_hfw = -1;
169 cached_wfh = -1;
170}
171
172void Q3DockAreaLayout::invalidate()
173{
174 dirty = true;
175 cached_width = 0;
176 cached_height = 0;
177 QLayout::invalidate();
178}
179
180static int start_pos(const QRect &r, Qt::Orientation o)
181{
182 if (o == Qt::Horizontal) {
183 return qMax(0, r.x());
184 } else {
185 return qMax(0, r.y());
186 }
187}
188
189static void add_size(int s, int &pos, Qt::Orientation o)
190{
191 if (o == Qt::Horizontal) {
192 pos += s;
193 } else {
194 pos += s;
195 }
196}
197
198static int space_left(const QRect &r, int pos, Qt::Orientation o)
199{
200 if (o == Qt::Horizontal) {
201 return (r.x() + r.width()) - pos;
202 } else {
203 return (r.y() + r.height()) - pos;
204 }
205}
206
207static int dock_extent(Q3DockWindow *w, Qt::Orientation o, int maxsize)
208{
209 if (o == Qt::Horizontal)
210 return qMin(maxsize, qMax(w->sizeHint().width(), w->fixedExtent().width()));
211 else
212 return qMin(maxsize, qMax(w->sizeHint().height(), w->fixedExtent().height()));
213}
214
215static int dock_strut(Q3DockWindow *w, Qt::Orientation o)
216{
217 if (o != Qt::Horizontal) {
218 int wid;
219 if ((wid = w->fixedExtent().width()) != -1)
220 return qMax(wid, qMax(w->minimumSize().width(), w->minimumSizeHint().width()));
221 return qMax(w->sizeHint().width(), qMax(w->minimumSize().width(), w->minimumSizeHint().width()));
222 } else {
223 int hei;
224 if ((hei = w->fixedExtent().height()) != -1)
225 return qMax(hei, qMax(w->minimumSizeHint().height(), w->minimumSize().height()));
226 return qMax(w->sizeHint().height(), qMax(w->minimumSizeHint().height(), w->minimumSize().height()));
227 }
228}
229
230static void set_geometry(Q3DockWindow *w, int pos, int sectionpos, int extent, int strut, Qt::Orientation o)
231{
232 if (o == Qt::Horizontal)
233 w->setGeometry(fix_x(w, pos, extent), sectionpos, extent, strut);
234 else
235 w->setGeometry(sectionpos, pos, strut, extent);
236}
237
238static int size_extent(const QSize &s, Qt::Orientation o, bool swap = false)
239{
240 return o == Qt::Horizontal ? (swap ? s.height() : s.width()) : (swap ? s.width() : s.height());
241}
242
243static int point_pos(const QPoint &p, Qt::Orientation o, bool swap = false)
244{
245 return o == Qt::Horizontal ? (swap ? p.y() : p.x()) : (swap ? p.x() : p.y());
246}
247
248static void shrink_extend(Q3DockWindow *dw, int &dockExtend, int /*spaceLeft*/, Qt::Orientation o)
249{
250 Q3ToolBar *tb = qobject_cast<Q3ToolBar*>(dw);
251 if (o == Qt::Horizontal) {
252 int mw = 0;
253 if (!tb)
254 mw = dw->minimumWidth();
255 else
256 mw = dw->sizeHint().width();
257 dockExtend = mw;
258 } else {
259 int mh = 0;
260 if (!tb)
261 mh = dw->minimumHeight();
262 else
263 mh = dw->sizeHint().height();
264 dockExtend = mh;
265 }
266}
267
268static void place_line(QList<Q3DockData> &lastLine, Qt::Orientation o, int linestrut, int fullextent, int tbstrut, int maxsize, Q3DockAreaLayout *)
269{
270 Q3DockWindow *last = 0;
271 QRect lastRect;
272 for (QList<Q3DockData>::Iterator it = lastLine.begin(); it != lastLine.end(); ++it) {
273 if (tbstrut != -1 && qobject_cast<Q3ToolBar*>((*it).w))
274 (*it).rect.setHeight(tbstrut);
275 if (!last) {
276 last = (*it).w;
277 lastRect = (*it).rect;
278 continue;
279 }
280 if (!last->isStretchable()) {
281 int w = qMin(lastRect.width(), maxsize);
282 set_geometry(last, lastRect.x(), lastRect.y(), w, lastRect.height(), o);
283 } else {
284 int w = qMin((*it).rect.x() - lastRect.x(), maxsize);
285 set_geometry(last, lastRect.x(), lastRect.y(), w,
286 last->isResizeEnabled() ? linestrut : lastRect.height(), o);
287 }
288 last = (*it).w;
289 lastRect = (*it).rect;
290 }
291 if (!last)
292 return;
293 if (!last->isStretchable()) {
294 int w = qMin(lastRect.width(), maxsize);
295 set_geometry(last, lastRect.x(), lastRect.y(), w, lastRect.height(), o);
296 } else {
297 int w = qMin(fullextent - lastRect.x() - (o == Qt::Vertical ? 1 : 0), maxsize);
298 set_geometry(last, lastRect.x(), lastRect.y(), w,
299 last->isResizeEnabled() ? linestrut : lastRect.height(), o);
300 }
301}
302
303QSize Q3DockAreaLayout::minimumSize() const
304{
305 if (dockWindows->isEmpty())
306 return QSize(0, 0);
307
308 if (dirty) {
309 Q3DockAreaLayout *that = (Q3DockAreaLayout *) this;
310 that->layoutItems(geometry());
311 }
312
313 int s = 0;
314
315 for (int i = 0; i < dockWindows->size(); ++i) {
316 Q3DockWindow *dw = dockWindows->at(i);
317 if (dw->isHidden())
318 continue;
319 s = qMax(s, dock_strut(dw, orientation()));
320 }
321
322 return orientation() == Qt::Horizontal ? QSize(0, s ? s+2 : 0) : QSize(s, 0);
323}
324
325
326
327int Q3DockAreaLayout::layoutItems(const QRect &rect, bool testonly)
328{
329 if (dockWindows->isEmpty())
330 return 0;
331
332 dirty = false;
333
334 // some corrections
335 QRect r = rect;
336 if (orientation() == Qt::Vertical)
337 r.setHeight(r.height() - 3);
338
339 // init
340 lines.clear();
341 ls.clear();
342 int start = start_pos(r, orientation());
343 int pos = start;
344 int sectionpos = 0;
345 int linestrut = 0;
346 QList<Q3DockData> lastLine;
347 int tbstrut = -1;
348 int maxsize = size_extent(rect.size(), orientation());
349 int visibleWindows = 0;
350
351 // go through all widgets in the dock
352 for (int i = 0; i < dockWindows->size(); ++i) {
353 Q3DockWindow *dw = dockWindows->at(i);
354 if (dw->isHidden())
355 continue;
356 ++visibleWindows;
357 // find position for the widget: This is the maximum of the
358 // end of the previous widget and the offset of the widget. If
359 // the position + the width of the widget dosn't fit into the
360 // dock, try moving it a bit back, if possible.
361 int op = pos;
362 int dockExtend = dock_extent(dw, orientation(), maxsize);
363 if (!dw->isStretchable()) {
364 pos = qMax(pos, dw->offset());
365 if (pos + dockExtend > size_extent(r.size(), orientation()) - 1)
366 pos = qMax(op, size_extent(r.size(), orientation()) - 1 - dockExtend);
367 }
368 if (!lastLine.isEmpty() && !dw->newLine() && space_left(rect, pos, orientation()) < dockExtend)
369 shrink_extend(dw, dockExtend, space_left(rect, pos, orientation()), orientation());
370 // if the current widget doesn't fit into the line anymore and it is not the first widget of the line
371 if (!lastLine.isEmpty() &&
372 (space_left(rect, pos, orientation()) < dockExtend || dw->newLine())) {
373 if (!testonly) // place the last line, if not in test mode
374 place_line(lastLine, orientation(), linestrut, size_extent(r.size(), orientation()), tbstrut, maxsize, this);
375 // remember the line coordinats of the last line
376 if (orientation() == Qt::Horizontal)
377 lines.append(QRect(0, sectionpos, r.width(), linestrut));
378 else
379 lines.append(QRect(sectionpos, 0, linestrut, r.height()));
380 // do some clearing for the next line
381 lastLine.clear();
382 sectionpos += linestrut;
383 linestrut = 0;
384 pos = start;
385 tbstrut = -1;
386 }
387
388 // remember first widget of a line
389 if (lastLine.isEmpty()) {
390 ls.append(dw);
391 // try to make the best position
392 int op = pos;
393 if (!dw->isStretchable())
394 pos = qMax(pos, dw->offset());
395 if (pos + dockExtend > size_extent(r.size(), orientation()) - 1)
396 pos = qMax(op, size_extent(r.size(), orientation()) - 1 - dockExtend);
397 }
398 // do some calculations and add the remember the rect which the docking widget requires for the placing
399 QRect dwRect(pos, sectionpos, dockExtend, dock_strut(dw, orientation() ));
400 lastLine.append(Q3DockData(dw, dwRect));
401 if (qobject_cast<Q3ToolBar*>(dw))
402 tbstrut = qMax(tbstrut, dock_strut(dw, orientation()));
403 linestrut = qMax(dock_strut(dw, orientation()), linestrut);
404 add_size(dockExtend, pos, orientation());
405 }
406
407 // if some stuff was not placed/stored yet, do it now
408 if (!testonly)
409 place_line(lastLine, orientation(), linestrut, size_extent(r.size(), orientation()), tbstrut, maxsize, this);
410 if (orientation() == Qt::Horizontal)
411 lines.append(QRect(0, sectionpos, r.width(), linestrut));
412 else
413 lines.append(QRect(sectionpos, 0, linestrut, r.height()));
414 if (lines.size() >= 2 && *(--lines.end()) == *(--(--lines.end())))
415 lines.removeLast();
416
417 bool hadResizable = false;
418 for (int i = 0; i < dockWindows->size(); ++i) {
419 Q3DockWindow *dw = dockWindows->at(i);
420 if (!dw->isVisibleTo(parentWidget))
421 continue;
422 hadResizable = hadResizable || dw->isResizeEnabled();
423 dw->updateSplitterVisibility(visibleWindows > 1); //!dw->area()->isLastDockWindow(dw));
424 if (Q3ToolBar *tb = qobject_cast<Q3ToolBar *>(dw))
425 tb->checkForExtension(dw->size());
426 }
427 return sectionpos + linestrut;
428}
429
430int Q3DockAreaLayout::heightForWidth(int w) const
431{
432 if (dockWindows->isEmpty() && parentWidget)
433 return parentWidget->minimumHeight();
434
435 if (cached_width != w) {
436 Q3DockAreaLayout * mthis = (Q3DockAreaLayout*)this;
437 mthis->cached_width = w;
438 int h = mthis->layoutItems(QRect(0, 0, w, 0), true);
439 mthis->cached_hfw = h;
440 return h;
441 }
442
443 return cached_hfw;
444}
445
446int Q3DockAreaLayout::widthForHeight(int h) const
447{
448 if (cached_height != h) {
449 Q3DockAreaLayout * mthis = (Q3DockAreaLayout*)this;
450 mthis->cached_height = h;
451 int w = mthis->layoutItems(QRect(0, 0, 0, h), true);
452 mthis->cached_wfh = w;
453 return w;
454 }
455 return cached_wfh;
456}
457
458
459
460
461/*!
462 \class Q3DockArea
463 \brief The Q3DockArea class manages and lays out Q3DockWindows.
464
465 \compat
466
467 A Q3DockArea is a container which manages a list of
468 \l{Q3DockWindow}s which it lays out within its area. In cooperation
469 with the \l{Q3DockWindow}s it is responsible for the docking and
470 undocking of \l{Q3DockWindow}s and moving them inside the dock
471 area. Q3DockAreas also handle the wrapping of \l{Q3DockWindow}s to
472 fill the available space as compactly as possible. Q3DockAreas can
473 contain Q3ToolBars since Q3ToolBar is a Q3DockWindow subclass.
474
475 QMainWindow contains four Q3DockAreas which you can use for your
476 Q3ToolBars and Q3DockWindows, so in most situations you do not
477 need to use the Q3DockArea class directly. Although QMainWindow
478 contains support for its own dock areas, you can't add new ones.
479 You also can't add a Q3DockArea to your own subclass of QWidget.
480 It won't be shown.
481
482 \img qmainwindow-qdockareas.png QMainWindow's Q3DockAreas
483
484 \target lines
485 \section1 Lines.
486
487 Q3DockArea uses the concept of lines. A line is a horizontal
488 region which may contain dock windows side-by-side. A dock area
489 may have room for more than one line. When dock windows are docked
490 into a dock area they are usually added at the right hand side of
491 the top-most line that has room (unless manually placed by the
492 user). When users move dock windows they may leave empty lines or
493 gaps in non-empty lines. Qt::Dock windows can be lined up to
494 minimize wasted space using the lineUp() function.
495
496 The Q3DockArea class maintains a position list of all its child
497 dock windows. Qt::Dock windows are added to a dock area from position
498 0 onwards. Qt::Dock windows are laid out sequentially in position
499 order from left to right, and in the case of multiple lines of
500 dock windows, from top to bottom. If a dock window is floated it
501 still retains its position since this is where the window will
502 return if the user double clicks its caption. A dock window's
503 position can be determined with hasDockWindow(). The position can
504 be changed with moveDockWindow().
505
506 To dock or undock a dock window use Q3DockWindow::dock() and
507 Q3DockWindow::undock() respectively. If you want to control which
508 dock windows can dock in a dock area use setAcceptDockWindow(). To
509 see if a dock area contains a particular dock window use
510 \l{hasDockWindow()}; to see how many dock windows a dock area
511 contains use count().
512
513 The streaming operators can write the positions of the dock
514 windows in the dock area to a QTextStream. The positions can be
515 read back later to restore the saved positions.
516
517 Save the positions to a QTextStream:
518 \snippet doc/src/snippets/code/src_qt3support_widgets_q3dockarea.cpp 0
519
520 Restore the positions from a QTextStream:
521 \snippet doc/src/snippets/code/src_qt3support_widgets_q3dockarea.cpp 1
522*/
523
524/*!
525 \property Q3DockArea::handlePosition
526 \brief where the dock window splitter handle is placed in the dock
527 area
528
529 The default position is \c Normal.
530*/
531
532/*!
533 \property Q3DockArea::orientation
534 \brief the dock area's orientation
535
536 There is no default value; the orientation is specified in the
537 constructor.
538*/
539
540/*!
541 \enum Q3DockArea::HandlePosition
542
543 A dock window has two kinds of handles, the dock window handle
544 used for dragging the dock window, and the splitter handle used to
545 resize the dock window in relation to other dock windows using a
546 splitter. (The splitter handle is only visible for docked
547 windows.)
548
549 This enum specifies where the dock window splitter handle is
550 placed in the dock area.
551
552 \value Normal The splitter handles of dock windows are placed at
553 the right or bottom.
554
555 \value Reverse The splitter handles of dock windows are placed at
556 the left or top.
557*/
558
559/*!
560 Constructs a Q3DockArea with orientation \a o, HandlePosition \a h,
561 parent \a parent and called \a name.
562*/
563
564Q3DockArea::Q3DockArea(Qt::Orientation o, HandlePosition h, QWidget *parent, const char *name)
565 : QWidget(parent, name), orient(o), layout(0), hPos(h)
566{
567 layout = new Q3DockAreaLayout(this, o, &dockWindows, 0, 0, "toollayout");
568 installEventFilter(this);
569}
570
571/*!
572 Destroys the dock area and all the dock windows docked in the dock
573 area.
574
575 Does not affect any floating dock windows or dock windows in other
576 dock areas, even if they first appeared in this dock area.
577 Floating dock windows are effectively top level windows and are
578 not child windows of the dock area. When a floating dock window is
579 docked (dragged into a dock area) its parent becomes the dock
580 area.
581*/
582
583Q3DockArea::~Q3DockArea()
584{
585 while (!dockWindows.isEmpty())
586 delete dockWindows.takeFirst();
587}
588
589/*!
590 Moves the Q3DockWindow \a w within the dock area. If \a w is not
591 already docked in this area, \a w is docked first. If \a index is
592 -1 or larger than the number of docked widgets, \a w is appended
593 at the end, otherwise it is inserted at the position \a index.
594*/
595
596void Q3DockArea::moveDockWindow(Q3DockWindow *w, int index)
597{
598 invalidateFixedSizes();
599 Q3DockWindow *dockWindow = 0;
600 int dockWindowIndex = findDockWindow(w);
601 if (dockWindowIndex == -1) {
602 dockWindow = w;
603 bool vis = dockWindow->isVisible();
604 dockWindow->setParent(this);
605 dockWindow->move(0, 0);
606 if(vis)
607 dockWindow->show();
608 w->installEventFilter(this);
609 updateLayout();
610 setSizePolicy(QSizePolicy(orientation() == Qt::Horizontal ? QSizePolicy::Expanding : QSizePolicy::Minimum,
611 orientation() == Qt::Vertical ? QSizePolicy::Expanding : QSizePolicy::Minimum));
612 dockWindows.append(w);
613 } else {
614 if (w->parent() != this) {
615 bool vis = w->isVisible();
616 w->setParent(this);
617 w->move(0, 0);
618 if(vis)
619 w->show();
620 }
621 if (index == -1) {
622 dockWindows.removeAll(w);
623 dockWindows.append(w);
624 }
625 }
626
627 w->dockArea = this;
628 w->curPlace = Q3DockWindow::InDock;
629 w->updateGui();
630
631 if (index != -1 && index < (int)dockWindows.count()) {
632 dockWindows.removeAll(w);
633 dockWindows.insert(index, w);
634 }
635}
636
637/*!
638 Returns true if the dock area contains the dock window \a w;
639 otherwise returns false. If \a index is not 0 it will be set as
640 follows: if the dock area contains the dock window *\a{index} is
641 set to \a w's index position; otherwise *\a{index} is set to -1.
642*/
643
644bool Q3DockArea::hasDockWindow(Q3DockWindow *w, int *index)
645{
646 int i = dockWindows.indexOf(w);
647 if (index)
648 *index = i;
649 return i != -1;
650}
651
652int Q3DockArea::lineOf(int index)
653{
654 QList<Q3DockWindow *> lineStarts = layout->lineStarts();
655 int i = 0;
656 for (; i < lineStarts.size(); ++i) {
657 Q3DockWindow *w = lineStarts.at(i);
658 if (dockWindows.indexOf(w) >= index)
659 return i;
660 }
661 return i;
662}
663
664/*!
665 \overload
666
667 Moves the dock window \a w inside the dock area where \a p is the
668 new position (in global screen coordinates), \a r is the suggested
669 rectangle of the dock window and \a swap specifies whether or not
670 the orientation of the docked widget needs to be changed.
671
672 This function is used internally by Q3DockWindow. You shouldn't
673 need to call it yourself.
674*/
675
676void Q3DockArea::moveDockWindow(Q3DockWindow *w, const QPoint &p, const QRect &r, bool swap)
677{
678 invalidateFixedSizes();
679 int mse = -10;
680 bool hasResizable = false;
681 for (int i = 0; i < dockWindows.size(); ++i) {
682 Q3DockWindow *dw = dockWindows.at(i);
683 if (dw->isHidden())
684 continue;
685 if (dw->isResizeEnabled())
686 hasResizable = true;
687 if (orientation() != Qt::Horizontal)
688 mse = qMax(qMax(dw->fixedExtent().width(), dw->width()), mse);
689 else
690 mse = qMax(qMax(dw->fixedExtent().height(), dw->height()), mse);
691 }
692 if (!hasResizable && w->isResizeEnabled()) {
693 if (orientation() != Qt::Horizontal)
694 mse = qMax(w->fixedExtent().width(), mse);
695 else
696 mse = qMax(w->fixedExtent().height(), mse);
697 }
698
699 Q3DockWindow *dockWindow = 0;
700 int dockWindowIndex = findDockWindow(w);
701 QList<Q3DockWindow *> lineStarts = layout->lineStarts();
702 QList<QRect> lines = layout->lineList();
703 bool wasAloneInLine = false;
704 QPoint pos = mapFromGlobal(p);
705 int line = lineOf(dockWindowIndex);
706 QRect lr;
707 if (line < lines.size())
708 lr = lines.at(line);
709 if (dockWindowIndex != -1) {
710 if (lineStarts.contains(w)
711 && ((dockWindowIndex < dockWindows.count() - 1
712 && lineStarts.contains(dockWindows.at(dockWindowIndex + 1)))
713 || dockWindowIndex == dockWindows.count() - 1))
714 wasAloneInLine = true;
715 dockWindow = dockWindows.takeAt(dockWindowIndex);
716 if (!wasAloneInLine) { // only do the pre-layout if the widget isn't the only one in its line
717 if (lineStarts.contains(dockWindow) && dockWindowIndex < dockWindows.count())
718 dockWindows.at(dockWindowIndex)->setNewLine(true);
719 layout->layoutItems(QRect(0, 0, width(), height()), true);
720 }
721 } else {
722 dockWindow = w;
723 bool vis = dockWindow->isVisible();
724 dockWindow->setParent(this);
725 dockWindow->move(0, 0);
726 if(vis)
727 dockWindow->show();
728 if (swap)
729 dockWindow->resize(dockWindow->height(), dockWindow->width());
730 w->installEventFilter(this);
731 }
732
733 lineStarts = layout->lineStarts();
734 lines = layout->lineList();
735
736 QRect rect = QRect(mapFromGlobal(r.topLeft()), r.size());
737 if (orientation() == Qt::Horizontal && QApplication::reverseLayout()) {
738 rect = QRect(width() - rect.x() - rect.width(), rect.y(), rect.width(), rect.height());
739 pos.rx() = width() - pos.x();
740 }
741 dockWindow->setOffset(point_pos(rect.topLeft(), orientation()));
742 if (orientation() == Qt::Horizontal) {
743 int offs = dockWindow->offset();
744 if (width() - offs < dockWindow->minimumWidth())
745 dockWindow->setOffset(width() - dockWindow->minimumWidth());
746 } else {
747 int offs = dockWindow->offset();
748 if (height() - offs < dockWindow->minimumHeight())
749 dockWindow->setOffset(height() - dockWindow->minimumHeight());
750 }
751
752 if (dockWindows.isEmpty()) {
753 dockWindows.append(dockWindow);
754 } else {
755 int dockLine = -1;
756 bool insertLine = false;
757 int i = 0;
758 QRect lineRect;
759 // find the line which we touched with the mouse
760 for (QList<QRect>::Iterator it = lines.begin(); it != lines.end(); ++it, ++i) {
761 if (point_pos(pos, orientation(), true) >= point_pos((*it).topLeft(), orientation(), true) &&
762 point_pos(pos, orientation(), true) <= point_pos((*it).topLeft(), orientation(), true) +
763 size_extent((*it).size(), orientation(), true)) {
764 dockLine = i;
765 lineRect = *it;
766 break;
767 }
768 }
769 if (dockLine == -1) { // outside the dock...
770 insertLine = true;
771 if (point_pos(pos, orientation(), true) < 0) // insert as first line
772 dockLine = 0;
773 else
774 dockLine = (int)lines.count(); // insert after the last line ### size_t/int cast
775 } else { // inside the dock (we have found a dockLine)
776 if (point_pos(pos, orientation(), true) <
777 point_pos(lineRect.topLeft(), orientation(), true) + 4) { // mouse was at the very beginning of the line
778 insertLine = true; // insert a new line before that with the docking widget
779 } else if (point_pos(pos, orientation(), true) >
780 point_pos(lineRect.topLeft(), orientation(), true) +
781 size_extent(lineRect.size(), orientation(), true) - 4) { // mouse was at the very and of the line
782 insertLine = true; // insert a line after that with the docking widget
783 dockLine++;
784 }
785 }
786
787 if (!insertLine && wasAloneInLine && lr.contains(pos)) // if we are alone in a line and just moved in there, re-insert it
788 insertLine = true;
789
790#if defined(QDOCKAREA_DEBUG)
791 qDebug("insert in line %d, and insert that line: %d", dockLine, insertLine);
792 qDebug(" (btw, we have %d lines)", lines.count());
793#endif
794 Q3DockWindow *dw = 0;
795 if (dockLine >= (int)lines.count()) { // insert after last line
796 dockWindows.append(dockWindow);
797 dockWindow->setNewLine(true);
798#if defined(QDOCKAREA_DEBUG)
799 qDebug("insert at the end");
800#endif
801 } else if (dockLine == 0 && insertLine) { // insert before first line
802 dockWindows.insert(0, dockWindow);
803 dockWindows.at(1)->setNewLine(true);
804#if defined(QDOCKAREA_DEBUG)
805 qDebug("insert at the begin");
806#endif
807 } else { // insert somewhere in between
808 // make sure each line start has a new line
809 for (int i = 0; i < lineStarts.size(); ++i) {
810 dw = lineStarts.at(i);
811 dw->setNewLine(true);
812 }
813
814 // find the index of the first widget in the search line
815 int searchLine = dockLine;
816#if defined(QDOCKAREA_DEBUG)
817 qDebug("search line start of %d", searchLine);
818#endif
819 Q3DockWindow *lsw = lineStarts.at(searchLine);
820 int index = dockWindows.indexOf(lsw);
821 if (index == -1) { // the linestart widget hasn't been found, try to find it harder
822 if (lsw == w && dockWindowIndex <= dockWindows.count())
823 index = dockWindowIndex;
824 else
825 index = 0;
826 }
827#if defined(QDOCKAREA_DEBUG)
828 qDebug(" which starts at %d", index);
829#endif
830 if (!insertLine) { // if we insert the docking widget in the existing line
831 // find the index for the widget
832 bool inc = true;
833 bool firstTime = true;
834 for (int i = index; i < dockWindows.size(); ++i) {
835 dw = dockWindows.at(i);
836 if (orientation() == Qt::Horizontal)
837 dw->setFixedExtentWidth(-1);
838 else
839 dw->setFixedExtentHeight(-1);
840 if (!firstTime && lineStarts.contains(dw)) // we are in the next line, so break
841 break;
842 if (point_pos(pos, orientation()) <
843 point_pos(fix_pos(dw), orientation()) + size_extent(dw->size(), orientation()) / 2) {
844 inc = false;
845 }
846 if (inc)
847 index++;
848 firstTime = false;
849 }
850#if defined(QDOCKAREA_DEBUG)
851 qDebug("insert at index: %d", index);
852#endif
853 // if we insert it just before a widget which has a new line, transfer the newline to the docking widget
854 // but not if we didn't only mave a widget in its line which was alone in the line before
855 if (!(wasAloneInLine && lr.contains(pos))
856 && index >= 0 && index < dockWindows.count() &&
857 dockWindows.at(index)->newLine() && lineOf(index) == dockLine) {
858#if defined(QDOCKAREA_DEBUG)
859 qDebug("get rid of the old newline and get me one");
860#endif
861 dockWindows.at(index)->setNewLine(false);
862 dockWindow->setNewLine(true);
863 } else if (wasAloneInLine && lr.contains(pos)) {
864 dockWindow->setNewLine(true);
865 } else { // if we are somewhere in a line, get rid of the newline
866 dockWindow->setNewLine(false);
867 }
868 } else { // insert in a new line, so make sure the dock widget and the widget which will be after it have a newline
869#if defined(QDOCKAREA_DEBUG)
870 qDebug("insert a new line");
871#endif
872 if (index < dockWindows.count()) {
873#if defined(QDOCKAREA_DEBUG)
874 qDebug("give the widget at %d a newline", index);
875#endif
876 Q3DockWindow* nldw = dockWindows.at(index);
877 if (nldw)
878 nldw->setNewLine(true);
879 }
880#if defined(QDOCKAREA_DEBUG)
881 qDebug("give me a newline");
882#endif
883 dockWindow->setNewLine(true);
884 }
885 // finally insert the widget
886 dockWindows.insert(index, dockWindow);
887 }
888 }
889
890 if (mse != -10 && w->isResizeEnabled()) {
891 if (orientation() != Qt::Horizontal)
892 w->setFixedExtentWidth(qMin(qMax(w->minimumWidth(), mse), w->sizeHint().width()));
893 else
894 w->setFixedExtentHeight(qMin(qMax(w->minimumHeight(), mse), w->sizeHint().height()));
895 }
896
897 updateLayout();
898 setSizePolicy(QSizePolicy(orientation() == Qt::Horizontal ? QSizePolicy::Expanding : QSizePolicy::Minimum,
899 orientation() == Qt::Vertical ? QSizePolicy::Expanding : QSizePolicy::Minimum));
900}
901
902/*!
903 Removes the dock window \a w from the dock area. If \a
904 makeFloating is true, \a w gets floated, and if \a swap is true,
905 the orientation of \a w gets swapped. If \a fixNewLines is true
906 (the default) newlines in the area will be fixed.
907
908 You should never need to call this function yourself. Use
909 Q3DockWindow::dock() and Q3DockWindow::undock() instead.
910*/
911
912void Q3DockArea::removeDockWindow(Q3DockWindow *w, bool makeFloating, bool swap, bool fixNewLines)
913{
914 w->removeEventFilter(this);
915 Q3DockWindow *dockWindow = 0;
916 int i = findDockWindow(w);
917 if (i == -1)
918 return;
919 dockWindow = dockWindows.at(i);
920 dockWindows.removeAt(i);
921 QList<Q3DockWindow *> lineStarts = layout->lineStarts();
922 if (fixNewLines && lineStarts.contains(dockWindow) && i < dockWindows.count())
923 dockWindows.at(i)->setNewLine(true);
924 if (makeFloating) {
925 QWidget *p = parentWidget() ? parentWidget() : window();
926 dockWindow->setParent(p, Qt::WType_Dialog | Qt::WStyle_Customize | Qt::WStyle_NoBorder | Qt::WStyle_Tool);
927 dockWindow->move(0, 0);
928 }
929 if (swap)
930 dockWindow->resize(dockWindow->height(), dockWindow->width());
931 updateLayout();
932 if (dockWindows.isEmpty())
933 setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
934}
935
936int Q3DockArea::findDockWindow(Q3DockWindow *w)
937{
938 return dockWindows.indexOf(w);
939}
940
941void Q3DockArea::updateLayout()
942{
943 layout->invalidate();
944 layout->activate();
945}
946
947/*! \reimp
948 */
949
950bool Q3DockArea::eventFilter(QObject *o, QEvent *e)
951{
952 if (e->type() == QEvent::Close) {
953 if (qobject_cast<Q3DockWindow*>(o)) {
954 o->removeEventFilter(this);
955 QApplication::sendEvent(o, e);
956 if (((QCloseEvent*)e)->isAccepted())
957 removeDockWindow((Q3DockWindow*)o, false, false);
958 return true;
959 }
960 }
961 return false;
962}
963
964/*! \internal
965
966 Invalidates the offset of the next dock window in the dock area.
967 */
968
969void Q3DockArea::invalidNextOffset(Q3DockWindow *dw)
970{
971 int i = dockWindows.indexOf(dw);
972 if (i == -1 || i >= (int)dockWindows.count() - 1)
973 return;
974 if ((dw = dockWindows.at(++i)))
975 dw->setOffset(0);
976}
977
978/*!
979 \property Q3DockArea::count
980 \brief the number of dock windows in the dock area
981*/
982int Q3DockArea::count() const
983{
984 return dockWindows.count();
985}
986
987/*!
988 \property Q3DockArea::empty
989 \brief whether the dock area is empty
990*/
991
992bool Q3DockArea::isEmpty() const
993{
994 return dockWindows.isEmpty();
995}