diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/qt3support/widgets/q3dockarea.cpp | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'src/qt3support/widgets/q3dockarea.cpp')
-rw-r--r-- | src/qt3support/widgets/q3dockarea.cpp | 1349 |
1 files changed, 1349 insertions, 0 deletions
diff --git a/src/qt3support/widgets/q3dockarea.cpp b/src/qt3support/widgets/q3dockarea.cpp new file mode 100644 index 0000000..1609aa4 --- /dev/null +++ b/src/qt3support/widgets/q3dockarea.cpp @@ -0,0 +1,1349 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3dockarea.h" + +#ifndef QT_NO_MAINWINDOW +#include "qsplitter.h" +#include "qevent.h" +#include "qlayout.h" +#include "qapplication.h" +#include "qpainter.h" +#include "qmap.h" +#include "q3mainwindow.h" +#include "q3toolbar.h" + +QT_BEGIN_NAMESPACE + +//#define QDOCKAREA_DEBUG + +struct Q3DockData +{ + Q3DockData() : w(0), rect() {} + Q3DockData(Q3DockWindow *dw, const QRect &r) : w(dw), rect(r) {} + Q3DockWindow *w; + QRect rect; + + Q_DUMMY_COMPARISON_OPERATOR(Q3DockData) +}; + +static int fix_x(Q3DockWindow* w, int width = -1) { + if (QApplication::reverseLayout()) { + if (width < 0) + width = w->width(); + return w->parentWidget()->width() - w->x() - width; + } + return w->x(); +} +static int fix_x(Q3DockWindow* w, int x, int width = -1) { + if (QApplication::reverseLayout()) { + if (width < 0) + width = w->width(); + return w->parentWidget()->width() - x - width; + } + return x; +} + +static QPoint fix_pos(Q3DockWindow* w) { + if (QApplication::reverseLayout()) { + QPoint p = w->pos(); + p.rx() = w->parentWidget()->width() - p.x() - w->width(); + return p; + } + return w->pos(); +} + + +void Q3DockAreaLayout::setGeometry(const QRect &r) +{ + QLayout::setGeometry(r); + layoutItems(r); +} + +QLayoutItem *Q3DockAreaLayout::itemAt(int) const +{ + return 0; //### +} + +QLayoutItem *Q3DockAreaLayout::takeAt(int) +{ + return 0; //### +} + +int Q3DockAreaLayout::count() const +{ + return 0; //### +} + + +QSize Q3DockAreaLayout::sizeHint() const +{ + if (dockWindows->isEmpty()) + return QSize(0, 0); + + if (dirty) { + Q3DockAreaLayout *that = (Q3DockAreaLayout *) this; + that->layoutItems(geometry()); + } + + int w = 0; + int h = 0; + int y = -1; + int x = -1; + int ph = 0; + int pw = 0; + for (int i = 0; i < dockWindows->size(); ++i) { + Q3DockWindow *dw = dockWindows->at(i); + int plush = 0, plusw = 0; + if (dw->isHidden()) + continue; + if (hasHeightForWidth()) { + if (y != dw->y()) + plush = ph; + y = dw->y(); + ph = dw->height(); + } else { + if (x != dw->x()) + plusw = pw; + x = dw->x(); + pw = dw->width(); + } + h = qMax(h, dw->height() + plush); + w = qMax(w, dw->width() + plusw); + } + + if (hasHeightForWidth()) + return QSize(0, h); + return QSize(w, 0); +} + +bool Q3DockAreaLayout::hasHeightForWidth() const +{ + return orient == Qt::Horizontal; +} + +void Q3DockAreaLayout::init() +{ + dirty = true; + cached_width = 0; + cached_height = 0; + cached_hfw = -1; + cached_wfh = -1; +} + +void Q3DockAreaLayout::invalidate() +{ + dirty = true; + cached_width = 0; + cached_height = 0; + QLayout::invalidate(); +} + +static int start_pos(const QRect &r, Qt::Orientation o) +{ + if (o == Qt::Horizontal) { + return qMax(0, r.x()); + } else { + return qMax(0, r.y()); + } +} + +static void add_size(int s, int &pos, Qt::Orientation o) +{ + if (o == Qt::Horizontal) { + pos += s; + } else { + pos += s; + } +} + +static int space_left(const QRect &r, int pos, Qt::Orientation o) +{ + if (o == Qt::Horizontal) { + return (r.x() + r.width()) - pos; + } else { + return (r.y() + r.height()) - pos; + } +} + +static int dock_extent(Q3DockWindow *w, Qt::Orientation o, int maxsize) +{ + if (o == Qt::Horizontal) + return qMin(maxsize, qMax(w->sizeHint().width(), w->fixedExtent().width())); + else + return qMin(maxsize, qMax(w->sizeHint().height(), w->fixedExtent().height())); +} + +static int dock_strut(Q3DockWindow *w, Qt::Orientation o) +{ + if (o != Qt::Horizontal) { + int wid; + if ((wid = w->fixedExtent().width()) != -1) + return qMax(wid, qMax(w->minimumSize().width(), w->minimumSizeHint().width())); + return qMax(w->sizeHint().width(), qMax(w->minimumSize().width(), w->minimumSizeHint().width())); + } else { + int hei; + if ((hei = w->fixedExtent().height()) != -1) + return qMax(hei, qMax(w->minimumSizeHint().height(), w->minimumSize().height())); + return qMax(w->sizeHint().height(), qMax(w->minimumSizeHint().height(), w->minimumSize().height())); + } +} + +static void set_geometry(Q3DockWindow *w, int pos, int sectionpos, int extent, int strut, Qt::Orientation o) +{ + if (o == Qt::Horizontal) + w->setGeometry(fix_x(w, pos, extent), sectionpos, extent, strut); + else + w->setGeometry(sectionpos, pos, strut, extent); +} + +static int size_extent(const QSize &s, Qt::Orientation o, bool swap = false) +{ + return o == Qt::Horizontal ? (swap ? s.height() : s.width()) : (swap ? s.width() : s.height()); +} + +static int point_pos(const QPoint &p, Qt::Orientation o, bool swap = false) +{ + return o == Qt::Horizontal ? (swap ? p.y() : p.x()) : (swap ? p.x() : p.y()); +} + +static void shrink_extend(Q3DockWindow *dw, int &dockExtend, int /*spaceLeft*/, Qt::Orientation o) +{ + Q3ToolBar *tb = qobject_cast<Q3ToolBar*>(dw); + if (o == Qt::Horizontal) { + int mw = 0; + if (!tb) + mw = dw->minimumWidth(); + else + mw = dw->sizeHint().width(); + dockExtend = mw; + } else { + int mh = 0; + if (!tb) + mh = dw->minimumHeight(); + else + mh = dw->sizeHint().height(); + dockExtend = mh; + } +} + +static void place_line(QList<Q3DockData> &lastLine, Qt::Orientation o, int linestrut, int fullextent, int tbstrut, int maxsize, Q3DockAreaLayout *) +{ + Q3DockWindow *last = 0; + QRect lastRect; + for (QList<Q3DockData>::Iterator it = lastLine.begin(); it != lastLine.end(); ++it) { + if (tbstrut != -1 && qobject_cast<Q3ToolBar*>((*it).w)) + (*it).rect.setHeight(tbstrut); + if (!last) { + last = (*it).w; + lastRect = (*it).rect; + continue; + } + if (!last->isStretchable()) { + int w = qMin(lastRect.width(), maxsize); + set_geometry(last, lastRect.x(), lastRect.y(), w, lastRect.height(), o); + } else { + int w = qMin((*it).rect.x() - lastRect.x(), maxsize); + set_geometry(last, lastRect.x(), lastRect.y(), w, + last->isResizeEnabled() ? linestrut : lastRect.height(), o); + } + last = (*it).w; + lastRect = (*it).rect; + } + if (!last) + return; + if (!last->isStretchable()) { + int w = qMin(lastRect.width(), maxsize); + set_geometry(last, lastRect.x(), lastRect.y(), w, lastRect.height(), o); + } else { + int w = qMin(fullextent - lastRect.x() - (o == Qt::Vertical ? 1 : 0), maxsize); + set_geometry(last, lastRect.x(), lastRect.y(), w, + last->isResizeEnabled() ? linestrut : lastRect.height(), o); + } +} + +QSize Q3DockAreaLayout::minimumSize() const +{ + if (dockWindows->isEmpty()) + return QSize(0, 0); + + if (dirty) { + Q3DockAreaLayout *that = (Q3DockAreaLayout *) this; + that->layoutItems(geometry()); + } + + int s = 0; + + for (int i = 0; i < dockWindows->size(); ++i) { + Q3DockWindow *dw = dockWindows->at(i); + if (dw->isHidden()) + continue; + s = qMax(s, dock_strut(dw, orientation())); + } + + return orientation() == Qt::Horizontal ? QSize(0, s ? s+2 : 0) : QSize(s, 0); +} + + + +int Q3DockAreaLayout::layoutItems(const QRect &rect, bool testonly) +{ + if (dockWindows->isEmpty()) + return 0; + + dirty = false; + + // some corrections + QRect r = rect; + if (orientation() == Qt::Vertical) + r.setHeight(r.height() - 3); + + // init + lines.clear(); + ls.clear(); + int start = start_pos(r, orientation()); + int pos = start; + int sectionpos = 0; + int linestrut = 0; + QList<Q3DockData> lastLine; + int tbstrut = -1; + int maxsize = size_extent(rect.size(), orientation()); + int visibleWindows = 0; + + // go through all widgets in the dock + for (int i = 0; i < dockWindows->size(); ++i) { + Q3DockWindow *dw = dockWindows->at(i); + if (dw->isHidden()) + continue; + ++visibleWindows; + // find position for the widget: This is the maximum of the + // end of the previous widget and the offset of the widget. If + // the position + the width of the widget dosn't fit into the + // dock, try moving it a bit back, if possible. + int op = pos; + int dockExtend = dock_extent(dw, orientation(), maxsize); + if (!dw->isStretchable()) { + pos = qMax(pos, dw->offset()); + if (pos + dockExtend > size_extent(r.size(), orientation()) - 1) + pos = qMax(op, size_extent(r.size(), orientation()) - 1 - dockExtend); + } + if (!lastLine.isEmpty() && !dw->newLine() && space_left(rect, pos, orientation()) < dockExtend) + shrink_extend(dw, dockExtend, space_left(rect, pos, orientation()), orientation()); + // if the current widget doesn't fit into the line anymore and it is not the first widget of the line + if (!lastLine.isEmpty() && + (space_left(rect, pos, orientation()) < dockExtend || dw->newLine())) { + if (!testonly) // place the last line, if not in test mode + place_line(lastLine, orientation(), linestrut, size_extent(r.size(), orientation()), tbstrut, maxsize, this); + // remember the line coordinats of the last line + if (orientation() == Qt::Horizontal) + lines.append(QRect(0, sectionpos, r.width(), linestrut)); + else + lines.append(QRect(sectionpos, 0, linestrut, r.height())); + // do some clearing for the next line + lastLine.clear(); + sectionpos += linestrut; + linestrut = 0; + pos = start; + tbstrut = -1; + } + + // remember first widget of a line + if (lastLine.isEmpty()) { + ls.append(dw); + // try to make the best position + int op = pos; + if (!dw->isStretchable()) + pos = qMax(pos, dw->offset()); + if (pos + dockExtend > size_extent(r.size(), orientation()) - 1) + pos = qMax(op, size_extent(r.size(), orientation()) - 1 - dockExtend); + } + // do some calculations and add the remember the rect which the docking widget requires for the placing + QRect dwRect(pos, sectionpos, dockExtend, dock_strut(dw, orientation() )); + lastLine.append(Q3DockData(dw, dwRect)); + if (qobject_cast<Q3ToolBar*>(dw)) + tbstrut = qMax(tbstrut, dock_strut(dw, orientation())); + linestrut = qMax(dock_strut(dw, orientation()), linestrut); + add_size(dockExtend, pos, orientation()); + } + + // if some stuff was not placed/stored yet, do it now + if (!testonly) + place_line(lastLine, orientation(), linestrut, size_extent(r.size(), orientation()), tbstrut, maxsize, this); + if (orientation() == Qt::Horizontal) + lines.append(QRect(0, sectionpos, r.width(), linestrut)); + else + lines.append(QRect(sectionpos, 0, linestrut, r.height())); + if (lines.size() >= 2 && *(--lines.end()) == *(--(--lines.end()))) + lines.removeLast(); + + bool hadResizable = false; + for (int i = 0; i < dockWindows->size(); ++i) { + Q3DockWindow *dw = dockWindows->at(i); + if (!dw->isVisibleTo(parentWidget)) + continue; + hadResizable = hadResizable || dw->isResizeEnabled(); + dw->updateSplitterVisibility(visibleWindows > 1); //!dw->area()->isLastDockWindow(dw)); + if (Q3ToolBar *tb = qobject_cast<Q3ToolBar *>(dw)) + tb->checkForExtension(dw->size()); + } + return sectionpos + linestrut; +} + +int Q3DockAreaLayout::heightForWidth(int w) const +{ + if (dockWindows->isEmpty() && parentWidget) + return parentWidget->minimumHeight(); + + if (cached_width != w) { + Q3DockAreaLayout * mthis = (Q3DockAreaLayout*)this; + mthis->cached_width = w; + int h = mthis->layoutItems(QRect(0, 0, w, 0), true); + mthis->cached_hfw = h; + return h; + } + + return cached_hfw; +} + +int Q3DockAreaLayout::widthForHeight(int h) const +{ + if (cached_height != h) { + Q3DockAreaLayout * mthis = (Q3DockAreaLayout*)this; + mthis->cached_height = h; + int w = mthis->layoutItems(QRect(0, 0, 0, h), true); + mthis->cached_wfh = w; + return w; + } + return cached_wfh; +} + + + + +/*! + \class Q3DockArea + \brief The Q3DockArea class manages and lays out Q3DockWindows. + + \compat + + A Q3DockArea is a container which manages a list of + \l{Q3DockWindow}s which it lays out within its area. In cooperation + with the \l{Q3DockWindow}s it is responsible for the docking and + undocking of \l{Q3DockWindow}s and moving them inside the dock + area. Q3DockAreas also handle the wrapping of \l{Q3DockWindow}s to + fill the available space as compactly as possible. Q3DockAreas can + contain Q3ToolBars since Q3ToolBar is a Q3DockWindow subclass. + + QMainWindow contains four Q3DockAreas which you can use for your + Q3ToolBars and Q3DockWindows, so in most situations you do not need + to use the Q3DockArea class directly. Although QMainWindow contains + support for its own dock areas it isn't convenient for adding new + Q3DockAreas. If you need to create your own dock areas we suggest + that you create a subclass of QWidget and add your Q3DockAreas to + your subclass. + + \img qmainwindow-qdockareas.png QMainWindow's Q3DockAreas + + \target lines + \e Lines. Q3DockArea uses the concept of lines. A line is a + horizontal region which may contain dock windows side-by-side. A + dock area may have room for more than one line. When dock windows + are docked into a dock area they are usually added at the right + hand side of the top-most line that has room (unless manually + placed by the user). When users move dock windows they may leave + empty lines or gaps in non-empty lines. Qt::Dock windows can be lined + up to minimize wasted space using the lineUp() function. + + The Q3DockArea class maintains a position list of all its child + dock windows. Qt::Dock windows are added to a dock area from position + 0 onwards. Qt::Dock windows are laid out sequentially in position + order from left to right, and in the case of multiple lines of + dock windows, from top to bottom. If a dock window is floated it + still retains its position since this is where the window will + return if the user double clicks its caption. A dock window's + position can be determined with hasDockWindow(). The position can + be changed with moveDockWindow(). + + To dock or undock a dock window use Q3DockWindow::dock() and + Q3DockWindow::undock() respectively. If you want to control which + dock windows can dock in a dock area use setAcceptDockWindow(). To + see if a dock area contains a particular dock window use + \l{hasDockWindow()}; to see how many dock windows a dock area + contains use count(). + + The streaming operators can write the positions of the dock + windows in the dock area to a QTextStream. The positions can be + read back later to restore the saved positions. + + Save the positions to a QTextStream: + \snippet doc/src/snippets/code/src_qt3support_widgets_q3dockarea.cpp 0 + + Restore the positions from a QTextStream: + \snippet doc/src/snippets/code/src_qt3support_widgets_q3dockarea.cpp 1 +*/ + +/*! + \property Q3DockArea::handlePosition + \brief where the dock window splitter handle is placed in the dock + area + + The default position is \c Normal. +*/ + +/*! + \property Q3DockArea::orientation + \brief the dock area's orientation + + There is no default value; the orientation is specified in the + constructor. +*/ + +/*! + \enum Q3DockArea::HandlePosition + + A dock window has two kinds of handles, the dock window handle + used for dragging the dock window, and the splitter handle used to + resize the dock window in relation to other dock windows using a + splitter. (The splitter handle is only visible for docked + windows.) + + This enum specifies where the dock window splitter handle is + placed in the dock area. + + \value Normal The splitter handles of dock windows are placed at + the right or bottom. + + \value Reverse The splitter handles of dock windows are placed at + the left or top. +*/ + +/*! + Constructs a Q3DockArea with orientation \a o, HandlePosition \a h, + parent \a parent and called \a name. +*/ + +Q3DockArea::Q3DockArea(Qt::Orientation o, HandlePosition h, QWidget *parent, const char *name) + : QWidget(parent, name), orient(o), layout(0), hPos(h) +{ + layout = new Q3DockAreaLayout(this, o, &dockWindows, 0, 0, "toollayout"); + installEventFilter(this); +} + +/*! + Destroys the dock area and all the dock windows docked in the dock + area. + + Does not affect any floating dock windows or dock windows in other + dock areas, even if they first appeared in this dock area. + Floating dock windows are effectively top level windows and are + not child windows of the dock area. When a floating dock window is + docked (dragged into a dock area) its parent becomes the dock + area. +*/ + +Q3DockArea::~Q3DockArea() +{ + while (!dockWindows.isEmpty()) + delete dockWindows.takeFirst(); +} + +/*! + Moves the Q3DockWindow \a w within the dock area. If \a w is not + already docked in this area, \a w is docked first. If \a index is + -1 or larger than the number of docked widgets, \a w is appended + at the end, otherwise it is inserted at the position \a index. +*/ + +void Q3DockArea::moveDockWindow(Q3DockWindow *w, int index) +{ + invalidateFixedSizes(); + Q3DockWindow *dockWindow = 0; + int dockWindowIndex = findDockWindow(w); + if (dockWindowIndex == -1) { + dockWindow = w; + bool vis = dockWindow->isVisible(); + dockWindow->setParent(this); + dockWindow->move(0, 0); + if(vis) + dockWindow->show(); + w->installEventFilter(this); + updateLayout(); + setSizePolicy(QSizePolicy(orientation() == Qt::Horizontal ? QSizePolicy::Expanding : QSizePolicy::Minimum, + orientation() == Qt::Vertical ? QSizePolicy::Expanding : QSizePolicy::Minimum)); + dockWindows.append(w); + } else { + if (w->parent() != this) { + bool vis = w->isVisible(); + w->setParent(this); + w->move(0, 0); + if(vis) + w->show(); + } + if (index == -1) { + dockWindows.removeAll(w); + dockWindows.append(w); + } + } + + w->dockArea = this; + w->curPlace = Q3DockWindow::InDock; + w->updateGui(); + + if (index != -1 && index < (int)dockWindows.count()) { + dockWindows.removeAll(w); + dockWindows.insert(index, w); + } +} + +/*! + Returns true if the dock area contains the dock window \a w; + otherwise returns false. If \a index is not 0 it will be set as + follows: if the dock area contains the dock window *\a{index} is + set to \a w's index position; otherwise *\a{index} is set to -1. +*/ + +bool Q3DockArea::hasDockWindow(Q3DockWindow *w, int *index) +{ + int i = dockWindows.indexOf(w); + if (index) + *index = i; + return i != -1; +} + +int Q3DockArea::lineOf(int index) +{ + QList<Q3DockWindow *> lineStarts = layout->lineStarts(); + int i = 0; + for (; i < lineStarts.size(); ++i) { + Q3DockWindow *w = lineStarts.at(i); + if (dockWindows.indexOf(w) >= index) + return i; + } + return i; +} + +/*! + \overload + + Moves the dock window \a w inside the dock area where \a p is the + new position (in global screen coordinates), \a r is the suggested + rectangle of the dock window and \a swap specifies whether or not + the orientation of the docked widget needs to be changed. + + This function is used internally by Q3DockWindow. You shouldn't + need to call it yourself. +*/ + +void Q3DockArea::moveDockWindow(Q3DockWindow *w, const QPoint &p, const QRect &r, bool swap) +{ + invalidateFixedSizes(); + int mse = -10; + bool hasResizable = false; + for (int i = 0; i < dockWindows.size(); ++i) { + Q3DockWindow *dw = dockWindows.at(i); + if (dw->isHidden()) + continue; + if (dw->isResizeEnabled()) + hasResizable = true; + if (orientation() != Qt::Horizontal) + mse = qMax(qMax(dw->fixedExtent().width(), dw->width()), mse); + else + mse = qMax(qMax(dw->fixedExtent().height(), dw->height()), mse); + } + if (!hasResizable && w->isResizeEnabled()) { + if (orientation() != Qt::Horizontal) + mse = qMax(w->fixedExtent().width(), mse); + else + mse = qMax(w->fixedExtent().height(), mse); + } + + Q3DockWindow *dockWindow = 0; + int dockWindowIndex = findDockWindow(w); + QList<Q3DockWindow *> lineStarts = layout->lineStarts(); + QList<QRect> lines = layout->lineList(); + bool wasAloneInLine = false; + QPoint pos = mapFromGlobal(p); + int line = lineOf(dockWindowIndex); + QRect lr; + if (line < lines.size()) + lr = lines.at(line); + if (dockWindowIndex != -1) { + if (lineStarts.contains(w) + && ((dockWindowIndex < dockWindows.count() - 1 + && lineStarts.contains(dockWindows.at(dockWindowIndex + 1))) + || dockWindowIndex == dockWindows.count() - 1)) + wasAloneInLine = true; + dockWindow = dockWindows.takeAt(dockWindowIndex); + if (!wasAloneInLine) { // only do the pre-layout if the widget isn't the only one in its line + if (lineStarts.contains(dockWindow) && dockWindowIndex < dockWindows.count()) + dockWindows.at(dockWindowIndex)->setNewLine(true); + layout->layoutItems(QRect(0, 0, width(), height()), true); + } + } else { + dockWindow = w; + bool vis = dockWindow->isVisible(); + dockWindow->setParent(this); + dockWindow->move(0, 0); + if(vis) + dockWindow->show(); + if (swap) + dockWindow->resize(dockWindow->height(), dockWindow->width()); + w->installEventFilter(this); + } + + lineStarts = layout->lineStarts(); + lines = layout->lineList(); + + QRect rect = QRect(mapFromGlobal(r.topLeft()), r.size()); + if (orientation() == Qt::Horizontal && QApplication::reverseLayout()) { + rect = QRect(width() - rect.x() - rect.width(), rect.y(), rect.width(), rect.height()); + pos.rx() = width() - pos.x(); + } + dockWindow->setOffset(point_pos(rect.topLeft(), orientation())); + if (orientation() == Qt::Horizontal) { + int offs = dockWindow->offset(); + if (width() - offs < dockWindow->minimumWidth()) + dockWindow->setOffset(width() - dockWindow->minimumWidth()); + } else { + int offs = dockWindow->offset(); + if (height() - offs < dockWindow->minimumHeight()) + dockWindow->setOffset(height() - dockWindow->minimumHeight()); + } + + if (dockWindows.isEmpty()) { + dockWindows.append(dockWindow); + } else { + int dockLine = -1; + bool insertLine = false; + int i = 0; + QRect lineRect; + // find the line which we touched with the mouse + for (QList<QRect>::Iterator it = lines.begin(); it != lines.end(); ++it, ++i) { + if (point_pos(pos, orientation(), true) >= point_pos((*it).topLeft(), orientation(), true) && + point_pos(pos, orientation(), true) <= point_pos((*it).topLeft(), orientation(), true) + + size_extent((*it).size(), orientation(), true)) { + dockLine = i; + lineRect = *it; + break; + } + } + if (dockLine == -1) { // outside the dock... + insertLine = true; + if (point_pos(pos, orientation(), true) < 0) // insert as first line + dockLine = 0; + else + dockLine = (int)lines.count(); // insert after the last line ### size_t/int cast + } else { // inside the dock (we have found a dockLine) + if (point_pos(pos, orientation(), true) < + point_pos(lineRect.topLeft(), orientation(), true) + 4) { // mouse was at the very beginning of the line + insertLine = true; // insert a new line before that with the docking widget + } else if (point_pos(pos, orientation(), true) > + point_pos(lineRect.topLeft(), orientation(), true) + + size_extent(lineRect.size(), orientation(), true) - 4) { // mouse was at the very and of the line + insertLine = true; // insert a line after that with the docking widget + dockLine++; + } + } + + if (!insertLine && wasAloneInLine && lr.contains(pos)) // if we are alone in a line and just moved in there, re-insert it + insertLine = true; + +#if defined(QDOCKAREA_DEBUG) + qDebug("insert in line %d, and insert that line: %d", dockLine, insertLine); + qDebug(" (btw, we have %d lines)", lines.count()); +#endif + Q3DockWindow *dw = 0; + if (dockLine >= (int)lines.count()) { // insert after last line + dockWindows.append(dockWindow); + dockWindow->setNewLine(true); +#if defined(QDOCKAREA_DEBUG) + qDebug("insert at the end"); +#endif + } else if (dockLine == 0 && insertLine) { // insert before first line + dockWindows.insert(0, dockWindow); + dockWindows.at(1)->setNewLine(true); +#if defined(QDOCKAREA_DEBUG) + qDebug("insert at the begin"); +#endif + } else { // insert somewhere in between + // make sure each line start has a new line + for (int i = 0; i < lineStarts.size(); ++i) { + dw = lineStarts.at(i); + dw->setNewLine(true); + } + + // find the index of the first widget in the search line + int searchLine = dockLine; +#if defined(QDOCKAREA_DEBUG) + qDebug("search line start of %d", searchLine); +#endif + Q3DockWindow *lsw = lineStarts.at(searchLine); + int index = dockWindows.indexOf(lsw); + if (index == -1) { // the linestart widget hasn't been found, try to find it harder + if (lsw == w && dockWindowIndex <= dockWindows.count()) + index = dockWindowIndex; + else + index = 0; + } +#if defined(QDOCKAREA_DEBUG) + qDebug(" which starts at %d", index); +#endif + if (!insertLine) { // if we insert the docking widget in the existing line + // find the index for the widget + bool inc = true; + bool firstTime = true; + for (int i = index; i < dockWindows.size(); ++i) { + dw = dockWindows.at(i); + if (orientation() == Qt::Horizontal) + dw->setFixedExtentWidth(-1); + else + dw->setFixedExtentHeight(-1); + if (!firstTime && lineStarts.contains(dw)) // we are in the next line, so break + break; + if (point_pos(pos, orientation()) < + point_pos(fix_pos(dw), orientation()) + size_extent(dw->size(), orientation()) / 2) { + inc = false; + } + if (inc) + index++; + firstTime = false; + } +#if defined(QDOCKAREA_DEBUG) + qDebug("insert at index: %d", index); +#endif + // if we insert it just before a widget which has a new line, transfer the newline to the docking widget + // but not if we didn't only mave a widget in its line which was alone in the line before + if (!(wasAloneInLine && lr.contains(pos)) + && index >= 0 && index < dockWindows.count() && + dockWindows.at(index)->newLine() && lineOf(index) == dockLine) { +#if defined(QDOCKAREA_DEBUG) + qDebug("get rid of the old newline and get me one"); +#endif + dockWindows.at(index)->setNewLine(false); + dockWindow->setNewLine(true); + } else if (wasAloneInLine && lr.contains(pos)) { + dockWindow->setNewLine(true); + } else { // if we are somewhere in a line, get rid of the newline + dockWindow->setNewLine(false); + } + } else { // insert in a new line, so make sure the dock widget and the widget which will be after it have a newline +#if defined(QDOCKAREA_DEBUG) + qDebug("insert a new line"); +#endif + if (index < dockWindows.count()) { +#if defined(QDOCKAREA_DEBUG) + qDebug("give the widget at %d a newline", index); +#endif + Q3DockWindow* nldw = dockWindows.at(index); + if (nldw) + nldw->setNewLine(true); + } +#if defined(QDOCKAREA_DEBUG) + qDebug("give me a newline"); +#endif + dockWindow->setNewLine(true); + } + // finally insert the widget + dockWindows.insert(index, dockWindow); + } + } + + if (mse != -10 && w->isResizeEnabled()) { + if (orientation() != Qt::Horizontal) + w->setFixedExtentWidth(qMin(qMax(w->minimumWidth(), mse), w->sizeHint().width())); + else + w->setFixedExtentHeight(qMin(qMax(w->minimumHeight(), mse), w->sizeHint().height())); + } + + updateLayout(); + setSizePolicy(QSizePolicy(orientation() == Qt::Horizontal ? QSizePolicy::Expanding : QSizePolicy::Minimum, + orientation() == Qt::Vertical ? QSizePolicy::Expanding : QSizePolicy::Minimum)); +} + +/*! + Removes the dock window \a w from the dock area. If \a + makeFloating is true, \a w gets floated, and if \a swap is true, + the orientation of \a w gets swapped. If \a fixNewLines is true + (the default) newlines in the area will be fixed. + + You should never need to call this function yourself. Use + Q3DockWindow::dock() and Q3DockWindow::undock() instead. +*/ + +void Q3DockArea::removeDockWindow(Q3DockWindow *w, bool makeFloating, bool swap, bool fixNewLines) +{ + w->removeEventFilter(this); + Q3DockWindow *dockWindow = 0; + int i = findDockWindow(w); + if (i == -1) + return; + dockWindow = dockWindows.at(i); + dockWindows.removeAt(i); + QList<Q3DockWindow *> lineStarts = layout->lineStarts(); + if (fixNewLines && lineStarts.contains(dockWindow) && i < dockWindows.count()) + dockWindows.at(i)->setNewLine(true); + if (makeFloating) { + QWidget *p = parentWidget() ? parentWidget() : window(); + dockWindow->setParent(p, Qt::WType_Dialog | Qt::WStyle_Customize | Qt::WStyle_NoBorder | Qt::WStyle_Tool); + dockWindow->move(0, 0); + } + if (swap) + dockWindow->resize(dockWindow->height(), dockWindow->width()); + updateLayout(); + if (dockWindows.isEmpty()) + setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred)); +} + +int Q3DockArea::findDockWindow(Q3DockWindow *w) +{ + return dockWindows.indexOf(w); +} + +void Q3DockArea::updateLayout() +{ + layout->invalidate(); + layout->activate(); +} + +/*! \reimp + */ + +bool Q3DockArea::eventFilter(QObject *o, QEvent *e) +{ + if (e->type() == QEvent::Close) { + if (qobject_cast<Q3DockWindow*>(o)) { + o->removeEventFilter(this); + QApplication::sendEvent(o, e); + if (((QCloseEvent*)e)->isAccepted()) + removeDockWindow((Q3DockWindow*)o, false, false); + return true; + } + } + return false; +} + +/*! \internal + + Invalidates the offset of the next dock window in the dock area. + */ + +void Q3DockArea::invalidNextOffset(Q3DockWindow *dw) +{ + int i = dockWindows.indexOf(dw); + if (i == -1 || i >= (int)dockWindows.count() - 1) + return; + if ((dw = dockWindows.at(++i))) + dw->setOffset(0); +} + +/*! + \property Q3DockArea::count + \brief the number of dock windows in the dock area +*/ +int Q3DockArea::count() const +{ + return dockWindows.count(); +} + +/*! + \property Q3DockArea::empty + \brief whether the dock area is empty +*/ + +bool Q3DockArea::isEmpty() const +{ + return dockWindows.isEmpty(); +} + + +/*! + Returns a list of the dock windows in the dock area. +*/ + +QList<Q3DockWindow *> Q3DockArea::dockWindowList() const +{ + return dockWindows; +} + +/*! + Lines up the dock windows in this dock area to minimize wasted + space. If \a keepNewLines is true, only space within lines is + cleaned up. If \a keepNewLines is false the number of lines might + be changed. +*/ + +void Q3DockArea::lineUp(bool keepNewLines) +{ + for (int i = 0; i < dockWindows.size(); ++i) { + Q3DockWindow *dw = dockWindows.at(i); + dw->setOffset(0); + if (!keepNewLines) + dw->setNewLine(false); + } + layout->activate(); +} + +Q3DockArea::DockWindowData *Q3DockArea::dockWindowData(Q3DockWindow *w) +{ + DockWindowData *data = new DockWindowData; + data->index = findDockWindow(w); + if (data->index == -1) { + delete data; + return 0; + } + QList<Q3DockWindow *> lineStarts = layout->lineStarts(); + int i = -1; + for (int j = 0; j < dockWindows.size(); ++j) { + Q3DockWindow *dw = dockWindows.at(j); + if (lineStarts.contains(dw)) + ++i; + if (dw == w) + break; + } + data->line = i; + data->offset = point_pos(QPoint(fix_x(w), w->y()), orientation()); + data->area = this; + data->fixedExtent = w->fixedExtent(); + return data; +} + +void Q3DockArea::dockWindow(Q3DockWindow *dockWindow, DockWindowData *data) +{ + if (!data) + return; + + dockWindow->setParent(this); + dockWindow->move(0, 0); + + dockWindow->installEventFilter(this); + dockWindow->dockArea = this; + dockWindow->updateGui(); + + if (dockWindows.isEmpty()) { + dockWindows.append(dockWindow); + } else { + QList<Q3DockWindow *> lineStarts = layout->lineStarts(); + int index = 0; + if (lineStarts.count() > data->line) + index = dockWindows.indexOf(lineStarts.at(data->line)); + if (index == -1) + index = 0; + bool firstTime = true; + int offset = data->offset; + for (int i = index; i < dockWindows.size(); ++i) { + Q3DockWindow *dw = dockWindows.at(i); + if (!firstTime && lineStarts.contains(dw)) + break; + if (offset < + point_pos(fix_pos(dw), orientation()) + size_extent(dw->size(), orientation()) / 2) + break; + index++; + firstTime = false; + } + if (index >= 0 && index < dockWindows.count() && + dockWindows.at(index)->newLine() && lineOf(index) == data->line) { + dockWindows.at(index)->setNewLine(false); + dockWindow->setNewLine(true); + } else { + dockWindow->setNewLine(false); + } + + dockWindows.insert(index, dockWindow); + } + dockWindow->show(); + + dockWindow->setFixedExtentWidth(data->fixedExtent.width()); + dockWindow->setFixedExtentHeight(data->fixedExtent.height()); + + updateLayout(); + setSizePolicy(QSizePolicy(orientation() == Qt::Horizontal ? QSizePolicy::Expanding : QSizePolicy::Minimum, + orientation() == Qt::Vertical ? QSizePolicy::Expanding : QSizePolicy::Minimum)); + +} + +/*! + Returns true if dock window \a dw could be docked into the dock + area; otherwise returns false. + + \sa setAcceptDockWindow() +*/ + +bool Q3DockArea::isDockWindowAccepted(Q3DockWindow *dw) +{ + if (!dw) + return false; + if (forbiddenWidgets.contains(dw)) + return false; + + Q3MainWindow *mw = qobject_cast<Q3MainWindow*>(parentWidget()); + if (!mw) + return true; + if (!mw->hasDockWindow(dw)) + return false; + if (!mw->isDockEnabled(this)) + return false; + if (!mw->isDockEnabled(dw, this)) + return false; + return true; +} + +/*! + If \a accept is true, dock window \a dw can be docked in the dock + area. If \a accept is false, dock window \a dw cannot be docked in + the dock area. + + \sa isDockWindowAccepted() +*/ + +void Q3DockArea::setAcceptDockWindow(Q3DockWindow *dw, bool accept) +{ + if (accept) + forbiddenWidgets.removeAll(dw); + else if (forbiddenWidgets.contains(dw)) + forbiddenWidgets.append(dw); +} + +void Q3DockArea::invalidateFixedSizes() +{ + for (int i = 0; i < dockWindows.size(); ++i) { + Q3DockWindow *dw = dockWindows.at(i); + if (orientation() == Qt::Horizontal) + dw->setFixedExtentWidth(-1); + else + dw->setFixedExtentHeight(-1); + } +} + +int Q3DockArea::maxSpace(int hint, Q3DockWindow *dw) +{ + int index = findDockWindow(dw); + if (index == -1 || index + 1 >= (int)dockWindows.count()) { + if (orientation() == Qt::Horizontal) + return dw->width(); + return dw->height(); + } + + Q3DockWindow *w = 0; + int i = 0; + do { + w = dockWindows.at(index + (++i)); + } while (i + 1 < (int)dockWindows.count() && (!w || w->isHidden())); + if (!w || !w->isResizeEnabled() || i >= (int)dockWindows.count()) { + if (orientation() == Qt::Horizontal) + return dw->width(); + return dw->height(); + } + int min = 0; + Q3ToolBar *tb = qobject_cast<Q3ToolBar*>(w); + if (orientation() == Qt::Horizontal) { + w->setFixedExtentWidth(-1); + if (!tb) + min = qMax(w->minimumSize().width(), w->minimumSizeHint().width()); + else + min = w->sizeHint().width(); + } else { + w->setFixedExtentHeight(-1); + if (!tb) + min = qMax(w->minimumSize().height(), w->minimumSizeHint().height()); + else + min = w->sizeHint().height(); + } + + int diff = hint - (orientation() == Qt::Horizontal ? dw->width() : dw->height()); + + if ((orientation() == Qt::Horizontal ? w->width() : w->height()) - diff < min) + hint = (orientation() == Qt::Horizontal ? dw->width() : dw->height()) + (orientation() == Qt::Horizontal ? w->width() : w->height()) - min; + + diff = hint - (orientation() == Qt::Horizontal ? dw->width() : dw->height()); + if (orientation() == Qt::Horizontal) + w->setFixedExtentWidth(w->width() - diff); + else + w->setFixedExtentHeight(w->height() - diff); + return hint; +} + +void Q3DockArea::setFixedExtent(int d, Q3DockWindow *dw) +{ + QList<Q3DockWindow *> lst; + for (int i = 0; i < dockWindows.size(); ++i) { + Q3DockWindow *w = dockWindows.at(i); + if (w->isHidden()) + continue; + if (orientation() == Qt::Horizontal) { + if (dw->y() != w->y()) + continue; + } else { + if (dw->x() != w->x()) + continue; + } + if (orientation() == Qt::Horizontal) + d = qMax(d, w->minimumHeight()); + else + d = qMax(d, w->minimumWidth()); + if (w->isResizeEnabled()) + lst.append(w); + } + for (int i = 0; i < lst.size(); ++i) { + Q3DockWindow *w = lst.at(i); + if (orientation() == Qt::Horizontal) + w->setFixedExtentHeight(d); + else + w->setFixedExtentWidth(d); + } +} + +bool Q3DockArea::isLastDockWindow(Q3DockWindow *dw) +{ + int i = dockWindows.indexOf(dw); + if (i == -1 || i >= (int)dockWindows.count() - 1) + return true; + Q3DockWindow *w = 0; + if ((w = dockWindows.at(++i))) { + if (orientation() == Qt::Horizontal && dw->y() < w->y()) + return true; + if (orientation() == Qt::Vertical && dw->x() < w->x()) + return true; + } else { + return true; + } + return false; +} + +#ifndef QT_NO_TEXTSTREAM + +/*! + \relates Q3DockArea + + Writes the layout of the dock windows in dock area \a dockArea to + the text stream \a ts. +*/ + +QTextStream &operator<<(QTextStream &ts, const Q3DockArea &dockArea) +{ + QString str; + QList<Q3DockWindow *> l = dockArea.dockWindowList(); + + for (int i = 0; i < l.size(); ++i) { + Q3DockWindow *dw = l.at(i); + str += QLatin1Char('[') + QString(dw->windowTitle()) + QLatin1Char(',') + QString::number((int)dw->offset()) + + QLatin1Char(',') + QString::number((int)dw->newLine()) + QLatin1Char(',') + QString::number(dw->fixedExtent().width()) + + QLatin1Char(',') + QString::number(dw->fixedExtent().height()) + QLatin1Char(',') + QString::number((int)!dw->isHidden()) + QLatin1Char(']'); + } + ts << str << endl; + + return ts; +} + +/*! + \relates Q3DockArea + + Reads the layout description of the dock windows in dock area \a + dockArea from the text stream \a ts and restores it. The layout + description must have been previously written by the operator<<() + function. +*/ + +QTextStream &operator>>(QTextStream &ts, Q3DockArea &dockArea) +{ + QString s = ts.readLine(); + + QString name, offset, newLine, width, height, visible; + + enum State { Pre, Name, Offset, NewLine, Width, Height, Visible, Post }; + int state = Pre; + QChar c; + QList<Q3DockWindow *> l = dockArea.dockWindowList(); + + for (int i = 0; i < s.length(); ++i) { + c = s[i]; + if (state == Pre && c == QLatin1Char('[')) { + state++; + continue; + } + if (c == QLatin1Char(',') && + (state == Name || state == Offset || state == NewLine || state == Width || state == Height)) { + state++; + continue; + } + if (state == Visible && c == QLatin1Char(']')) { + for (int j = 0; j < l.size(); ++j) { + Q3DockWindow *dw = l.at(j); + if (QString(dw->windowTitle()) == name) { + dw->setNewLine((bool)newLine.toInt()); + dw->setOffset(offset.toInt()); + dw->setFixedExtentWidth(width.toInt()); + dw->setFixedExtentHeight(height.toInt()); + if (!(bool)visible.toInt()) + dw->hide(); + else + dw->show(); + break; + } + } + + name = offset = newLine = width = height = visible = QLatin1String(""); + + state = Pre; + continue; + } + if (state == Name) + name += c; + else if (state == Offset) + offset += c; + else if (state == NewLine) + newLine += c; + else if (state == Width) + width += c; + else if (state == Height) + height += c; + else if (state == Visible) + visible += c; + } + + dockArea.QWidget::layout()->invalidate(); + dockArea.QWidget::layout()->activate(); + return ts; +} +#endif + +QT_END_NAMESPACE + +#endif //QT_NO_MAINWINDOW |