/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the Qt3Support module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** 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.1, 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. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $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, you can't add new ones. | |
You also can't add a Q3DockArea to your own subclass of QWidget. | |
It won't be shown. | |
\img qmainwindow-qdockareas.png QMainWindow's Q3DockAreas | |
\target lines | |
\section1 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->invalidate(); | |
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 |