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

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

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 44.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the Qt3Support module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department 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 need
477 to use the Q3DockArea class directly. Although QMainWindow contains
478 support for its own dock areas it isn't convenient for adding new
479 Q3DockAreas. If you need to create your own dock areas we suggest
480 that you create a subclass of QWidget and add your Q3DockAreas to
481 your subclass.
482
483 \img qmainwindow-qdockareas.png QMainWindow's Q3DockAreas
484
485 \target lines
486 \e Lines. Q3DockArea uses the concept of lines. A line is a
487 horizontal region which may contain dock windows side-by-side. A
488 dock area may have room for more than one line. When dock windows
489 are docked into a dock area they are usually added at the right
490 hand side of the top-most line that has room (unless manually
491 placed by the user). When users move dock windows they may leave
492 empty lines or gaps in non-empty lines. Qt::Dock windows can be lined
493 up to minimize wasted space using the lineUp() function.
494
495 The Q3DockArea class maintains a position list of all its child
496 dock windows. Qt::Dock windows are added to a dock area from position
497 0 onwards. Qt::Dock windows are laid out sequentially in position
498 order from left to right, and in the case of multiple lines of
499 dock windows, from top to bottom. If a dock window is floated it
500 still retains its position since this is where the window will
501 return if the user double clicks its caption. A dock window's
502 position can be determined with hasDockWindow(). The position can
503 be changed with moveDockWindow().
504
505 To dock or undock a dock window use Q3DockWindow::dock() and
506 Q3DockWindow::undock() respectively. If you want to control which
507 dock windows can dock in a dock area use setAcceptDockWindow(). To
508 see if a dock area contains a particular dock window use
509 \l{hasDockWindow()}; to see how many dock windows a dock area
510 contains use count().
511
512 The streaming operators can write the positions of the dock
513 windows in the dock area to a QTextStream. The positions can be
514 read back later to restore the saved positions.
515
516 Save the positions to a QTextStream:
517 \snippet doc/src/snippets/code/src_qt3support_widgets_q3dockarea.cpp 0
518
519 Restore the positions from a QTextStream:
520 \snippet doc/src/snippets/code/src_qt3support_widgets_q3dockarea.cpp 1
521*/
522
523/*!
524 \property Q3DockArea::handlePosition
525 \brief where the dock window splitter handle is placed in the dock
526 area
527
528 The default position is \c Normal.
529*/
530
531/*!
532 \property Q3DockArea::orientation
533 \brief the dock area's orientation
534
535 There is no default value; the orientation is specified in the
536 constructor.
537*/
538
539/*!
540 \enum Q3DockArea::HandlePosition
541
542 A dock window has two kinds of handles, the dock window handle
543 used for dragging the dock window, and the splitter handle used to
544 resize the dock window in relation to other dock windows using a
545 splitter. (The splitter handle is only visible for docked
546 windows.)
547
548 This enum specifies where the dock window splitter handle is
549 placed in the dock area.
550
551 \value Normal The splitter handles of dock windows are placed at
552 the right or bottom.
553
554 \value Reverse The splitter handles of dock windows are placed at
555 the left or top.
556*/
557
558/*!
559 Constructs a Q3DockArea with orientation \a o, HandlePosition \a h,
560 parent \a parent and called \a name.
561*/
562
563Q3DockArea::Q3DockArea(Qt::Orientation o, HandlePosition h, QWidget *parent, const char *name)
564 : QWidget(parent, name), orient(o), layout(0), hPos(h)
565{
566 layout = new Q3DockAreaLayout(this, o, &dockWindows, 0, 0, "toollayout");
567 installEventFilter(this);
568}
569
570/*!
571 Destroys the dock area and all the dock windows docked in the dock
572 area.
573
574 Does not affect any floating dock windows or dock windows in other
575 dock areas, even if they first appeared in this dock area.
576 Floating dock windows are effectively top level windows and are
577 not child windows of the dock area. When a floating dock window is
578 docked (dragged into a dock area) its parent becomes the dock
579 area.
580*/
581
582Q3DockArea::~Q3DockArea()
583{
584 while (!dockWindows.isEmpty())
585 delete dockWindows.takeFirst();
586}
587
588/*!
589 Moves the Q3DockWindow \a w within the dock area. If \a w is not
590 already docked in this area, \a w is docked first. If \a index is
591 -1 or larger than the number of docked widgets, \a w is appended
592 at the end, otherwise it is inserted at the position \a index.
593*/
594
595void Q3DockArea::moveDockWindow(Q3DockWindow *w, int index)
596{
597 invalidateFixedSizes();
598 Q3DockWindow *dockWindow = 0;
599 int dockWindowIndex = findDockWindow(w);
600 if (dockWindowIndex == -1) {
601 dockWindow = w;
602 bool vis = dockWindow->isVisible();
603 dockWindow->setParent(this);
604 dockWindow->move(0, 0);
605 if(vis)
606 dockWindow->show();
607 w->installEventFilter(this);
608 updateLayout();
609 setSizePolicy(QSizePolicy(orientation() == Qt::Horizontal ? QSizePolicy::Expanding : QSizePolicy::Minimum,
610 orientation() == Qt::Vertical ? QSizePolicy::Expanding : QSizePolicy::Minimum));
611 dockWindows.append(w);
612 } else {
613 if (w->parent() != this) {
614 bool vis = w->isVisible();
615 w->setParent(this);
616 w->move(0, 0);
617 if(vis)
618 w->show();
619 }
620 if (index == -1) {
621 dockWindows.removeAll(w);
622 dockWindows.append(w);
623 }
624 }
625
626 w->dockArea = this;
627 w->curPlace = Q3DockWindow::InDock;
628 w->updateGui();
629
630 if (index != -1 && index < (int)dockWindows.count()) {
631 dockWindows.removeAll(w);
632 dockWindows.insert(index, w);
633 }
634}
635
636/*!
637 Returns true if the dock area contains the dock window \a w;
638 otherwise returns false. If \a index is not 0 it will be set as
639 follows: if the dock area contains the dock window *\a{index} is
640 set to \a w's index position; otherwise *\a{index} is set to -1.
641*/
642
643bool Q3DockArea::hasDockWindow(Q3DockWindow *w, int *index)
644{
645 int i = dockWindows.indexOf(w);
646 if (index)
647 *index = i;
648 return i != -1;
649}
650
651int Q3DockArea::lineOf(int index)
652{
653 QList<Q3DockWindow *> lineStarts = layout->lineStarts();
654 int i = 0;
655 for (; i < lineStarts.size(); ++i) {
656 Q3DockWindow *w = lineStarts.at(i);
657 if (dockWindows.indexOf(w) >= index)
658 return i;
659 }
660 return i;
661}
662
663/*!
664 \overload
665
666 Moves the dock window \a w inside the dock area where \a p is the
667 new position (in global screen coordinates), \a r is the suggested
668 rectangle of the dock window and \a swap specifies whether or not
669 the orientation of the docked widget needs to be changed.
670
671 This function is used internally by Q3DockWindow. You shouldn't
672 need to call it yourself.
673*/
674
675void Q3DockArea::moveDockWindow(Q3DockWindow *w, const QPoint &p, const QRect &r, bool swap)
676{
677 invalidateFixedSizes();
678 int mse = -10;
679 bool hasResizable = false;
680 for (int i = 0; i < dockWindows.size(); ++i) {
681 Q3DockWindow *dw = dockWindows.at(i);
682 if (dw->isHidden())
683 continue;
684 if (dw->isResizeEnabled())
685 hasResizable = true;
686 if (orientation() != Qt::Horizontal)
687 mse = qMax(qMax(dw->fixedExtent().width(), dw->width()), mse);
688 else
689 mse = qMax(qMax(dw->fixedExtent().height(), dw->height()), mse);
690 }
691 if (!hasResizable && w->isResizeEnabled()) {
692 if (orientation() != Qt::Horizontal)
693 mse = qMax(w->fixedExtent().width(), mse);
694 else
695 mse = qMax(w->fixedExtent().height(), mse);
696 }
697
698 Q3DockWindow *dockWindow = 0;
699 int dockWindowIndex = findDockWindow(w);
700 QList<Q3DockWindow *> lineStarts = layout->lineStarts();
701 QList<QRect> lines = layout->lineList();
702 bool wasAloneInLine = false;
703 QPoint pos = mapFromGlobal(p);
704 int line = lineOf(dockWindowIndex);
705 QRect lr;
706 if (line < lines.size())
707 lr = lines.at(line);
708 if (dockWindowIndex != -1) {
709 if (lineStarts.contains(w)
710 && ((dockWindowIndex < dockWindows.count() - 1
711 && lineStarts.contains(dockWindows.at(dockWindowIndex + 1)))
712 || dockWindowIndex == dockWindows.count() - 1))
713 wasAloneInLine = true;
714 dockWindow = dockWindows.takeAt(dockWindowIndex);
715 if (!wasAloneInLine) { // only do the pre-layout if the widget isn't the only one in its line
716 if (lineStarts.contains(dockWindow) && dockWindowIndex < dockWindows.count())
717 dockWindows.at(dockWindowIndex)->setNewLine(true);
718 layout->layoutItems(QRect(0, 0, width(), height()), true);
719 }
720 } else {
721 dockWindow = w;
722 bool vis = dockWindow->isVisible();
723 dockWindow->setParent(this);
724 dockWindow->move(0, 0);
725 if(vis)
726 dockWindow->show();
727 if (swap)
728 dockWindow->resize(dockWindow->height(), dockWindow->width());
729 w->installEventFilter(this);
730 }
731
732 lineStarts = layout->lineStarts();
733 lines = layout->lineList();
734
735 QRect rect = QRect(mapFromGlobal(r.topLeft()), r.size());
736 if (orientation() == Qt::Horizontal && QApplication::reverseLayout()) {
737 rect = QRect(width() - rect.x() - rect.width(), rect.y(), rect.width(), rect.height());
738 pos.rx() = width() - pos.x();
739 }
740 dockWindow->setOffset(point_pos(rect.topLeft(), orientation()));
741 if (orientation() == Qt::Horizontal) {
742 int offs = dockWindow->offset();
743 if (width() - offs < dockWindow->minimumWidth())
744 dockWindow->setOffset(width() - dockWindow->minimumWidth());
745 } else {
746 int offs = dockWindow->offset();
747 if (height() - offs < dockWindow->minimumHeight())
748 dockWindow->setOffset(height() - dockWindow->minimumHeight());
749 }
750
751 if (dockWindows.isEmpty()) {
752 dockWindows.append(dockWindow);
753 } else {
754 int dockLine = -1;
755 bool insertLine = false;
756 int i = 0;
757 QRect lineRect;
758 // find the line which we touched with the mouse
759 for (QList<QRect>::Iterator it = lines.begin(); it != lines.end(); ++it, ++i) {
760 if (point_pos(pos, orientation(), true) >= point_pos((*it).topLeft(), orientation(), true) &&
761 point_pos(pos, orientation(), true) <= point_pos((*it).topLeft(), orientation(), true) +
762 size_extent((*it).size(), orientation(), true)) {
763 dockLine = i;
764 lineRect = *it;
765 break;
766 }
767 }
768 if (dockLine == -1) { // outside the dock...
769 insertLine = true;
770 if (point_pos(pos, orientation(), true) < 0) // insert as first line
771 dockLine = 0;
772 else
773 dockLine = (int)lines.count(); // insert after the last line ### size_t/int cast
774 } else { // inside the dock (we have found a dockLine)
775 if (point_pos(pos, orientation(), true) <
776 point_pos(lineRect.topLeft(), orientation(), true) + 4) { // mouse was at the very beginning of the line
777 insertLine = true; // insert a new line before that with the docking widget
778 } else if (point_pos(pos, orientation(), true) >
779 point_pos(lineRect.topLeft(), orientation(), true) +
780 size_extent(lineRect.size(), orientation(), true) - 4) { // mouse was at the very and of the line
781 insertLine = true; // insert a line after that with the docking widget
782 dockLine++;
783 }
784 }
785
786 if (!insertLine && wasAloneInLine && lr.contains(pos)) // if we are alone in a line and just moved in there, re-insert it
787 insertLine = true;
788
789#if defined(QDOCKAREA_DEBUG)
790 qDebug("insert in line %d, and insert that line: %d", dockLine, insertLine);
791 qDebug(" (btw, we have %d lines)", lines.count());
792#endif
793 Q3DockWindow *dw = 0;
794 if (dockLine >= (int)lines.count()) { // insert after last line
795 dockWindows.append(dockWindow);
796 dockWindow->setNewLine(true);
797#if defined(QDOCKAREA_DEBUG)
798 qDebug("insert at the end");
799#endif
800 } else if (dockLine == 0 && insertLine) { // insert before first line
801 dockWindows.insert(0, dockWindow);
802 dockWindows.at(1)->setNewLine(true);
803#if defined(QDOCKAREA_DEBUG)
804 qDebug("insert at the begin");
805#endif
806 } else { // insert somewhere in between
807 // make sure each line start has a new line
808 for (int i = 0; i < lineStarts.size(); ++i) {
809 dw = lineStarts.at(i);
810 dw->setNewLine(true);
811 }
812
813 // find the index of the first widget in the search line
814 int searchLine = dockLine;
815#if defined(QDOCKAREA_DEBUG)
816 qDebug("search line start of %d", searchLine);
817#endif
818 Q3DockWindow *lsw = lineStarts.at(searchLine);
819 int index = dockWindows.indexOf(lsw);
820 if (index == -1) { // the linestart widget hasn't been found, try to find it harder
821 if (lsw == w && dockWindowIndex <= dockWindows.count())
822 index = dockWindowIndex;
823 else
824 index = 0;
825 }
826#if defined(QDOCKAREA_DEBUG)
827 qDebug(" which starts at %d", index);
828#endif
829 if (!insertLine) { // if we insert the docking widget in the existing line
830 // find the index for the widget
831 bool inc = true;
832 bool firstTime = true;
833 for (int i = index; i < dockWindows.size(); ++i) {
834 dw = dockWindows.at(i);
835 if (orientation() == Qt::Horizontal)
836 dw->setFixedExtentWidth(-1);
837 else
838 dw->setFixedExtentHeight(-1);
839 if (!firstTime && lineStarts.contains(dw)) // we are in the next line, so break
840 break;
841 if (point_pos(pos, orientation()) <
842 point_pos(fix_pos(dw), orientation()) + size_extent(dw->size(), orientation()) / 2) {
843 inc = false;
844 }
845 if (inc)
846 index++;
847 firstTime = false;
848 }
849#if defined(QDOCKAREA_DEBUG)
850 qDebug("insert at index: %d", index);
851#endif
852 // if we insert it just before a widget which has a new line, transfer the newline to the docking widget
853 // but not if we didn't only mave a widget in its line which was alone in the line before
854 if (!(wasAloneInLine && lr.contains(pos))
855 && index >= 0 && index < dockWindows.count() &&
856 dockWindows.at(index)->newLine() && lineOf(index) == dockLine) {
857#if defined(QDOCKAREA_DEBUG)
858 qDebug("get rid of the old newline and get me one");
859#endif
860 dockWindows.at(index)->setNewLine(false);
861 dockWindow->setNewLine(true);
862 } else if (wasAloneInLine && lr.contains(pos)) {
863 dockWindow->setNewLine(true);
864 } else { // if we are somewhere in a line, get rid of the newline
865 dockWindow->setNewLine(false);
866 }
867 } else { // insert in a new line, so make sure the dock widget and the widget which will be after it have a newline
868#if defined(QDOCKAREA_DEBUG)
869 qDebug("insert a new line");
870#endif
871 if (index < dockWindows.count()) {
872#if defined(QDOCKAREA_DEBUG)
873 qDebug("give the widget at %d a newline", index);
874#endif
875 Q3DockWindow* nldw = dockWindows.at(index);
876 if (nldw)
877 nldw->setNewLine(true);
878 }
879#if defined(QDOCKAREA_DEBUG)
880 qDebug("give me a newline");
881#endif
882 dockWindow->setNewLine(true);
883 }
884 // finally insert the widget
885 dockWindows.insert(index, dockWindow);
886 }
887 }
888
889 if (mse != -10 && w->isResizeEnabled()) {
890 if (orientation() != Qt::Horizontal)
891 w->setFixedExtentWidth(qMin(qMax(w->minimumWidth(), mse), w->sizeHint().width()));
892 else
893 w->setFixedExtentHeight(qMin(qMax(w->minimumHeight(), mse), w->sizeHint().height()));
894 }
895
896 updateLayout();
897 setSizePolicy(QSizePolicy(orientation() == Qt::Horizontal ? QSizePolicy::Expanding : QSizePolicy::Minimum,
898 orientation() == Qt::Vertical ? QSizePolicy::Expanding : QSizePolicy::Minimum));
899}
900
901/*!
902 Removes the dock window \a w from the dock area. If \a
903 makeFloating is true, \a w gets floated, and if \a swap is true,
904 the orientation of \a w gets swapped. If \a fixNewLines is true
905 (the default) newlines in the area will be fixed.
906
907 You should never need to call this function yourself. Use
908 Q3DockWindow::dock() and Q3DockWindow::undock() instead.
909*/
910
911void Q3DockArea::removeDockWindow(Q3DockWindow *w, bool makeFloating, bool swap, bool fixNewLines)
912{
913 w->removeEventFilter(this);
914 Q3DockWindow *dockWindow = 0;
915 int i = findDockWindow(w);
916 if (i == -1)
917 return;
918 dockWindow = dockWindows.at(i);
919 dockWindows.removeAt(i);
920 QList<Q3DockWindow *> lineStarts = layout->lineStarts();
921 if (fixNewLines && lineStarts.contains(dockWindow) && i < dockWindows.count())
922 dockWindows.at(i)->setNewLine(true);
923 if (makeFloating) {
924 QWidget *p = parentWidget() ? parentWidget() : window();
925 dockWindow->setParent(p, Qt::WType_Dialog | Qt::WStyle_Customize | Qt::WStyle_NoBorder | Qt::WStyle_Tool);
926 dockWindow->move(0, 0);
927 }
928 if (swap)
929 dockWindow->resize(dockWindow->height(), dockWindow->width());
930 updateLayout();
931 if (dockWindows.isEmpty())
932 setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
933}
934
935int Q3DockArea::findDockWindow(Q3DockWindow *w)
936{
937 return dockWindows.indexOf(w);
938}
939
940void Q3DockArea::updateLayout()
941{
942 layout->invalidate();
943 layout->activate();
944}
945
946/*! \reimp
947 */
948
949bool Q3DockArea::eventFilter(QObject *o, QEvent *e)
950{
951 if (e->type() == QEvent::Close) {
952 if (qobject_cast<Q3DockWindow*>(o)) {
953 o->removeEventFilter(this);
954 QApplication::sendEvent(o, e);
955 if (((QCloseEvent*)e)->isAccepted())
956 removeDockWindow((Q3DockWindow*)o, false, false);
957 return true;
958 }
959 }
960 return false;
961}
962
963/*! \internal
964
965 Invalidates the offset of the next dock window in the dock area.
966 */
967
968void Q3DockArea::invalidNextOffset(Q3DockWindow *dw)
969{
970 int i = dockWindows.indexOf(dw);
971 if (i == -1 || i >= (int)dockWindows.count() - 1)
972 return;
973 if ((dw = dockWindows.at(++i)))
974 dw->setOffset(0);
975}
976
977/*!
978 \property Q3DockArea::count
979 \brief the number of dock windows in the dock area
980*/
981int Q3DockArea::count() const
982{
983 return dockWindows.count();
984}
985
986/*!
987 \property Q3DockArea::empty
988 \brief whether the dock area is empty
989*/
990
991bool Q3DockArea::isEmpty() const
992{
993 return dockWindows.isEmpty();
994}
995
996
997/*!
998 Returns a list of the dock windows in the dock area.
999*/
1000
1001QList<Q3DockWindow *> Q3DockArea::dockWindowList() const
1002{
1003 return dockWindows;
1004}
1005
1006/*!
1007 Lines up the dock windows in this dock area to minimize wasted
1008 space. If \a keepNewLines is true, only space within lines is
1009 cleaned up. If \a keepNewLines is false the number of lines might
1010 be changed.
1011*/
1012
1013void Q3DockArea::lineUp(bool keepNewLines)
1014{
1015 for (int i = 0; i < dockWindows.size(); ++i) {
1016 Q3DockWindow *dw = dockWindows.at(i);
1017 dw->setOffset(0);
1018 if (!keepNewLines)
1019 dw->setNewLine(false);
1020 }
1021 layout->activate();
1022}
1023
1024Q3DockArea::DockWindowData *Q3DockArea::dockWindowData(Q3DockWindow *w)
1025{
1026 DockWindowData *data = new DockWindowData;
1027 data->index = findDockWindow(w);
1028 if (data->index == -1) {
1029 delete data;
1030 return 0;
1031 }
1032 QList<Q3DockWindow *> lineStarts = layout->lineStarts();
1033 int i = -1;
1034 for (int j = 0; j < dockWindows.size(); ++j) {
1035 Q3DockWindow *dw = dockWindows.at(j);
1036 if (lineStarts.contains(dw))
1037 ++i;
1038 if (dw == w)
1039 break;
1040 }
1041 data->line = i;
1042 data->offset = point_pos(QPoint(fix_x(w), w->y()), orientation());
1043 data->area = this;
1044 data->fixedExtent = w->fixedExtent();
1045 return data;
1046}
1047
1048void Q3DockArea::dockWindow(Q3DockWindow *dockWindow, DockWindowData *data)
1049{
1050 if (!data)
1051 return;
1052
1053 dockWindow->setParent(this);
1054 dockWindow->move(0, 0);
1055
1056 dockWindow->installEventFilter(this);
1057 dockWindow->dockArea = this;
1058 dockWindow->updateGui();
1059
1060 if (dockWindows.isEmpty()) {
1061 dockWindows.append(dockWindow);
1062 } else {
1063 QList<Q3DockWindow *> lineStarts = layout->lineStarts();
1064 int index = 0;
1065 if (lineStarts.count() > data->line)
1066 index = dockWindows.indexOf(lineStarts.at(data->line));
1067 if (index == -1)
1068 index = 0;
1069 bool firstTime = true;
1070 int offset = data->offset;
1071 for (int i = index; i < dockWindows.size(); ++i) {
1072 Q3DockWindow *dw = dockWindows.at(i);
1073 if (!firstTime && lineStarts.contains(dw))
1074 break;
1075 if (offset <
1076 point_pos(fix_pos(dw), orientation()) + size_extent(dw->size(), orientation()) / 2)
1077 break;
1078 index++;
1079 firstTime = false;
1080 }
1081 if (index >= 0 && index < dockWindows.count() &&
1082 dockWindows.at(index)->newLine() && lineOf(index) == data->line) {
1083 dockWindows.at(index)->setNewLine(false);
1084 dockWindow->setNewLine(true);
1085 } else {
1086 dockWindow->setNewLine(false);
1087 }
1088
1089 dockWindows.insert(index, dockWindow);
1090 }
1091 dockWindow->show();
1092
1093 dockWindow->setFixedExtentWidth(data->fixedExtent.width());
1094 dockWindow->setFixedExtentHeight(data->fixedExtent.height());
1095
1096 updateLayout();
1097 setSizePolicy(QSizePolicy(orientation() == Qt::Horizontal ? QSizePolicy::Expanding : QSizePolicy::Minimum,
1098 orientation() == Qt::Vertical ? QSizePolicy::Expanding : QSizePolicy::Minimum));
1099
1100}
1101
1102/*!
1103 Returns true if dock window \a dw could be docked into the dock
1104 area; otherwise returns false.
1105
1106 \sa setAcceptDockWindow()
1107*/
1108
1109bool Q3DockArea::isDockWindowAccepted(Q3DockWindow *dw)
1110{
1111 if (!dw)
1112 return false;
1113 if (forbiddenWidgets.contains(dw))
1114 return false;
1115
1116 Q3MainWindow *mw = qobject_cast<Q3MainWindow*>(parentWidget());
1117 if (!mw)
1118 return true;
1119 if (!mw->hasDockWindow(dw))
1120 return false;
1121 if (!mw->isDockEnabled(this))
1122 return false;
1123 if (!mw->isDockEnabled(dw, this))
1124 return false;
1125 return true;
1126}
1127
1128/*!
1129 If \a accept is true, dock window \a dw can be docked in the dock
1130 area. If \a accept is false, dock window \a dw cannot be docked in
1131 the dock area.
1132
1133 \sa isDockWindowAccepted()
1134*/
1135
1136void Q3DockArea::setAcceptDockWindow(Q3DockWindow *dw, bool accept)
1137{
1138 if (accept)
1139 forbiddenWidgets.removeAll(dw);
1140 else if (forbiddenWidgets.contains(dw))
1141 forbiddenWidgets.append(dw);
1142}
1143
1144void Q3DockArea::invalidateFixedSizes()
1145{
1146 for (int i = 0; i < dockWindows.size(); ++i) {
1147 Q3DockWindow *dw = dockWindows.at(i);
1148 if (orientation() == Qt::Horizontal)
1149 dw->setFixedExtentWidth(-1);
1150 else
1151 dw->setFixedExtentHeight(-1);
1152 }
1153}
1154
1155int Q3DockArea::maxSpace(int hint, Q3DockWindow *dw)
1156{
1157 int index = findDockWindow(dw);
1158 if (index == -1 || index + 1 >= (int)dockWindows.count()) {
1159 if (orientation() == Qt::Horizontal)
1160 return dw->width();
1161 return dw->height();
1162 }
1163
1164 Q3DockWindow *w = 0;
1165 int i = 0;
1166 do {
1167 w = dockWindows.at(index + (++i));
1168 } while (i + 1 < (int)dockWindows.count() && (!w || w->isHidden()));
1169 if (!w || !w->isResizeEnabled() || i >= (int)dockWindows.count()) {
1170 if (orientation() == Qt::Horizontal)
1171 return dw->width();
1172 return dw->height();
1173 }
1174 int min = 0;
1175 Q3ToolBar *tb = qobject_cast<Q3ToolBar*>(w);
1176 if (orientation() == Qt::Horizontal) {
1177 w->setFixedExtentWidth(-1);
1178 if (!tb)
1179 min = qMax(w->minimumSize().width(), w->minimumSizeHint().width());
1180 else
1181 min = w->sizeHint().width();
1182 } else {
1183 w->setFixedExtentHeight(-1);
1184 if (!tb)
1185 min = qMax(w->minimumSize().height(), w->minimumSizeHint().height());
1186 else
1187 min = w->sizeHint().height();
1188 }
1189
1190 int diff = hint - (orientation() == Qt::Horizontal ? dw->width() : dw->height());
1191
1192 if ((orientation() == Qt::Horizontal ? w->width() : w->height()) - diff < min)
1193 hint = (orientation() == Qt::Horizontal ? dw->width() : dw->height()) + (orientation() == Qt::Horizontal ? w->width() : w->height()) - min;
1194
1195 diff = hint - (orientation() == Qt::Horizontal ? dw->width() : dw->height());
1196 if (orientation() == Qt::Horizontal)
1197 w->setFixedExtentWidth(w->width() - diff);
1198 else
1199 w->setFixedExtentHeight(w->height() - diff);
1200 return hint;
1201}
1202
1203void Q3DockArea::setFixedExtent(int d, Q3DockWindow *dw)
1204{
1205 QList<Q3DockWindow *> lst;
1206 for (int i = 0; i < dockWindows.size(); ++i) {
1207 Q3DockWindow *w = dockWindows.at(i);
1208 if (w->isHidden())
1209 continue;
1210 if (orientation() == Qt::Horizontal) {
1211 if (dw->y() != w->y())
1212 continue;
1213 } else {
1214 if (dw->x() != w->x())
1215 continue;
1216 }
1217 if (orientation() == Qt::Horizontal)
1218 d = qMax(d, w->minimumHeight());
1219 else
1220 d = qMax(d, w->minimumWidth());
1221 if (w->isResizeEnabled())
1222 lst.append(w);
1223 }
1224 for (int i = 0; i < lst.size(); ++i) {
1225 Q3DockWindow *w = lst.at(i);
1226 if (orientation() == Qt::Horizontal)
1227 w->setFixedExtentHeight(d);
1228 else
1229 w->setFixedExtentWidth(d);
1230 }
1231}
1232
1233bool Q3DockArea::isLastDockWindow(Q3DockWindow *dw)
1234{
1235 int i = dockWindows.indexOf(dw);
1236 if (i == -1 || i >= (int)dockWindows.count() - 1)
1237 return true;
1238 Q3DockWindow *w = 0;
1239 if ((w = dockWindows.at(++i))) {
1240 if (orientation() == Qt::Horizontal && dw->y() < w->y())
1241 return true;
1242 if (orientation() == Qt::Vertical && dw->x() < w->x())
1243 return true;
1244 } else {
1245 return true;
1246 }
1247 return false;
1248}
1249
1250#ifndef QT_NO_TEXTSTREAM
1251
1252/*!
1253 \relates Q3DockArea
1254
1255 Writes the layout of the dock windows in dock area \a dockArea to
1256 the text stream \a ts.
1257*/
1258
1259QTextStream &operator<<(QTextStream &ts, const Q3DockArea &dockArea)
1260{
1261 QString str;
1262 QList<Q3DockWindow *> l = dockArea.dockWindowList();
1263
1264 for (int i = 0; i < l.size(); ++i) {
1265 Q3DockWindow *dw = l.at(i);
1266 str += QLatin1Char('[') + QString(dw->windowTitle()) + QLatin1Char(',') + QString::number((int)dw->offset()) +
1267 QLatin1Char(',') + QString::number((int)dw->newLine()) + QLatin1Char(',') + QString::number(dw->fixedExtent().width()) +
1268 QLatin1Char(',') + QString::number(dw->fixedExtent().height()) + QLatin1Char(',') + QString::number((int)!dw->isHidden()) + QLatin1Char(']');
1269 }
1270 ts << str << endl;
1271
1272 return ts;
1273}
1274
1275/*!
1276 \relates Q3DockArea
1277
1278 Reads the layout description of the dock windows in dock area \a
1279 dockArea from the text stream \a ts and restores it. The layout
1280 description must have been previously written by the operator<<()
1281 function.
1282*/
1283
1284QTextStream &operator>>(QTextStream &ts, Q3DockArea &dockArea)
1285{
1286 QString s = ts.readLine();
1287
1288 QString name, offset, newLine, width, height, visible;
1289
1290 enum State { Pre, Name, Offset, NewLine, Width, Height, Visible, Post };
1291 int state = Pre;
1292 QChar c;
1293 QList<Q3DockWindow *> l = dockArea.dockWindowList();
1294
1295 for (int i = 0; i < s.length(); ++i) {
1296 c = s[i];
1297 if (state == Pre && c == QLatin1Char('[')) {
1298 state++;
1299 continue;
1300 }
1301 if (c == QLatin1Char(',') &&
1302 (state == Name || state == Offset || state == NewLine || state == Width || state == Height)) {
1303 state++;
1304 continue;
1305 }
1306 if (state == Visible && c == QLatin1Char(']')) {
1307 for (int j = 0; j < l.size(); ++j) {
1308 Q3DockWindow *dw = l.at(j);
1309 if (QString(dw->windowTitle()) == name) {
1310 dw->setNewLine((bool)newLine.toInt());
1311 dw->setOffset(offset.toInt());
1312 dw->setFixedExtentWidth(width.toInt());
1313 dw->setFixedExtentHeight(height.toInt());
1314 if (!(bool)visible.toInt())
1315 dw->hide();
1316 else
1317 dw->show();
1318 break;
1319 }
1320 }
1321
1322 name = offset = newLine = width = height = visible = QLatin1String("");
1323
1324 state = Pre;
1325 continue;
1326 }
1327 if (state == Name)
1328 name += c;
1329 else if (state == Offset)
1330 offset += c;
1331 else if (state == NewLine)
1332 newLine += c;
1333 else if (state == Width)
1334 width += c;
1335 else if (state == Height)
1336 height += c;
1337 else if (state == Visible)
1338 visible += c;
1339 }
1340
1341 dockArea.QWidget::layout()->invalidate();
1342 dockArea.QWidget::layout()->activate();
1343 return ts;
1344}
1345#endif
1346
1347QT_END_NAMESPACE
1348
1349#endif //QT_NO_MAINWINDOW
Note: See TracBrowser for help on using the repository browser.