/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** 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, Digia gives you certain additional ** rights. These rights are described in the Digia 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. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "QtWidgets/qapplication.h" #include "QtWidgets/qwidget.h" #include "QtWidgets/qtabbar.h" #include "QtWidgets/qstyle.h" #include "QtWidgets/qdesktopwidget.h" #include "QtCore/qvariant.h" #include "qdockarealayout_p.h" #include "qdockwidget.h" #include "qmainwindow.h" #include "qwidgetanimator_p.h" #include "qmainwindowlayout_p.h" #include "qdockwidget_p.h" #include #include #include #ifndef QT_NO_DOCKWIDGET QT_BEGIN_NAMESPACE // qmainwindow.cpp extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window); enum { StateFlagVisible = 1, StateFlagFloating = 2 }; /****************************************************************************** ** QPlaceHolderItem */ QPlaceHolderItem::QPlaceHolderItem(QWidget *w) { objectName = w->objectName(); hidden = w->isHidden(); window = w->isWindow(); if (window) topLevelRect = w->geometry(); } /****************************************************************************** ** QDockAreaLayoutItem */ QDockAreaLayoutItem::QDockAreaLayoutItem(QLayoutItem *_widgetItem) : widgetItem(_widgetItem), subinfo(0), placeHolderItem(0), pos(0), size(-1), flags(NoFlags) { } QDockAreaLayoutItem::QDockAreaLayoutItem(QDockAreaLayoutInfo *_subinfo) : widgetItem(0), subinfo(_subinfo), placeHolderItem(0), pos(0), size(-1), flags(NoFlags) { } QDockAreaLayoutItem::QDockAreaLayoutItem(QPlaceHolderItem *_placeHolderItem) : widgetItem(0), subinfo(0), placeHolderItem(_placeHolderItem), pos(0), size(-1), flags(NoFlags) { } QDockAreaLayoutItem::QDockAreaLayoutItem(const QDockAreaLayoutItem &other) : widgetItem(other.widgetItem), subinfo(0), placeHolderItem(0), pos(other.pos), size(other.size), flags(other.flags) { if (other.subinfo != 0) subinfo = new QDockAreaLayoutInfo(*other.subinfo); else if (other.placeHolderItem != 0) placeHolderItem = new QPlaceHolderItem(*other.placeHolderItem); } QDockAreaLayoutItem::~QDockAreaLayoutItem() { delete subinfo; delete placeHolderItem; } bool QDockAreaLayoutItem::skip() const { if (placeHolderItem != 0) return true; if (flags & GapItem) return false; if (widgetItem != 0) return widgetItem->isEmpty(); if (subinfo != 0) { for (int i = 0; i < subinfo->item_list.count(); ++i) { if (!subinfo->item_list.at(i).skip()) return false; } } return true; } QSize QDockAreaLayoutItem::minimumSize() const { if (widgetItem != 0) { int left, top, right, bottom; widgetItem->widget()->getContentsMargins(&left, &top, &right, &bottom); return widgetItem->minimumSize() + QSize(left+right, top+bottom); } if (subinfo != 0) return subinfo->minimumSize(); return QSize(0, 0); } QSize QDockAreaLayoutItem::maximumSize() const { if (widgetItem != 0) { int left, top, right, bottom; widgetItem->widget()->getContentsMargins(&left, &top, &right, &bottom); return widgetItem->maximumSize()+ QSize(left+right, top+bottom); } if (subinfo != 0) return subinfo->maximumSize(); return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); } bool QDockAreaLayoutItem::hasFixedSize(Qt::Orientation o) const { return perp(o, minimumSize()) == perp(o, maximumSize()); } bool QDockAreaLayoutItem::expansive(Qt::Orientation o) const { if ((flags & GapItem) || placeHolderItem != 0) return false; if (widgetItem != 0) return ((widgetItem->expandingDirections() & o) == o); if (subinfo != 0) return subinfo->expansive(o); return false; } QSize QDockAreaLayoutItem::sizeHint() const { if (placeHolderItem != 0) return QSize(0, 0); if (widgetItem != 0) { int left, top, right, bottom; widgetItem->widget()->getContentsMargins(&left, &top, &right, &bottom); return widgetItem->sizeHint() + QSize(left+right, top+bottom); } if (subinfo != 0) return subinfo->sizeHint(); return QSize(-1, -1); } QDockAreaLayoutItem &QDockAreaLayoutItem::operator = (const QDockAreaLayoutItem &other) { widgetItem = other.widgetItem; if (other.subinfo == 0) subinfo = 0; else subinfo = new QDockAreaLayoutInfo(*other.subinfo); delete placeHolderItem; if (other.placeHolderItem == 0) placeHolderItem = 0; else placeHolderItem = new QPlaceHolderItem(*other.placeHolderItem); pos = other.pos; size = other.size; flags = other.flags; return *this; } /****************************************************************************** ** QDockAreaLayoutInfo */ #ifndef QT_NO_TABBAR static quintptr tabId(const QDockAreaLayoutItem &item) { if (item.widgetItem == 0) return 0; return reinterpret_cast(item.widgetItem->widget()); } #endif static const int zero = 0; QDockAreaLayoutInfo::QDockAreaLayoutInfo() : sep(&zero), dockPos(QInternal::LeftDock), o(Qt::Horizontal), mainWindow(0) #ifndef QT_NO_TABBAR , tabbed(false), tabBar(0), tabBarShape(QTabBar::RoundedSouth) #endif { } QDockAreaLayoutInfo::QDockAreaLayoutInfo(const int *_sep, QInternal::DockPosition _dockPos, Qt::Orientation _o, int tbshape, QMainWindow *window) : sep(_sep), dockPos(_dockPos), o(_o), mainWindow(window) #ifndef QT_NO_TABBAR , tabbed(false), tabBar(0), tabBarShape(static_cast(tbshape)) #endif { #ifdef QT_NO_TABBAR Q_UNUSED(tbshape); #endif } QSize QDockAreaLayoutInfo::size() const { return isEmpty() ? QSize(0, 0) : rect.size(); } void QDockAreaLayoutInfo::clear() { item_list.clear(); rect = QRect(); #ifndef QT_NO_TABBAR tabbed = false; tabBar = 0; #endif } bool QDockAreaLayoutInfo::isEmpty() const { return next(-1) == -1; } QSize QDockAreaLayoutInfo::minimumSize() const { if (isEmpty()) return QSize(0, 0); int a = 0, b = 0; bool first = true; for (int i = 0; i < item_list.size(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); if (item.skip()) continue; QSize min_size = item.minimumSize(); #ifndef QT_NO_TABBAR if (tabbed) { a = qMax(a, pick(o, min_size)); } else #endif { if (!first) a += *sep; a += pick(o, min_size); } b = qMax(b, perp(o, min_size)); first = false; } QSize result; rpick(o, result) = a; rperp(o, result) = b; #ifndef QT_NO_TABBAR QSize tbm = tabBarMinimumSize(); if (!tbm.isNull()) { switch (tabBarShape) { case QTabBar::RoundedNorth: case QTabBar::RoundedSouth: case QTabBar::TriangularNorth: case QTabBar::TriangularSouth: result.rheight() += tbm.height(); result.rwidth() = qMax(tbm.width(), result.width()); break; case QTabBar::RoundedEast: case QTabBar::RoundedWest: case QTabBar::TriangularEast: case QTabBar::TriangularWest: result.rheight() = qMax(tbm.height(), result.height()); result.rwidth() += tbm.width(); break; default: break; } } #endif // QT_NO_TABBAR return result; } QSize QDockAreaLayoutInfo::maximumSize() const { if (isEmpty()) return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); int a = 0, b = QWIDGETSIZE_MAX; #ifndef QT_NO_TABBAR if (tabbed) a = QWIDGETSIZE_MAX; #endif int min_perp = 0; bool first = true; for (int i = 0; i < item_list.size(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); if (item.skip()) continue; QSize max_size = item.maximumSize(); min_perp = qMax(min_perp, perp(o, item.minimumSize())); #ifndef QT_NO_TABBAR if (tabbed) { a = qMin(a, pick(o, max_size)); } else #endif { if (!first) a += *sep; a += pick(o, max_size); } b = qMin(b, perp(o, max_size)); a = qMin(a, int(QWIDGETSIZE_MAX)); b = qMin(b, int(QWIDGETSIZE_MAX)); first = false; } b = qMax(b, min_perp); QSize result; rpick(o, result) = a; rperp(o, result) = b; #ifndef QT_NO_TABBAR QSize tbh = tabBarSizeHint(); if (!tbh.isNull()) { switch (tabBarShape) { case QTabBar::RoundedNorth: case QTabBar::RoundedSouth: result.rheight() += tbh.height(); break; case QTabBar::RoundedEast: case QTabBar::RoundedWest: result.rwidth() += tbh.width(); break; default: break; } } #endif // QT_NO_TABBAR return result; } QSize QDockAreaLayoutInfo::sizeHint() const { if (isEmpty()) return QSize(0, 0); int a = 0, b = 0; int min_perp = 0; int max_perp = QWIDGETSIZE_MAX; const QDockAreaLayoutItem *previous = 0; for (int i = 0; i < item_list.size(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); if (item.skip()) continue; bool gap = item.flags & QDockAreaLayoutItem::GapItem; QSize size_hint = item.sizeHint(); min_perp = qMax(min_perp, perp(o, item.minimumSize())); max_perp = qMin(max_perp, perp(o, item.maximumSize())); #ifndef QT_NO_TABBAR if (tabbed) { a = qMax(a, gap ? item.size : pick(o, size_hint)); } else #endif { if (previous && !gap && !(previous->flags & QDockAreaLayoutItem::GapItem) && !previous->hasFixedSize(o)) { a += *sep; } a += gap ? item.size : pick(o, size_hint); } b = qMax(b, perp(o, size_hint)); previous = &item; } max_perp = qMax(max_perp, min_perp); b = qMax(b, min_perp); b = qMin(b, max_perp); QSize result; rpick(o, result) = a; rperp(o, result) = b; #ifndef QT_NO_TABBAR if (tabbed) { QSize tbh = tabBarSizeHint(); switch (tabBarShape) { case QTabBar::RoundedNorth: case QTabBar::RoundedSouth: case QTabBar::TriangularNorth: case QTabBar::TriangularSouth: result.rheight() += tbh.height(); result.rwidth() = qMax(tbh.width(), result.width()); break; case QTabBar::RoundedEast: case QTabBar::RoundedWest: case QTabBar::TriangularEast: case QTabBar::TriangularWest: result.rheight() = qMax(tbh.height(), result.height()); result.rwidth() += tbh.width(); break; default: break; } } #endif // QT_NO_TABBAR return result; } bool QDockAreaLayoutInfo::expansive(Qt::Orientation o) const { for (int i = 0; i < item_list.size(); ++i) { if (item_list.at(i).expansive(o)) return true; } return false; } /* QDockAreaLayoutInfo::maximumSize() doesn't return the real max size. For example, if the layout is empty, it returns QWIDGETSIZE_MAX. This is so that empty dock areas don't constrain the size of the QMainWindow, but sometimes we really need to know the maximum size. Also, these functions take into account widgets that want to keep their size (f.ex. when they are hidden and then shown, they should not change size). */ static int realMinSize(const QDockAreaLayoutInfo &info) { int result = 0; bool first = true; for (int i = 0; i < info.item_list.size(); ++i) { const QDockAreaLayoutItem &item = info.item_list.at(i); if (item.skip()) continue; int min = 0; if ((item.flags & QDockAreaLayoutItem::KeepSize) && item.size != -1) min = item.size; else min = pick(info.o, item.minimumSize()); if (!first) result += *info.sep; result += min; first = false; } return result; } static int realMaxSize(const QDockAreaLayoutInfo &info) { int result = 0; bool first = true; for (int i = 0; i < info.item_list.size(); ++i) { const QDockAreaLayoutItem &item = info.item_list.at(i); if (item.skip()) continue; int max = 0; if ((item.flags & QDockAreaLayoutItem::KeepSize) && item.size != -1) max = item.size; else max = pick(info.o, item.maximumSize()); if (!first) result += *info.sep; result += max; if (result >= QWIDGETSIZE_MAX) return QWIDGETSIZE_MAX; first = false; } return result; } void QDockAreaLayoutInfo::fitItems() { #ifndef QT_NO_TABBAR if (tabbed) { return; } #endif QVector layout_struct_list(item_list.size()*2); int j = 0; int size = pick(o, rect.size()); int min_size = realMinSize(*this); int max_size = realMaxSize(*this); int last_index = -1; const QDockAreaLayoutItem *previous = 0; for (int i = 0; i < item_list.size(); ++i) { QDockAreaLayoutItem &item = item_list[i]; if (item.skip()) continue; bool gap = item.flags & QDockAreaLayoutItem::GapItem; if (previous && !gap) { if (!(previous->flags & QDockAreaLayoutItem::GapItem)) { QLayoutStruct &ls = layout_struct_list[j++]; ls.init(); ls.minimumSize = ls.maximumSize = ls.sizeHint = previous->hasFixedSize(o) ? 0 : *sep; ls.empty = false; } } if (item.flags & QDockAreaLayoutItem::KeepSize) { // Check if the item can keep its size, without violating size constraints // of other items. if (size < min_size) { // There is too little space to keep this widget's size item.flags &= ~QDockAreaLayoutItem::KeepSize; min_size -= item.size; min_size += pick(o, item.minimumSize()); min_size = qMax(0, min_size); } else if (size > max_size) { // There is too much space to keep this widget's size item.flags &= ~QDockAreaLayoutItem::KeepSize; max_size -= item.size; max_size += pick(o, item.maximumSize()); max_size = qMin(QWIDGETSIZE_MAX, max_size); } } last_index = j; QLayoutStruct &ls = layout_struct_list[j++]; ls.init(); ls.empty = false; if (item.flags & QDockAreaLayoutItem::KeepSize) { ls.minimumSize = ls.maximumSize = ls.sizeHint = item.size; ls.expansive = false; ls.stretch = 0; } else { ls.maximumSize = pick(o, item.maximumSize()); ls.expansive = item.expansive(o); ls.minimumSize = pick(o, item.minimumSize()); ls.sizeHint = item.size == -1 ? pick(o, item.sizeHint()) : item.size; ls.stretch = ls.expansive ? ls.sizeHint : 0; } item.flags &= ~QDockAreaLayoutItem::KeepSize; previous = &item; } layout_struct_list.resize(j); // If there is more space than the widgets can take (due to maximum size constraints), // we detect it here and stretch the last widget to take up the rest of the space. if (size > max_size && last_index != -1) { layout_struct_list[last_index].maximumSize = QWIDGETSIZE_MAX; layout_struct_list[last_index].expansive = true; } qGeomCalc(layout_struct_list, 0, j, pick(o, rect.topLeft()), size, 0); j = 0; bool prev_gap = false; bool first = true; for (int i = 0; i < item_list.size(); ++i) { QDockAreaLayoutItem &item = item_list[i]; if (item.skip()) continue; bool gap = item.flags & QDockAreaLayoutItem::GapItem; if (!first && !gap && !prev_gap) ++j; const QLayoutStruct &ls = layout_struct_list.at(j++); item.size = ls.size; item.pos = ls.pos; if (item.subinfo != 0) { item.subinfo->rect = itemRect(i); item.subinfo->fitItems(); } prev_gap = gap; first = false; } } static QInternal::DockPosition dockPosHelper(const QRect &rect, const QPoint &_pos, Qt::Orientation o, bool nestingEnabled, QDockAreaLayoutInfo::TabMode tabMode) { if (tabMode == QDockAreaLayoutInfo::ForceTabs) return QInternal::DockCount; QPoint pos = _pos - rect.topLeft(); int x = pos.x(); int y = pos.y(); int w = rect.width(); int h = rect.height(); if (tabMode != QDockAreaLayoutInfo::NoTabs) { // is it in the center? if (nestingEnabled) { /* 2/3 +--------------+ | | | CCCCCCCC | 2/3 | CCCCCCCC | | CCCCCCCC | | | +--------------+ */ QRect center(w/6, h/6, 2*w/3, 2*h/3); if (center.contains(pos)) return QInternal::DockCount; } else if (o == Qt::Horizontal) { /* 2/3 +--------------+ | CCCCCCCC | | CCCCCCCC | | CCCCCCCC | | CCCCCCCC | | CCCCCCCC | +--------------+ */ if (x > w/6 && x < w*5/6) return QInternal::DockCount; } else { /* +--------------+ | | 2/3 |CCCCCCCCCCCCCC| |CCCCCCCCCCCCCC| | | +--------------+ */ if (y > h/6 && y < 5*h/6) return QInternal::DockCount; } } // not in the center. which edge? if (nestingEnabled) { if (o == Qt::Horizontal) { /* 1/3 1/3 1/3 +------------+ (we've already ruled out the center) |LLLLTTTTRRRR| |LLLLTTTTRRRR| |LLLLBBBBRRRR| |LLLLBBBBRRRR| +------------+ */ if (x < w/3) return QInternal::LeftDock; if (x > 2*w/3) return QInternal::RightDock; if (y < h/2) return QInternal::TopDock; return QInternal::BottomDock; } else { /* +------------+ (we've already ruled out the center) 1/3 |TTTTTTTTTTTT| |LLLLLLRRRRRR| 1/3 |LLLLLLRRRRRR| 1/3 |BBBBBBBBBBBB| +------------+ */ if (y < h/3) return QInternal::TopDock; if (y > 2*h/3) return QInternal::BottomDock; if (x < w/2) return QInternal::LeftDock; return QInternal::RightDock; } } else { if (o == Qt::Horizontal) { return x < w/2 ? QInternal::LeftDock : QInternal::RightDock; } else { return y < h/2 ? QInternal::TopDock : QInternal::BottomDock; } } } QList QDockAreaLayoutInfo::gapIndex(const QPoint& _pos, bool nestingEnabled, TabMode tabMode) const { QList result; QRect item_rect; int item_index = 0; #ifndef QT_NO_TABBAR if (tabbed) { item_rect = tabContentRect(); } else #endif { int pos = pick(o, _pos); int last = -1; for (int i = 0; i < item_list.size(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); if (item.skip()) continue; last = i; if (item.pos + item.size < pos) continue; if (item.subinfo != 0 #ifndef QT_NO_TABBAR && !item.subinfo->tabbed #endif ) { result = item.subinfo->gapIndex(_pos, nestingEnabled, tabMode); result.prepend(i); return result; } item_rect = itemRect(i); item_index = i; break; } if (item_rect.isNull()) { result.append(last + 1); return result; } } Q_ASSERT(!item_rect.isNull()); QInternal::DockPosition dock_pos = dockPosHelper(item_rect, _pos, o, nestingEnabled, tabMode); switch (dock_pos) { case QInternal::LeftDock: if (o == Qt::Horizontal) result << item_index; else result << item_index << 0; // this subinfo doesn't exist yet, but insertGap() // handles this by inserting it break; case QInternal::RightDock: if (o == Qt::Horizontal) result << item_index + 1; else result << item_index << 1; break; case QInternal::TopDock: if (o == Qt::Horizontal) result << item_index << 0; else result << item_index; break; case QInternal::BottomDock: if (o == Qt::Horizontal) result << item_index << 1; else result << item_index + 1; break; case QInternal::DockCount: result << (-item_index - 1) << 0; // negative item_index means "on top of" // -item_index - 1, insertGap() // will insert a tabbed subinfo break; default: break; } return result; } static inline int shrink(QLayoutStruct &ls, int delta) { if (ls.empty) return 0; int old_size = ls.size; ls.size = qMax(ls.size - delta, ls.minimumSize); return old_size - ls.size; } static inline int grow(QLayoutStruct &ls, int delta) { if (ls.empty) return 0; int old_size = ls.size; ls.size = qMin(ls.size + delta, ls.maximumSize); return ls.size - old_size; } static int separatorMoveHelper(QVector &list, int index, int delta, int sep) { // adjust sizes int pos = -1; for (int i = 0; i < list.size(); ++i) { const QLayoutStruct &ls = list.at(i); if (!ls.empty) { pos = ls.pos; break; } } if (pos == -1) return 0; if (delta > 0) { int growlimit = 0; for (int i = 0; i<=index; ++i) { const QLayoutStruct &ls = list.at(i); if (ls.empty) continue; if (ls.maximumSize == QLAYOUTSIZE_MAX) { growlimit = QLAYOUTSIZE_MAX; break; } growlimit += ls.maximumSize - ls.size; } if (delta > growlimit) delta = growlimit; int d = 0; for (int i = index + 1; d < delta && i < list.count(); ++i) d += shrink(list[i], delta - d); delta = d; d = 0; for (int i = index; d < delta && i >= 0; --i) d += grow(list[i], delta - d); } else if (delta < 0) { int growlimit = 0; for (int i = index + 1; i < list.count(); ++i) { const QLayoutStruct &ls = list.at(i); if (ls.empty) continue; if (ls.maximumSize == QLAYOUTSIZE_MAX) { growlimit = QLAYOUTSIZE_MAX; break; } growlimit += ls.maximumSize - ls.size; } if (-delta > growlimit) delta = -growlimit; int d = 0; for (int i = index; d < -delta && i >= 0; --i) d += shrink(list[i], -delta - d); delta = -d; d = 0; for (int i = index + 1; d < -delta && i < list.count(); ++i) d += grow(list[i], -delta - d); } // adjust positions bool first = true; for (int i = 0; i < list.size(); ++i) { QLayoutStruct &ls = list[i]; if (ls.empty) { ls.pos = pos + (first ? 0 : sep); continue; } if (!first) pos += sep; ls.pos = pos; pos += ls.size; first = false; } return delta; } int QDockAreaLayoutInfo::separatorMove(int index, int delta) { #ifndef QT_NO_TABBAR Q_ASSERT(!tabbed); #endif QVector list(item_list.size()); for (int i = 0; i < list.size(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); QLayoutStruct &ls = list[i]; Q_ASSERT(!(item.flags & QDockAreaLayoutItem::GapItem)); if (item.skip()) { ls.empty = true; } else { const int separatorSpace = item.hasFixedSize(o) ? 0 : *sep; ls.empty = false; ls.pos = item.pos; ls.size = item.size + separatorSpace; ls.minimumSize = pick(o, item.minimumSize()) + separatorSpace; ls.maximumSize = pick(o, item.maximumSize()) + separatorSpace; } } //the separator space has been added to the size, so we pass 0 as a parameter delta = separatorMoveHelper(list, index, delta, 0 /*separator*/); for (int i = 0; i < list.size(); ++i) { QDockAreaLayoutItem &item = item_list[i]; if (item.skip()) continue; QLayoutStruct &ls = list[i]; const int separatorSpace = item.hasFixedSize(o) ? 0 : *sep; item.size = ls.size - separatorSpace; item.pos = ls.pos; if (item.subinfo != 0) { item.subinfo->rect = itemRect(i); item.subinfo->fitItems(); } } return delta; } void QDockAreaLayoutInfo::unnest(int index) { QDockAreaLayoutItem &item = item_list[index]; if (item.subinfo == 0) return; if (item.subinfo->item_list.count() > 1) return; if (item.subinfo->item_list.count() == 0) { item_list.removeAt(index); } else if (item.subinfo->item_list.count() == 1) { QDockAreaLayoutItem &child = item.subinfo->item_list.first(); if (child.widgetItem != 0) { item.widgetItem = child.widgetItem; delete item.subinfo; item.subinfo = 0; } else if (child.subinfo != 0) { QDockAreaLayoutInfo *tmp = item.subinfo; item.subinfo = child.subinfo; child.subinfo = 0; tmp->item_list.clear(); delete tmp; } } } void QDockAreaLayoutInfo::remove(const QList &path) { Q_ASSERT(!path.isEmpty()); if (path.count() > 1) { const int index = path.first(); QDockAreaLayoutItem &item = item_list[index]; Q_ASSERT(item.subinfo != 0); item.subinfo->remove(path.mid(1)); unnest(index); } else { int index = path.first(); item_list.removeAt(index); } } QLayoutItem *QDockAreaLayoutInfo::plug(const QList &path) { Q_ASSERT(!path.isEmpty()); int index = path.first(); if (index < 0) index = -index - 1; if (path.count() > 1) { const QDockAreaLayoutItem &item = item_list.at(index); Q_ASSERT(item.subinfo != 0); return item.subinfo->plug(path.mid(1)); } QDockAreaLayoutItem &item = item_list[index]; Q_ASSERT(item.widgetItem != 0); Q_ASSERT(item.flags & QDockAreaLayoutItem::GapItem); item.flags &= ~QDockAreaLayoutItem::GapItem; QRect result; #ifndef QT_NO_TABBAR if (tabbed) { } else #endif { int prev = this->prev(index); int next = this->next(index); if (prev != -1 && !(item_list.at(prev).flags & QDockAreaLayoutItem::GapItem)) { item.pos += *sep; item.size -= *sep; } if (next != -1 && !(item_list.at(next).flags & QDockAreaLayoutItem::GapItem)) item.size -= *sep; QPoint pos; rpick(o, pos) = item.pos; rperp(o, pos) = perp(o, rect.topLeft()); QSize s; rpick(o, s) = item.size; rperp(o, s) = perp(o, rect.size()); result = QRect(pos, s); } return item.widgetItem; } QLayoutItem *QDockAreaLayoutInfo::unplug(const QList &path) { Q_ASSERT(!path.isEmpty()); const int index = path.first(); if (path.count() > 1) { const QDockAreaLayoutItem &item = item_list.at(index); Q_ASSERT(item.subinfo != 0); return item.subinfo->unplug(path.mid(1)); } QDockAreaLayoutItem &item = item_list[index]; int prev = this->prev(index); int next = this->next(index); Q_ASSERT(!(item.flags & QDockAreaLayoutItem::GapItem)); item.flags |= QDockAreaLayoutItem::GapItem; #ifndef QT_NO_TABBAR if (tabbed) { } else #endif { if (prev != -1 && !(item_list.at(prev).flags & QDockAreaLayoutItem::GapItem)) { item.pos -= *sep; item.size += *sep; } if (next != -1 && !(item_list.at(next).flags & QDockAreaLayoutItem::GapItem)) item.size += *sep; } return item.widgetItem; } #ifndef QT_NO_TABBAR quintptr QDockAreaLayoutInfo::currentTabId() const { if (!tabbed || tabBar == 0) return 0; int index = tabBar->currentIndex(); if (index == -1) return 0; return qvariant_cast(tabBar->tabData(index)); } void QDockAreaLayoutInfo::setCurrentTab(QWidget *widget) { setCurrentTabId(reinterpret_cast(widget)); } void QDockAreaLayoutInfo::setCurrentTabId(quintptr id) { if (!tabbed || tabBar == 0) return; for (int i = 0; i < tabBar->count(); ++i) { if (qvariant_cast(tabBar->tabData(i)) == id) { tabBar->setCurrentIndex(i); return; } } } #endif // QT_NO_TABBAR static QRect dockedGeometry(QWidget *widget) { int titleHeight = 0; QDockWidgetLayout *layout = qobject_cast(widget->layout()); if(layout != 0 && layout->nativeWindowDeco()) titleHeight = layout->titleHeight(); QRect result = widget->geometry(); result.adjust(0, -titleHeight, 0, 0); return result; } bool QDockAreaLayoutInfo::insertGap(const QList &path, QLayoutItem *dockWidgetItem) { Q_ASSERT(!path.isEmpty()); bool insert_tabbed = false; int index = path.first(); if (index < 0) { insert_tabbed = true; index = -index - 1; } // dump(qDebug() << "insertGap() before:" << index << tabIndex, *this, QString()); if (path.count() > 1) { QDockAreaLayoutItem &item = item_list[index]; if (item.subinfo == 0 #ifndef QT_NO_TABBAR || (item.subinfo->tabbed && !insert_tabbed) #endif ) { // this is not yet a nested layout - make it QDockAreaLayoutInfo *subinfo = item.subinfo; QLayoutItem *widgetItem = item.widgetItem; QPlaceHolderItem *placeHolderItem = item.placeHolderItem; QRect r = subinfo == 0 ? widgetItem ? dockedGeometry(widgetItem->widget()) : placeHolderItem->topLevelRect : subinfo->rect; Qt::Orientation opposite = o == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal; #ifdef QT_NO_TABBAR const int tabBarShape = 0; #endif QDockAreaLayoutInfo *new_info = new QDockAreaLayoutInfo(sep, dockPos, opposite, tabBarShape, mainWindow); //item become a new top-level item.subinfo = new_info; item.widgetItem = 0; item.placeHolderItem = 0; QDockAreaLayoutItem new_item = widgetItem == 0 ? QDockAreaLayoutItem(subinfo) : widgetItem ? QDockAreaLayoutItem(widgetItem) : QDockAreaLayoutItem(placeHolderItem); new_item.size = pick(opposite, r.size()); new_item.pos = pick(opposite, r.topLeft()); new_info->item_list.append(new_item); #ifndef QT_NO_TABBAR if (insert_tabbed) { new_info->tabbed = true; } #endif } return item.subinfo->insertGap(path.mid(1), dockWidgetItem); } // create the gap item QDockAreaLayoutItem gap_item; gap_item.flags |= QDockAreaLayoutItem::GapItem; gap_item.widgetItem = dockWidgetItem; // so minimumSize(), maximumSize() and // sizeHint() will work #ifndef QT_NO_TABBAR if (!tabbed) #endif { int prev = this->prev(index); int next = this->next(index - 1); // find out how much space we have in the layout int space = 0; if (isEmpty()) { // I am an empty dock area, therefore I am a top-level dock area. switch (dockPos) { case QInternal::LeftDock: case QInternal::RightDock: if (o == Qt::Vertical) { // the "size" is the height of the dock area (remember we are empty) space = pick(Qt::Vertical, rect.size()); } else { space = pick(Qt::Horizontal, dockWidgetItem->widget()->size()); } break; case QInternal::TopDock: case QInternal::BottomDock: default: if (o == Qt::Horizontal) { // the "size" is width of the dock area space = pick(Qt::Horizontal, rect.size()); } else { space = pick(Qt::Vertical, dockWidgetItem->widget()->size()); } break; } } else { for (int i = 0; i < item_list.count(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); if (item.skip()) continue; Q_ASSERT(!(item.flags & QDockAreaLayoutItem::GapItem)); space += item.size - pick(o, item.minimumSize()); } } // find the actual size of the gap int gap_size = 0; int sep_size = 0; if (isEmpty()) { gap_size = space; sep_size = 0; } else { QRect r = dockedGeometry(dockWidgetItem->widget()); gap_size = pick(o, r.size()); if (prev != -1 && !(item_list.at(prev).flags & QDockAreaLayoutItem::GapItem)) sep_size += *sep; if (next != -1 && !(item_list.at(next).flags & QDockAreaLayoutItem::GapItem)) sep_size += *sep; } if (gap_size + sep_size > space) gap_size = pick(o, gap_item.minimumSize()); gap_item.size = gap_size + sep_size; } // finally, insert the gap item_list.insert(index, gap_item); // dump(qDebug() << "insertGap() after:" << index << tabIndex, *this, QString()); return true; } QDockAreaLayoutInfo *QDockAreaLayoutInfo::info(QWidget *widget) { for (int i = 0; i < item_list.count(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); if (item.skip()) continue; #ifndef QT_NO_TABBAR if (tabbed && widget == tabBar) return this; #endif if (item.widgetItem != 0 && item.widgetItem->widget() == widget) return this; if (item.subinfo != 0) { if (QDockAreaLayoutInfo *result = item.subinfo->info(widget)) return result; } } return 0; } QDockAreaLayoutInfo *QDockAreaLayoutInfo::info(const QList &path) { int index = path.first(); if (index < 0) index = -index - 1; if (index >= item_list.count()) return this; if (path.count() == 1 || item_list[index].subinfo == 0) return this; return item_list[index].subinfo->info(path.mid(1)); } QRect QDockAreaLayoutInfo::itemRect(int index) const { const QDockAreaLayoutItem &item = item_list.at(index); if (item.skip()) return QRect(); QRect result; #ifndef QT_NO_TABBAR if (tabbed) { if (tabId(item) == currentTabId()) result = tabContentRect(); } else #endif { QPoint pos; rpick(o, pos) = item.pos; rperp(o, pos) = perp(o, rect.topLeft()); QSize s; rpick(o, s) = item.size; rperp(o, s) = perp(o, rect.size()); result = QRect(pos, s); } return result; } QRect QDockAreaLayoutInfo::itemRect(const QList &path) const { Q_ASSERT(!path.isEmpty()); const int index = path.first(); if (path.count() > 1) { const QDockAreaLayoutItem &item = item_list.at(index); Q_ASSERT(item.subinfo != 0); return item.subinfo->itemRect(path.mid(1)); } return itemRect(index); } QRect QDockAreaLayoutInfo::separatorRect(int index) const { #ifndef QT_NO_TABBAR if (tabbed) return QRect(); #endif const QDockAreaLayoutItem &item = item_list.at(index); if (item.skip()) return QRect(); QPoint pos = rect.topLeft(); rpick(o, pos) = item.pos + item.size; QSize s = rect.size(); rpick(o, s) = *sep; return QRect(pos, s); } QRect QDockAreaLayoutInfo::separatorRect(const QList &path) const { Q_ASSERT(!path.isEmpty()); const int index = path.first(); if (path.count() > 1) { const QDockAreaLayoutItem &item = item_list.at(index); Q_ASSERT(item.subinfo != 0); return item.subinfo->separatorRect(path.mid(1)); } return separatorRect(index); } QList QDockAreaLayoutInfo::findSeparator(const QPoint &_pos) const { #ifndef QT_NO_TABBAR if (tabbed) return QList(); #endif int pos = pick(o, _pos); for (int i = 0; i < item_list.size(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); if (item.skip() || (item.flags & QDockAreaLayoutItem::GapItem)) continue; if (item.pos + item.size > pos) { if (item.subinfo != 0) { QList result = item.subinfo->findSeparator(_pos); if (!result.isEmpty()) { result.prepend(i); return result; } else { return QList(); } } } int next = this->next(i); if (next == -1 || (item_list.at(next).flags & QDockAreaLayoutItem::GapItem)) continue; QRect sepRect = separatorRect(i); if (!sepRect.isNull() && *sep == 1) sepRect.adjust(-2, -2, 2, 2); //we also make sure we don't find a separator that's not there if (sepRect.contains(_pos) && !item.hasFixedSize(o)) { return QList() << i; } } return QList(); } QList QDockAreaLayoutInfo::indexOfPlaceHolder(const QString &objectName) const { for (int i = 0; i < item_list.size(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); if (item.subinfo != 0) { QList result = item.subinfo->indexOfPlaceHolder(objectName); if (!result.isEmpty()) { result.prepend(i); return result; } continue; } if (item.placeHolderItem != 0 && item.placeHolderItem->objectName == objectName) { QList result; result << i; return result; } } return QList(); } QList QDockAreaLayoutInfo::indexOf(QWidget *widget) const { for (int i = 0; i < item_list.size(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); if (item.placeHolderItem != 0) continue; if (item.subinfo != 0) { QList result = item.subinfo->indexOf(widget); if (!result.isEmpty()) { result.prepend(i); return result; } continue; } if (!(item.flags & QDockAreaLayoutItem::GapItem) && item.widgetItem->widget() == widget) { QList result; result << i; return result; } } return QList(); } QMainWindowLayout *QDockAreaLayoutInfo::mainWindowLayout() const { QMainWindowLayout *result = qt_mainwindow_layout(mainWindow); Q_ASSERT(result != 0); return result; } bool QDockAreaLayoutInfo::hasFixedSize() const { return perp(o, minimumSize()) == perp(o, maximumSize()); } void QDockAreaLayoutInfo::apply(bool animate) { QWidgetAnimator &widgetAnimator = mainWindowLayout()->widgetAnimator; #ifndef QT_NO_TABBAR if (tabbed) { QRect tab_rect; QSize tbh = tabBarSizeHint(); if (!tbh.isNull()) { switch (tabBarShape) { case QTabBar::RoundedNorth: case QTabBar::TriangularNorth: tab_rect = QRect(rect.left(), rect.top(), rect.width(), tbh.height()); break; case QTabBar::RoundedSouth: case QTabBar::TriangularSouth: tab_rect = QRect(rect.left(), rect.bottom() - tbh.height() + 1, rect.width(), tbh.height()); break; case QTabBar::RoundedEast: case QTabBar::TriangularEast: tab_rect = QRect(rect.right() - tbh.width() + 1, rect.top(), tbh.width(), rect.height()); break; case QTabBar::RoundedWest: case QTabBar::TriangularWest: tab_rect = QRect(rect.left(), rect.top(), tbh.width(), rect.height()); break; default: break; } } widgetAnimator.animate(tabBar, tab_rect, animate); } #endif // QT_NO_TABBAR for (int i = 0; i < item_list.size(); ++i) { QDockAreaLayoutItem &item = item_list[i]; if (item.flags & QDockAreaLayoutItem::GapItem) continue; if (item.subinfo != 0) { item.subinfo->apply(animate); continue; } if (item.skip()) continue; Q_ASSERT(item.widgetItem); QRect r = itemRect(i); QWidget *w = item.widgetItem->widget(); QRect geo = w->geometry(); widgetAnimator.animate(w, r, animate); if (!w->isHidden() && w->window()->isVisible()) { QDockWidget *dw = qobject_cast(w); if (!r.isValid() && geo.right() >= 0 && geo.bottom() >= 0) { dw->lower(); emit dw->visibilityChanged(false); } else if (r.isValid() && (geo.right() < 0 || geo.bottom() < 0)) { emit dw->visibilityChanged(true); } } } #ifndef QT_NO_TABBAR if (*sep == 1) updateSeparatorWidgets(); #endif //QT_NO_TABBAR } static void paintSep(QPainter *p, QWidget *w, const QRect &r, Qt::Orientation o, bool mouse_over) { QStyleOption opt(0); opt.state = QStyle::State_None; if (w->isEnabled()) opt.state |= QStyle::State_Enabled; if (o != Qt::Horizontal) opt.state |= QStyle::State_Horizontal; if (mouse_over) opt.state |= QStyle::State_MouseOver; opt.rect = r; opt.palette = w->palette(); w->style()->drawPrimitive(QStyle::PE_IndicatorDockWidgetResizeHandle, &opt, p, w); } QRegion QDockAreaLayoutInfo::separatorRegion() const { QRegion result; if (isEmpty()) return result; #ifndef QT_NO_TABBAR if (tabbed) return result; #endif for (int i = 0; i < item_list.count(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); if (item.skip()) continue; int next = this->next(i); if (item.subinfo) result |= item.subinfo->separatorRegion(); if (next == -1) break; result |= separatorRect(i); } return result; } void QDockAreaLayoutInfo::paintSeparators(QPainter *p, QWidget *widget, const QRegion &clip, const QPoint &mouse) const { if (isEmpty()) return; #ifndef QT_NO_TABBAR if (tabbed) return; #endif for (int i = 0; i < item_list.count(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); if (item.skip()) continue; int next = this->next(i); if ((item.flags & QDockAreaLayoutItem::GapItem) || (next != -1 && (item_list.at(next).flags & QDockAreaLayoutItem::GapItem))) continue; if (item.subinfo) { if (clip.contains(item.subinfo->rect)) item.subinfo->paintSeparators(p, widget, clip, mouse); } if (next == -1) break; QRect r = separatorRect(i); if (clip.contains(r) && !item.hasFixedSize(o)) paintSep(p, widget, r, o, r.contains(mouse)); } } int QDockAreaLayoutInfo::next(int index) const { for (int i = index + 1; i < item_list.size(); ++i) { if (!item_list.at(i).skip()) return i; } return -1; } int QDockAreaLayoutInfo::prev(int index) const { for (int i = index - 1; i >= 0; --i) { if (!item_list.at(i).skip()) return i; } return -1; } void QDockAreaLayoutInfo::tab(int index, QLayoutItem *dockWidgetItem) { #ifdef QT_NO_TABBAR Q_UNUSED(index); Q_UNUSED(dockWidgetItem); #else if (tabbed) { item_list.append(QDockAreaLayoutItem(dockWidgetItem)); updateTabBar(); setCurrentTab(dockWidgetItem->widget()); } else { QDockAreaLayoutInfo *new_info = new QDockAreaLayoutInfo(sep, dockPos, o, tabBarShape, mainWindow); item_list[index].subinfo = new_info; new_info->item_list.append(item_list.at(index).widgetItem); item_list[index].widgetItem = 0; new_info->item_list.append(dockWidgetItem); new_info->tabbed = true; new_info->updateTabBar(); new_info->setCurrentTab(dockWidgetItem->widget()); } #endif // QT_NO_TABBAR } void QDockAreaLayoutInfo::split(int index, Qt::Orientation orientation, QLayoutItem *dockWidgetItem) { if (orientation == o) { item_list.insert(index + 1, QDockAreaLayoutItem(dockWidgetItem)); } else { #ifdef QT_NO_TABBAR const int tabBarShape = 0; #endif QDockAreaLayoutInfo *new_info = new QDockAreaLayoutInfo(sep, dockPos, orientation, tabBarShape, mainWindow); item_list[index].subinfo = new_info; new_info->item_list.append(item_list.at(index).widgetItem); item_list[index].widgetItem = 0; new_info->item_list.append(dockWidgetItem); } } QDockAreaLayoutItem &QDockAreaLayoutInfo::item(const QList &path) { Q_ASSERT(!path.isEmpty()); const int index = path.first(); if (path.count() > 1) { const QDockAreaLayoutItem &item = item_list[index]; Q_ASSERT(item.subinfo != 0); return item.subinfo->item(path.mid(1)); } return item_list[index]; } QLayoutItem *QDockAreaLayoutInfo::itemAt(int *x, int index) const { for (int i = 0; i < item_list.count(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); if (item.placeHolderItem != 0) continue; if (item.subinfo) { if (QLayoutItem *ret = item.subinfo->itemAt(x, index)) return ret; } else if (item.widgetItem) { if ((*x)++ == index) return item.widgetItem; } } return 0; } QLayoutItem *QDockAreaLayoutInfo::takeAt(int *x, int index) { for (int i = 0; i < item_list.count(); ++i) { QDockAreaLayoutItem &item = item_list[i]; if (item.placeHolderItem != 0) continue; else if (item.subinfo) { if (QLayoutItem *ret = item.subinfo->takeAt(x, index)) { unnest(i); return ret; } } else if (item.widgetItem) { if ((*x)++ == index) { item.placeHolderItem = new QPlaceHolderItem(item.widgetItem->widget()); QLayoutItem *ret = item.widgetItem; item.widgetItem = 0; if (item.size != -1) item.flags |= QDockAreaLayoutItem::KeepSize; return ret; } } } return 0; } void QDockAreaLayoutInfo::deleteAllLayoutItems() { for (int i = 0; i < item_list.count(); ++i) { QDockAreaLayoutItem &item= item_list[i]; if (item.subinfo) { item.subinfo->deleteAllLayoutItems(); } else { delete item.widgetItem; item.widgetItem = 0; } } } void QDockAreaLayoutInfo::saveState(QDataStream &stream) const { #ifndef QT_NO_TABBAR if (tabbed) { stream << (uchar) TabMarker; // write the index in item_list of the widget that's currently on top. quintptr id = currentTabId(); int index = -1; for (int i = 0; i < item_list.count(); ++i) { if (tabId(item_list.at(i)) == id) { index = i; break; } } stream << index; } else #endif // QT_NO_TABBAR { stream << (uchar) SequenceMarker; } stream << (uchar) o << item_list.count(); for (int i = 0; i < item_list.count(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); if (item.widgetItem != 0) { stream << (uchar) WidgetMarker; QWidget *w = item.widgetItem->widget(); QString name = w->objectName(); if (name.isEmpty()) { qWarning("QMainWindow::saveState(): 'objectName' not set for QDockWidget %p '%s;", w, qPrintable(w->windowTitle())); } stream << name; uchar flags = 0; if (!w->isHidden()) flags |= StateFlagVisible; if (w->isWindow()) flags |= StateFlagFloating; stream << flags; if (w->isWindow()) { stream << w->x() << w->y() << w->width() << w->height(); } else { stream << item.pos << item.size << pick(o, item.minimumSize()) << pick(o, item.maximumSize()); } } else if (item.placeHolderItem != 0) { stream << (uchar) WidgetMarker; stream << item.placeHolderItem->objectName; uchar flags = 0; if (!item.placeHolderItem->hidden) flags |= StateFlagVisible; if (item.placeHolderItem->window) flags |= StateFlagFloating; stream << flags; if (item.placeHolderItem->window) { QRect r = item.placeHolderItem->topLevelRect; stream << r.x() << r.y() << r.width() << r.height(); } else { stream << item.pos << item.size << (int)0 << (int)0; } } else if (item.subinfo != 0) { stream << (uchar) SequenceMarker << item.pos << item.size << pick(o, item.minimumSize()) << pick(o, item.maximumSize()); item.subinfo->saveState(stream); } } } static Qt::DockWidgetArea toDockWidgetArea(QInternal::DockPosition pos) { switch (pos) { case QInternal::LeftDock: return Qt::LeftDockWidgetArea; case QInternal::RightDock: return Qt::RightDockWidgetArea; case QInternal::TopDock: return Qt::TopDockWidgetArea; case QInternal::BottomDock: return Qt::BottomDockWidgetArea; default: break; } return Qt::NoDockWidgetArea; } static QRect constrainedRect(QRect rect, const QRect &desktop) { if (desktop.isValid()) { rect.setWidth(qMin(rect.width(), desktop.width())); rect.setHeight(qMin(rect.height(), desktop.height())); rect.moveLeft(qMax(rect.left(), desktop.left())); rect.moveTop(qMax(rect.top(), desktop.top())); rect.moveRight(qMin(rect.right(), desktop.right())); rect.moveBottom(qMin(rect.bottom(), desktop.bottom())); } return rect; } bool QDockAreaLayoutInfo::restoreState(QDataStream &stream, QList &widgets, bool testing) { uchar marker; stream >> marker; if (marker != TabMarker && marker != SequenceMarker) return false; #ifndef QT_NO_TABBAR tabbed = marker == TabMarker; int index = -1; if (tabbed) stream >> index; #endif uchar orientation; stream >> orientation; o = static_cast(orientation); int cnt; stream >> cnt; for (int i = 0; i < cnt; ++i) { uchar nextMarker; stream >> nextMarker; if (nextMarker == WidgetMarker) { QString name; uchar flags; stream >> name >> flags; if (name.isEmpty()) { int dummy; stream >> dummy >> dummy >> dummy >> dummy; continue; } QDockWidget *widget = 0; for (int j = 0; j < widgets.count(); ++j) { if (widgets.at(j)->objectName() == name) { widget = widgets.takeAt(j); break; } } if (widget == 0) { QPlaceHolderItem *placeHolder = new QPlaceHolderItem; QDockAreaLayoutItem item(placeHolder); placeHolder->objectName = name; placeHolder->window = flags & StateFlagFloating; placeHolder->hidden = !(flags & StateFlagVisible); if (placeHolder->window) { int x, y, w, h; stream >> x >> y >> w >> h; placeHolder->topLevelRect = QRect(x, y, w, h); } else { int dummy; stream >> item.pos >> item.size >> dummy >> dummy; } if (item.size != -1) item.flags |= QDockAreaLayoutItem::KeepSize; if (!testing) item_list.append(item); } else { QDockAreaLayoutItem item(new QDockWidgetItem(widget)); if (flags & StateFlagFloating) { bool drawer = false; #ifdef Q_WS_MAC // drawer support extern bool qt_mac_is_macdrawer(const QWidget *); //qwidget_mac.cpp extern bool qt_mac_set_drawer_preferred_edge(QWidget *, Qt::DockWidgetArea); //qwidget_mac.cpp drawer = qt_mac_is_macdrawer(widget); #endif if (!testing) { widget->hide(); if (!drawer) widget->setFloating(true); } int x, y, w, h; stream >> x >> y >> w >> h; #ifdef Q_WS_MAC // drawer support if (drawer) { mainWindow->window()->createWinId(); widget->window()->createWinId(); qt_mac_set_drawer_preferred_edge(widget, toDockWidgetArea(dockPos)); } else #endif if (!testing) { QRect r(x, y, w, h); QDesktopWidget *desktop = QApplication::desktop(); if (desktop->isVirtualDesktop()) r = constrainedRect(r, desktop->screenGeometry(desktop->screenNumber(r.topLeft()))); else r = constrainedRect(r, desktop->screenGeometry(widget)); widget->move(r.topLeft()); widget->resize(r.size()); } if (!testing) { widget->setVisible(flags & StateFlagVisible); item_list.append(item); } } else { int dummy; stream >> item.pos >> item.size >> dummy >> dummy; if (!testing) { item_list.append(item); widget->setFloating(false); widget->setVisible(flags & StateFlagVisible); emit widget->dockLocationChanged(toDockWidgetArea(dockPos)); } } if (testing) { //was it is not really added to the layout, we need to delete the object here delete item.widgetItem; } } } else if (nextMarker == SequenceMarker) { int dummy; #ifdef QT_NO_TABBAR const int tabBarShape = 0; #endif QDockAreaLayoutItem item(new QDockAreaLayoutInfo(sep, dockPos, o, tabBarShape, mainWindow)); stream >> item.pos >> item.size >> dummy >> dummy; //we need to make sure the element is in the list so the dock widget can eventually be docked correctly if (!testing) item_list.append(item); //here we need to make sure we change the item in the item_list QDockAreaLayoutItem &lastItem = testing ? item : item_list.last(); if (!lastItem.subinfo->restoreState(stream, widgets, testing)) return false; } else { return false; } } #ifndef QT_NO_TABBAR if (!testing && tabbed && index >= 0 && index < item_list.count()) { updateTabBar(); setCurrentTabId(tabId(item_list.at(index))); } if (!testing && *sep == 1) updateSeparatorWidgets(); #endif return true; } #ifndef QT_NO_TABBAR void QDockAreaLayoutInfo::updateSeparatorWidgets() const { if (tabbed) { separatorWidgets.clear(); return; } int j = 0; for (int i = 0; i < item_list.count(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); if (item.skip()) continue; int next = this->next(i); if ((item.flags & QDockAreaLayoutItem::GapItem) || (next != -1 && (item_list.at(next).flags & QDockAreaLayoutItem::GapItem))) continue; if (item.subinfo) { item.subinfo->updateSeparatorWidgets(); } if (next == -1) break; QWidget *sepWidget; if (j < separatorWidgets.size() && separatorWidgets.at(j)) { sepWidget = separatorWidgets.at(j); } else { sepWidget = mainWindowLayout()->getSeparatorWidget(); separatorWidgets.append(sepWidget); } j++; #ifndef Q_WS_MAC sepWidget->raise(); #endif QRect sepRect = separatorRect(i).adjusted(-2, -2, 2, 2); sepWidget->setGeometry(sepRect); sepWidget->setMask( QRegion(separatorRect(i).translated( - sepRect.topLeft()))); sepWidget->show(); } for (int k = j; k < separatorWidgets.size(); ++k) { separatorWidgets[k]->hide(); } separatorWidgets.resize(j); Q_ASSERT(separatorWidgets.size() == j); } #endif //QT_NO_TABBAR #ifndef QT_NO_TABBAR //returns whether the tabbar is visible or not bool QDockAreaLayoutInfo::updateTabBar() const { if (!tabbed) return false; QDockAreaLayoutInfo *that = const_cast(this); if (that->tabBar == 0) { that->tabBar = mainWindowLayout()->getTabBar(); that->tabBar->setShape(static_cast(tabBarShape)); that->tabBar->setDrawBase(true); } bool blocked = tabBar->blockSignals(true); bool gap = false; int tab_idx = 0; for (int i = 0; i < item_list.count(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); if (item.skip()) continue; if (item.flags & QDockAreaLayoutItem::GapItem) { gap = true; continue; } if (item.widgetItem == 0) continue; QDockWidget *dw = qobject_cast(item.widgetItem->widget()); QString title = dw->d_func()->fixedWindowTitle; quintptr id = tabId(item); if (tab_idx == tabBar->count()) { tabBar->insertTab(tab_idx, title); #ifndef QT_NO_TOOLTIP tabBar->setTabToolTip(tab_idx, title); #endif tabBar->setTabData(tab_idx, id); } else if (qvariant_cast(tabBar->tabData(tab_idx)) != id) { if (tab_idx + 1 < tabBar->count() && qvariant_cast(tabBar->tabData(tab_idx + 1)) == id) tabBar->removeTab(tab_idx); else { tabBar->insertTab(tab_idx, title); #ifndef QT_NO_TOOLTIP tabBar->setTabToolTip(tab_idx, title); #endif tabBar->setTabData(tab_idx, id); } } if (title != tabBar->tabText(tab_idx)) { tabBar->setTabText(tab_idx, title); #ifndef QT_NO_TOOLTIP tabBar->setTabToolTip(tab_idx, title); #endif } ++tab_idx; } while (tab_idx < tabBar->count()) { tabBar->removeTab(tab_idx); } tabBar->blockSignals(blocked); //returns if the tabbar is visible or not return ( (gap ? 1 : 0) + tabBar->count()) > 1; } void QDockAreaLayoutInfo::setTabBarShape(int shape) { if (shape == tabBarShape) return; tabBarShape = shape; if (tabBar != 0) tabBar->setShape(static_cast(shape)); for (int i = 0; i < item_list.count(); ++i) { QDockAreaLayoutItem &item = item_list[i]; if (item.subinfo != 0) item.subinfo->setTabBarShape(shape); } } QSize QDockAreaLayoutInfo::tabBarMinimumSize() const { if (!updateTabBar()) return QSize(0, 0); return tabBar->minimumSizeHint(); } QSize QDockAreaLayoutInfo::tabBarSizeHint() const { if (!updateTabBar()) return QSize(0, 0); return tabBar->sizeHint(); } QSet QDockAreaLayoutInfo::usedTabBars() const { QSet result; if (tabbed) { updateTabBar(); result.insert(tabBar); } for (int i = 0; i < item_list.count(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); if (item.subinfo != 0) result += item.subinfo->usedTabBars(); } return result; } // returns a set of all used separator widgets for this dockarelayout info // and all subinfos QSet QDockAreaLayoutInfo::usedSeparatorWidgets() const { QSet result; for (int i = 0; i < separatorWidgets.count(); ++i) result << separatorWidgets.at(i); for (int i = 0; i < item_list.count(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); if (item.subinfo != 0) result += item.subinfo->usedSeparatorWidgets(); } return result; } QRect QDockAreaLayoutInfo::tabContentRect() const { if (!tabbed) return QRect(); QRect result = rect; QSize tbh = tabBarSizeHint(); if (!tbh.isNull()) { switch (tabBarShape) { case QTabBar::RoundedNorth: case QTabBar::TriangularNorth: result.adjust(0, tbh.height(), 0, 0); break; case QTabBar::RoundedSouth: case QTabBar::TriangularSouth: result.adjust(0, 0, 0, -tbh.height()); break; case QTabBar::RoundedEast: case QTabBar::TriangularEast: result.adjust(0, 0, -tbh.width(), 0); break; case QTabBar::RoundedWest: case QTabBar::TriangularWest: result.adjust(tbh.width(), 0, 0, 0); break; default: break; } } return result; } #endif // QT_NO_TABBAR /****************************************************************************** ** QDockAreaLayout */ QDockAreaLayout::QDockAreaLayout(QMainWindow *win) : fallbackToSizeHints(true) { mainWindow = win; sep = win->style()->pixelMetric(QStyle::PM_DockWidgetSeparatorExtent, 0, win); #ifndef QT_NO_TABBAR const int tabShape = QTabBar::RoundedSouth; #else const int tabShape = 0; #endif docks[QInternal::LeftDock] = QDockAreaLayoutInfo(&sep, QInternal::LeftDock, Qt::Vertical, tabShape, win); docks[QInternal::RightDock] = QDockAreaLayoutInfo(&sep, QInternal::RightDock, Qt::Vertical, tabShape, win); docks[QInternal::TopDock] = QDockAreaLayoutInfo(&sep, QInternal::TopDock, Qt::Horizontal, tabShape, win); docks[QInternal::BottomDock] = QDockAreaLayoutInfo(&sep, QInternal::BottomDock, Qt::Horizontal, tabShape, win); centralWidgetItem = 0; corners[Qt::TopLeftCorner] = Qt::TopDockWidgetArea; corners[Qt::TopRightCorner] = Qt::TopDockWidgetArea; corners[Qt::BottomLeftCorner] = Qt::BottomDockWidgetArea; corners[Qt::BottomRightCorner] = Qt::BottomDockWidgetArea; } bool QDockAreaLayout::isValid() const { return rect.isValid(); } void QDockAreaLayout::saveState(QDataStream &stream) const { stream << (uchar) DockWidgetStateMarker; int cnt = 0; for (int i = 0; i < QInternal::DockCount; ++i) { if (!docks[i].item_list.isEmpty()) ++cnt; } stream << cnt; for (int i = 0; i < QInternal::DockCount; ++i) { if (docks[i].item_list.isEmpty()) continue; stream << i << docks[i].rect.size(); docks[i].saveState(stream); } stream << centralWidgetRect.size(); for (int i = 0; i < 4; ++i) stream << static_cast(corners[i]); } bool QDockAreaLayout::restoreState(QDataStream &stream, const QList &_dockwidgets, bool testing) { QList dockwidgets = _dockwidgets; int cnt; stream >> cnt; for (int i = 0; i < cnt; ++i) { int pos; stream >> pos; QSize size; stream >> size; if (!testing) { docks[pos].rect = QRect(QPoint(0, 0), size); } if (!docks[pos].restoreState(stream, dockwidgets, testing)) { stream.setStatus(QDataStream::ReadCorruptData); return false; } } QSize size; stream >> size; centralWidgetRect = QRect(QPoint(0, 0), size); bool ok = stream.status() == QDataStream::Ok; if (ok) { int cornerData[4]; for (int i = 0; i < 4; ++i) stream >> cornerData[i]; if (stream.status() == QDataStream::Ok) { for (int i = 0; i < 4; ++i) corners[i] = static_cast(cornerData[i]); } if (!testing) fallbackToSizeHints = false; } return ok; } QList QDockAreaLayout::indexOfPlaceHolder(const QString &objectName) const { for (int i = 0; i < QInternal::DockCount; ++i) { QList result = docks[i].indexOfPlaceHolder(objectName); if (!result.isEmpty()) { result.prepend(i); return result; } } return QList(); } QList QDockAreaLayout::indexOf(QWidget *dockWidget) const { for (int i = 0; i < QInternal::DockCount; ++i) { QList result = docks[i].indexOf(dockWidget); if (!result.isEmpty()) { result.prepend(i); return result; } } return QList(); } QList QDockAreaLayout::gapIndex(const QPoint &pos) const { QMainWindow::DockOptions opts = mainWindow->dockOptions(); bool nestingEnabled = opts & QMainWindow::AllowNestedDocks; QDockAreaLayoutInfo::TabMode tabMode = QDockAreaLayoutInfo::NoTabs; #ifndef QT_NO_TABBAR if (opts & QMainWindow::AllowTabbedDocks || opts & QMainWindow::VerticalTabs) tabMode = QDockAreaLayoutInfo::AllowTabs; if (opts & QMainWindow::ForceTabbedDocks) tabMode = QDockAreaLayoutInfo::ForceTabs; if (tabMode == QDockAreaLayoutInfo::ForceTabs) nestingEnabled = false; #endif for (int i = 0; i < QInternal::DockCount; ++i) { const QDockAreaLayoutInfo &info = docks[i]; if (!info.isEmpty() && info.rect.contains(pos)) { QList result = docks[i].gapIndex(pos, nestingEnabled, tabMode); if (!result.isEmpty()) result.prepend(i); return result; } } for (int i = 0; i < QInternal::DockCount; ++i) { const QDockAreaLayoutInfo &info = docks[i]; if (info.isEmpty()) { QRect r; switch (i) { case QInternal::LeftDock: r = QRect(rect.left(), rect.top(), EmptyDropAreaSize, rect.height()); break; case QInternal::RightDock: r = QRect(rect.right() - EmptyDropAreaSize, rect.top(), EmptyDropAreaSize, rect.height()); break; case QInternal::TopDock: r = QRect(rect.left(), rect.top(), rect.width(), EmptyDropAreaSize); break; case QInternal::BottomDock: r = QRect(rect.left(), rect.bottom() - EmptyDropAreaSize, rect.width(), EmptyDropAreaSize); break; } if (r.contains(pos)) { if (opts & QMainWindow::ForceTabbedDocks && !info.item_list.isEmpty()) { //in case of ForceTabbedDocks, we pass -1 in order to force the gap to be tabbed //it mustn't be completely empty otherwise it won't work return QList() << i << -1 << 0; } else { return QList() << i << 0; } } } } return QList(); } QList QDockAreaLayout::findSeparator(const QPoint &pos) const { QList result; for (int i = 0; i < QInternal::DockCount; ++i) { const QDockAreaLayoutInfo &info = docks[i]; if (info.isEmpty()) continue; QRect rect = separatorRect(i); if (!rect.isNull() && sep == 1) rect.adjust(-2, -2, 2, 2); if (rect.contains(pos) && !info.hasFixedSize()) { result << i; break; } else if (info.rect.contains(pos)) { result = docks[i].findSeparator(pos); if (!result.isEmpty()) { result.prepend(i); break; } } } return result; } QDockAreaLayoutInfo *QDockAreaLayout::info(QWidget *widget) { for (int i = 0; i < QInternal::DockCount; ++i) { if (QDockAreaLayoutInfo *result = docks[i].info(widget)) return result; } return 0; } QDockAreaLayoutInfo *QDockAreaLayout::info(const QList &path) { Q_ASSERT(!path.isEmpty()); const int index = path.first(); Q_ASSERT(index >= 0 && index < QInternal::DockCount); if (path.count() == 1) return &docks[index]; return docks[index].info(path.mid(1)); } const QDockAreaLayoutInfo *QDockAreaLayout::info(const QList &path) const { return const_cast(this)->info(path); } QDockAreaLayoutItem &QDockAreaLayout::item(const QList &path) { Q_ASSERT(!path.isEmpty()); const int index = path.first(); Q_ASSERT(index >= 0 && index < QInternal::DockCount); return docks[index].item(path.mid(1)); } QRect QDockAreaLayout::itemRect(const QList &path) const { Q_ASSERT(!path.isEmpty()); const int index = path.first(); Q_ASSERT(index >= 0 && index < QInternal::DockCount); return docks[index].itemRect(path.mid(1)); } QRect QDockAreaLayout::separatorRect(int index) const { const QDockAreaLayoutInfo &dock = docks[index]; if (dock.isEmpty()) return QRect(); QRect r = dock.rect; switch (index) { case QInternal::LeftDock: return QRect(r.right() + 1, r.top(), sep, r.height()); case QInternal::RightDock: return QRect(r.left() - sep, r.top(), sep, r.height()); case QInternal::TopDock: return QRect(r.left(), r.bottom() + 1, r.width(), sep); case QInternal::BottomDock: return QRect(r.left(), r.top() - sep, r.width(), sep); default: break; } return QRect(); } QRect QDockAreaLayout::separatorRect(const QList &path) const { Q_ASSERT(!path.isEmpty()); const int index = path.first(); Q_ASSERT(index >= 0 && index < QInternal::DockCount); if (path.count() == 1) return separatorRect(index); else return docks[index].separatorRect(path.mid(1)); } bool QDockAreaLayout::insertGap(const QList &path, QLayoutItem *dockWidgetItem) { Q_ASSERT(!path.isEmpty()); const int index = path.first(); Q_ASSERT(index >= 0 && index < QInternal::DockCount); return docks[index].insertGap(path.mid(1), dockWidgetItem); } QLayoutItem *QDockAreaLayout::plug(const QList &path) { Q_ASSERT(!path.isEmpty()); const int index = path.first(); Q_ASSERT(index >= 0 && index < QInternal::DockCount); return docks[index].plug(path.mid(1)); } QLayoutItem *QDockAreaLayout::unplug(const QList &path) { Q_ASSERT(!path.isEmpty()); const int index = path.first(); Q_ASSERT(index >= 0 && index < QInternal::DockCount); return docks[index].unplug(path.mid(1)); } void QDockAreaLayout::remove(const QList &path) { Q_ASSERT(!path.isEmpty()); const int index = path.first(); Q_ASSERT(index >= 0 && index < QInternal::DockCount); docks[index].remove(path.mid(1)); } static inline int qMin(int i1, int i2, int i3) { return qMin(i1, qMin(i2, i3)); } static inline int qMax(int i1, int i2, int i3) { return qMax(i1, qMax(i2, i3)); } void QDockAreaLayout::getGrid(QVector *_ver_struct_list, QVector *_hor_struct_list) { QSize center_hint(0, 0); QSize center_min(0, 0); const bool have_central = centralWidgetItem != 0 && !centralWidgetItem->isEmpty(); if (have_central) { center_hint = centralWidgetRect.size(); if (!center_hint.isValid()) center_hint = centralWidgetItem->sizeHint(); center_min = centralWidgetItem->minimumSize(); } QRect center_rect = rect; if (!docks[QInternal::LeftDock].isEmpty()) center_rect.setLeft(rect.left() + docks[QInternal::LeftDock].rect.width() + sep); if (!docks[QInternal::TopDock].isEmpty()) center_rect.setTop(rect.top() + docks[QInternal::TopDock].rect.height() + sep); if (!docks[QInternal::RightDock].isEmpty()) center_rect.setRight(rect.right() - docks[QInternal::RightDock].rect.width() - sep); if (!docks[QInternal::BottomDock].isEmpty()) center_rect.setBottom(rect.bottom() - docks[QInternal::BottomDock].rect.height() - sep); QSize left_hint = docks[QInternal::LeftDock].size(); if (left_hint.isNull() || fallbackToSizeHints) left_hint = docks[QInternal::LeftDock].sizeHint(); QSize left_min = docks[QInternal::LeftDock].minimumSize(); QSize left_max = docks[QInternal::LeftDock].maximumSize(); left_hint = left_hint.boundedTo(left_max).expandedTo(left_min); QSize right_hint = docks[QInternal::RightDock].size(); if (right_hint.isNull() || fallbackToSizeHints) right_hint = docks[QInternal::RightDock].sizeHint(); QSize right_min = docks[QInternal::RightDock].minimumSize(); QSize right_max = docks[QInternal::RightDock].maximumSize(); right_hint = right_hint.boundedTo(right_max).expandedTo(right_min); QSize top_hint = docks[QInternal::TopDock].size(); if (top_hint.isNull() || fallbackToSizeHints) top_hint = docks[QInternal::TopDock].sizeHint(); QSize top_min = docks[QInternal::TopDock].minimumSize(); QSize top_max = docks[QInternal::TopDock].maximumSize(); top_hint = top_hint.boundedTo(top_max).expandedTo(top_min); QSize bottom_hint = docks[QInternal::BottomDock].size(); if (bottom_hint.isNull() || fallbackToSizeHints) bottom_hint = docks[QInternal::BottomDock].sizeHint(); QSize bottom_min = docks[QInternal::BottomDock].minimumSize(); QSize bottom_max = docks[QInternal::BottomDock].maximumSize(); bottom_hint = bottom_hint.boundedTo(bottom_max).expandedTo(bottom_min); if (_ver_struct_list != 0) { QVector &ver_struct_list = *_ver_struct_list; ver_struct_list.resize(3); // top -------------------------------------------------- ver_struct_list[0].init(); ver_struct_list[0].stretch = 0; ver_struct_list[0].sizeHint = top_hint.height(); ver_struct_list[0].minimumSize = top_min.height(); ver_struct_list[0].maximumSize = top_max.height(); ver_struct_list[0].expansive = false; ver_struct_list[0].empty = docks[QInternal::TopDock].isEmpty(); ver_struct_list[0].pos = docks[QInternal::TopDock].rect.top(); ver_struct_list[0].size = docks[QInternal::TopDock].rect.height(); // center -------------------------------------------------- ver_struct_list[1].init(); ver_struct_list[1].stretch = center_hint.height(); bool tl_significant = corners[Qt::TopLeftCorner] == Qt::TopDockWidgetArea || docks[QInternal::TopDock].isEmpty(); bool bl_significant = corners[Qt::BottomLeftCorner] == Qt::BottomDockWidgetArea || docks[QInternal::BottomDock].isEmpty(); bool tr_significant = corners[Qt::TopRightCorner] == Qt::TopDockWidgetArea || docks[QInternal::TopDock].isEmpty(); bool br_significant = corners[Qt::BottomRightCorner] == Qt::BottomDockWidgetArea || docks[QInternal::BottomDock].isEmpty(); int left = (tl_significant && bl_significant) ? left_hint.height() : 0; int right = (tr_significant && br_significant) ? right_hint.height() : 0; ver_struct_list[1].sizeHint = qMax(left, center_hint.height(), right); left = (tl_significant && bl_significant) ? left_min.height() : 0; right = (tr_significant && br_significant) ? right_min.height() : 0; ver_struct_list[1].minimumSize = qMax(left, center_min.height(), right); ver_struct_list[1].maximumSize = have_central ? QWIDGETSIZE_MAX : 0; ver_struct_list[1].expansive = have_central; ver_struct_list[1].empty = docks[QInternal::LeftDock].isEmpty() && !have_central && docks[QInternal::RightDock].isEmpty(); ver_struct_list[1].pos = center_rect.top(); ver_struct_list[1].size = center_rect.height(); // bottom -------------------------------------------------- ver_struct_list[2].init(); ver_struct_list[2].stretch = 0; ver_struct_list[2].sizeHint = bottom_hint.height(); ver_struct_list[2].minimumSize = bottom_min.height(); ver_struct_list[2].maximumSize = bottom_max.height(); ver_struct_list[2].expansive = false; ver_struct_list[2].empty = docks[QInternal::BottomDock].isEmpty(); ver_struct_list[2].pos = docks[QInternal::BottomDock].rect.top(); ver_struct_list[2].size = docks[QInternal::BottomDock].rect.height(); for (int i = 0; i < 3; ++i) { ver_struct_list[i].sizeHint = qMax(ver_struct_list[i].sizeHint, ver_struct_list[i].minimumSize); } } if (_hor_struct_list != 0) { QVector &hor_struct_list = *_hor_struct_list; hor_struct_list.resize(3); // left -------------------------------------------------- hor_struct_list[0].init(); hor_struct_list[0].stretch = 0; hor_struct_list[0].sizeHint = left_hint.width(); hor_struct_list[0].minimumSize = left_min.width(); hor_struct_list[0].maximumSize = left_max.width(); hor_struct_list[0].expansive = false; hor_struct_list[0].empty = docks[QInternal::LeftDock].isEmpty(); hor_struct_list[0].pos = docks[QInternal::LeftDock].rect.left(); hor_struct_list[0].size = docks[QInternal::LeftDock].rect.width(); // center -------------------------------------------------- hor_struct_list[1].init(); hor_struct_list[1].stretch = center_hint.width(); bool tl_significant = corners[Qt::TopLeftCorner] == Qt::LeftDockWidgetArea || docks[QInternal::LeftDock].isEmpty(); bool tr_significant = corners[Qt::TopRightCorner] == Qt::RightDockWidgetArea || docks[QInternal::RightDock].isEmpty(); bool bl_significant = corners[Qt::BottomLeftCorner] == Qt::LeftDockWidgetArea || docks[QInternal::LeftDock].isEmpty(); bool br_significant = corners[Qt::BottomRightCorner] == Qt::RightDockWidgetArea || docks[QInternal::RightDock].isEmpty(); int top = (tl_significant && tr_significant) ? top_hint.width() : 0; int bottom = (bl_significant && br_significant) ? bottom_hint.width() : 0; hor_struct_list[1].sizeHint = qMax(top, center_hint.width(), bottom); top = (tl_significant && tr_significant) ? top_min.width() : 0; bottom = (bl_significant && br_significant) ? bottom_min.width() : 0; hor_struct_list[1].minimumSize = qMax(top, center_min.width(), bottom); hor_struct_list[1].maximumSize = have_central ? QWIDGETSIZE_MAX : 0; hor_struct_list[1].expansive = have_central; hor_struct_list[1].empty = !have_central; hor_struct_list[1].pos = center_rect.left(); hor_struct_list[1].size = center_rect.width(); // right -------------------------------------------------- hor_struct_list[2].init(); hor_struct_list[2].stretch = 0; hor_struct_list[2].sizeHint = right_hint.width(); hor_struct_list[2].minimumSize = right_min.width(); hor_struct_list[2].maximumSize = right_max.width(); hor_struct_list[2].expansive = false; hor_struct_list[2].empty = docks[QInternal::RightDock].isEmpty(); hor_struct_list[2].pos = docks[QInternal::RightDock].rect.left(); hor_struct_list[2].size = docks[QInternal::RightDock].rect.width(); for (int i = 0; i < 3; ++i) { hor_struct_list[i].sizeHint = qMax(hor_struct_list[i].sizeHint, hor_struct_list[i].minimumSize); } } } void QDockAreaLayout::setGrid(QVector *ver_struct_list, QVector *hor_struct_list) { // top --------------------------------------------------- if (!docks[QInternal::TopDock].isEmpty()) { QRect r = docks[QInternal::TopDock].rect; if (hor_struct_list != 0) { r.setLeft(corners[Qt::TopLeftCorner] == Qt::TopDockWidgetArea || docks[QInternal::LeftDock].isEmpty() ? rect.left() : hor_struct_list->at(1).pos); r.setRight(corners[Qt::TopRightCorner] == Qt::TopDockWidgetArea || docks[QInternal::RightDock].isEmpty() ? rect.right() : hor_struct_list->at(2).pos - sep - 1); } if (ver_struct_list != 0) { r.setTop(rect.top()); r.setBottom(ver_struct_list->at(1).pos - sep - 1); } docks[QInternal::TopDock].rect = r; docks[QInternal::TopDock].fitItems(); } // bottom --------------------------------------------------- if (!docks[QInternal::BottomDock].isEmpty()) { QRect r = docks[QInternal::BottomDock].rect; if (hor_struct_list != 0) { r.setLeft(corners[Qt::BottomLeftCorner] == Qt::BottomDockWidgetArea || docks[QInternal::LeftDock].isEmpty() ? rect.left() : hor_struct_list->at(1).pos); r.setRight(corners[Qt::BottomRightCorner] == Qt::BottomDockWidgetArea || docks[QInternal::RightDock].isEmpty() ? rect.right() : hor_struct_list->at(2).pos - sep - 1); } if (ver_struct_list != 0) { r.setTop(ver_struct_list->at(2).pos); r.setBottom(rect.bottom()); } docks[QInternal::BottomDock].rect = r; docks[QInternal::BottomDock].fitItems(); } // left --------------------------------------------------- if (!docks[QInternal::LeftDock].isEmpty()) { QRect r = docks[QInternal::LeftDock].rect; if (hor_struct_list != 0) { r.setLeft(rect.left()); r.setRight(hor_struct_list->at(1).pos - sep - 1); } if (ver_struct_list != 0) { r.setTop(corners[Qt::TopLeftCorner] == Qt::LeftDockWidgetArea || docks[QInternal::TopDock].isEmpty() ? rect.top() : ver_struct_list->at(1).pos); r.setBottom(corners[Qt::BottomLeftCorner] == Qt::LeftDockWidgetArea || docks[QInternal::BottomDock].isEmpty() ? rect.bottom() : ver_struct_list->at(2).pos - sep - 1); } docks[QInternal::LeftDock].rect = r; docks[QInternal::LeftDock].fitItems(); } // right --------------------------------------------------- if (!docks[QInternal::RightDock].isEmpty()) { QRect r = docks[QInternal::RightDock].rect; if (hor_struct_list != 0) { r.setLeft(hor_struct_list->at(2).pos); r.setRight(rect.right()); } if (ver_struct_list != 0) { r.setTop(corners[Qt::TopRightCorner] == Qt::RightDockWidgetArea || docks[QInternal::TopDock].isEmpty() ? rect.top() : ver_struct_list->at(1).pos); r.setBottom(corners[Qt::BottomRightCorner] == Qt::RightDockWidgetArea || docks[QInternal::BottomDock].isEmpty() ? rect.bottom() : ver_struct_list->at(2).pos - sep - 1); } docks[QInternal::RightDock].rect = r; docks[QInternal::RightDock].fitItems(); } // center --------------------------------------------------- if (hor_struct_list != 0) { centralWidgetRect.setLeft(hor_struct_list->at(1).pos); centralWidgetRect.setWidth(hor_struct_list->at(1).size); } if (ver_struct_list != 0) { centralWidgetRect.setTop(ver_struct_list->at(1).pos); centralWidgetRect.setHeight(ver_struct_list->at(1).size); } } void QDockAreaLayout::fitLayout() { QVector ver_struct_list(3); QVector hor_struct_list(3); getGrid(&ver_struct_list, &hor_struct_list); qGeomCalc(ver_struct_list, 0, 3, rect.top(), rect.height(), sep); qGeomCalc(hor_struct_list, 0, 3, rect.left(), rect.width(), sep); setGrid(&ver_struct_list, &hor_struct_list); } void QDockAreaLayout::clear() { for (int i = 0; i < QInternal::DockCount; ++i) docks[i].clear(); rect = QRect(); centralWidgetRect = QRect(); } QSize QDockAreaLayout::sizeHint() const { int left_sep = 0; int right_sep = 0; int top_sep = 0; int bottom_sep = 0; if (centralWidgetItem != 0) { left_sep = docks[QInternal::LeftDock].isEmpty() ? 0 : sep; right_sep = docks[QInternal::RightDock].isEmpty() ? 0 : sep; top_sep = docks[QInternal::TopDock].isEmpty() ? 0 : sep; bottom_sep = docks[QInternal::BottomDock].isEmpty() ? 0 : sep; } QSize left = docks[QInternal::LeftDock].sizeHint() + QSize(left_sep, 0); QSize right = docks[QInternal::RightDock].sizeHint() + QSize(right_sep, 0); QSize top = docks[QInternal::TopDock].sizeHint() + QSize(0, top_sep); QSize bottom = docks[QInternal::BottomDock].sizeHint() + QSize(0, bottom_sep); QSize center = centralWidgetItem == 0 ? QSize(0, 0) : centralWidgetItem->sizeHint(); int row1 = top.width(); int row2 = left.width() + center.width() + right.width(); int row3 = bottom.width(); int col1 = left.height(); int col2 = top.height() + center.height() + bottom.height(); int col3 = right.height(); if (corners[Qt::TopLeftCorner] == Qt::LeftDockWidgetArea) row1 += left.width(); else col1 += top.height(); if (corners[Qt::TopRightCorner] == Qt::RightDockWidgetArea) row1 += right.width(); else col3 += top.height(); if (corners[Qt::BottomLeftCorner] == Qt::LeftDockWidgetArea) row3 += left.width(); else col1 += bottom.height(); if (corners[Qt::BottomRightCorner] == Qt::RightDockWidgetArea) row3 += right.width(); else col3 += bottom.height(); return QSize(qMax(row1, row2, row3), qMax(col1, col2, col3)); } QSize QDockAreaLayout::minimumSize() const { int left_sep = 0; int right_sep = 0; int top_sep = 0; int bottom_sep = 0; if (centralWidgetItem != 0) { left_sep = docks[QInternal::LeftDock].isEmpty() ? 0 : sep; right_sep = docks[QInternal::RightDock].isEmpty() ? 0 : sep; top_sep = docks[QInternal::TopDock].isEmpty() ? 0 : sep; bottom_sep = docks[QInternal::BottomDock].isEmpty() ? 0 : sep; } QSize left = docks[QInternal::LeftDock].minimumSize() + QSize(left_sep, 0); QSize right = docks[QInternal::RightDock].minimumSize() + QSize(right_sep, 0); QSize top = docks[QInternal::TopDock].minimumSize() + QSize(0, top_sep); QSize bottom = docks[QInternal::BottomDock].minimumSize() + QSize(0, bottom_sep); QSize center = centralWidgetItem == 0 ? QSize(0, 0) : centralWidgetItem->minimumSize(); int row1 = top.width(); int row2 = left.width() + center.width() + right.width(); int row3 = bottom.width(); int col1 = left.height(); int col2 = top.height() + center.height() + bottom.height(); int col3 = right.height(); if (corners[Qt::TopLeftCorner] == Qt::LeftDockWidgetArea) row1 += left.width(); else col1 += top.height(); if (corners[Qt::TopRightCorner] == Qt::RightDockWidgetArea) row1 += right.width(); else col3 += top.height(); if (corners[Qt::BottomLeftCorner] == Qt::LeftDockWidgetArea) row3 += left.width(); else col1 += bottom.height(); if (corners[Qt::BottomRightCorner] == Qt::RightDockWidgetArea) row3 += right.width(); else col3 += bottom.height(); return QSize(qMax(row1, row2, row3), qMax(col1, col2, col3)); } bool QDockAreaLayout::restoreDockWidget(QDockWidget *dockWidget) { QList index = indexOfPlaceHolder(dockWidget->objectName()); if (index.isEmpty()) return false; QDockAreaLayoutItem &item = this->item(index); QPlaceHolderItem *placeHolder = item.placeHolderItem; Q_ASSERT(placeHolder != 0); item.widgetItem = new QDockWidgetItem(dockWidget); if (placeHolder->window) { QDesktopWidget desktop; QRect r = constrainedRect(placeHolder->topLevelRect, desktop.screenGeometry(dockWidget)); dockWidget->d_func()->setWindowState(true, true, r); } dockWidget->setVisible(!placeHolder->hidden); #ifdef Q_WS_X11 if (placeHolder->window) // gets rid of the X11BypassWindowManager window flag dockWidget->d_func()->setWindowState(true); #endif item.placeHolderItem = 0; delete placeHolder; return true; } void QDockAreaLayout::addDockWidget(QInternal::DockPosition pos, QDockWidget *dockWidget, Qt::Orientation orientation) { QLayoutItem *dockWidgetItem = new QDockWidgetItem(dockWidget); QDockAreaLayoutInfo &info = docks[pos]; if (orientation == info.o || info.item_list.count() <= 1) { // empty dock areas, or dock areas containing exactly one widget can have their orientation // switched. info.o = orientation; QDockAreaLayoutItem new_item(dockWidgetItem); info.item_list.append(new_item); #ifndef QT_NO_TABBAR if (info.tabbed && !new_item.skip()) { info.updateTabBar(); info.setCurrentTabId(tabId(new_item)); } #endif } else { #ifndef QT_NO_TABBAR int tbshape = info.tabBarShape; #else int tbshape = 0; #endif QDockAreaLayoutInfo new_info(&sep, pos, orientation, tbshape, mainWindow); new_info.item_list.append(new QDockAreaLayoutInfo(info)); new_info.item_list.append(dockWidgetItem); info = new_info; } QList index = indexOfPlaceHolder(dockWidget->objectName()); if (!index.isEmpty()) remove(index); } void QDockAreaLayout::tabifyDockWidget(QDockWidget *first, QDockWidget *second) { QList path = indexOf(first); if (path.isEmpty()) return; QDockAreaLayoutInfo *info = this->info(path); Q_ASSERT(info != 0); info->tab(path.last(), new QDockWidgetItem(second)); QList index = indexOfPlaceHolder(second->objectName()); if (!index.isEmpty()) remove(index); } void QDockAreaLayout::splitDockWidget(QDockWidget *after, QDockWidget *dockWidget, Qt::Orientation orientation) { QList path = indexOf(after); if (path.isEmpty()) return; QDockAreaLayoutInfo *info = this->info(path); Q_ASSERT(info != 0); info->split(path.last(), orientation, new QDockWidgetItem(dockWidget)); QList index = indexOfPlaceHolder(dockWidget->objectName()); if (!index.isEmpty()) remove(index); } void QDockAreaLayout::apply(bool animate) { QWidgetAnimator &widgetAnimator = qt_mainwindow_layout(mainWindow)->widgetAnimator; for (int i = 0; i < QInternal::DockCount; ++i) docks[i].apply(animate); if (centralWidgetItem != 0 && !centralWidgetItem->isEmpty()) { widgetAnimator.animate(centralWidgetItem->widget(), centralWidgetRect, animate); } #ifndef QT_NO_TABBAR if (sep == 1) updateSeparatorWidgets(); #endif //QT_NO_TABBAR } void QDockAreaLayout::paintSeparators(QPainter *p, QWidget *widget, const QRegion &clip, const QPoint &mouse) const { for (int i = 0; i < QInternal::DockCount; ++i) { const QDockAreaLayoutInfo &dock = docks[i]; if (dock.isEmpty()) continue; QRect r = separatorRect(i); if (clip.contains(r) && !dock.hasFixedSize()) { Qt::Orientation opposite = dock.o == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal; paintSep(p, widget, r, opposite, r.contains(mouse)); } if (clip.contains(dock.rect)) dock.paintSeparators(p, widget, clip, mouse); } } QRegion QDockAreaLayout::separatorRegion() const { QRegion result; for (int i = 0; i < QInternal::DockCount; ++i) { const QDockAreaLayoutInfo &dock = docks[i]; if (dock.isEmpty()) continue; result |= separatorRect(i); result |= dock.separatorRegion(); } return result; } int QDockAreaLayout::separatorMove(const QList &separator, const QPoint &origin, const QPoint &dest) { int delta = 0; int index = separator.last(); if (separator.count() > 1) { QDockAreaLayoutInfo *info = this->info(separator); delta = pick(info->o, dest - origin); if (delta != 0) delta = info->separatorMove(index, delta); info->apply(false); return delta; } QVector list; if (index == QInternal::LeftDock || index == QInternal::RightDock) getGrid(0, &list); else getGrid(&list, 0); int sep_index = index == QInternal::LeftDock || index == QInternal::TopDock ? 0 : 1; Qt::Orientation o = index == QInternal::LeftDock || index == QInternal::RightDock ? Qt::Horizontal : Qt::Vertical; delta = pick(o, dest - origin); delta = separatorMoveHelper(list, sep_index, delta, sep); if (index == QInternal::LeftDock || index == QInternal::RightDock) setGrid(0, &list); else setGrid(&list, 0); apply(false); return delta; } #ifndef QT_NO_TABBAR // Sets the correct positions for the separator widgets // Allocates new sepearator widgets with getSeparatorWidget void QDockAreaLayout::updateSeparatorWidgets() const { int j = 0; for (int i = 0; i < QInternal::DockCount; ++i) { const QDockAreaLayoutInfo &dock = docks[i]; if (dock.isEmpty()) continue; QWidget *sepWidget; if (j < separatorWidgets.size()) { sepWidget = separatorWidgets.at(j); } else { sepWidget = qt_mainwindow_layout(mainWindow)->getSeparatorWidget(); separatorWidgets.append(sepWidget); } j++; #ifndef Q_WS_MAC sepWidget->raise(); #endif QRect sepRect = separatorRect(i).adjusted(-2, -2, 2, 2); sepWidget->setGeometry(sepRect); sepWidget->setMask( QRegion(separatorRect(i).translated( - sepRect.topLeft()))); sepWidget->show(); } for (int i = j; i < separatorWidgets.size(); ++i) separatorWidgets.at(i)->hide(); separatorWidgets.resize(j); } #endif //QT_NO_TABBAR QLayoutItem *QDockAreaLayout::itemAt(int *x, int index) const { Q_ASSERT(x != 0); for (int i = 0; i < QInternal::DockCount; ++i) { const QDockAreaLayoutInfo &dock = docks[i]; if (QLayoutItem *ret = dock.itemAt(x, index)) return ret; } if (centralWidgetItem && (*x)++ == index) return centralWidgetItem; return 0; } QLayoutItem *QDockAreaLayout::takeAt(int *x, int index) { Q_ASSERT(x != 0); for (int i = 0; i < QInternal::DockCount; ++i) { QDockAreaLayoutInfo &dock = docks[i]; if (QLayoutItem *ret = dock.takeAt(x, index)) return ret; } if (centralWidgetItem && (*x)++ == index) { QLayoutItem *ret = centralWidgetItem; centralWidgetItem = 0; return ret; } return 0; } void QDockAreaLayout::deleteAllLayoutItems() { for (int i = 0; i < QInternal::DockCount; ++i) docks[i].deleteAllLayoutItems(); } #ifndef QT_NO_TABBAR QSet QDockAreaLayout::usedTabBars() const { QSet result; for (int i = 0; i < QInternal::DockCount; ++i) { const QDockAreaLayoutInfo &dock = docks[i]; result += dock.usedTabBars(); } return result; } // Returns the set of all used separator widgets QSet QDockAreaLayout::usedSeparatorWidgets() const { QSet result; for (int i = 0; i < separatorWidgets.count(); ++i) result << separatorWidgets.at(i); for (int i = 0; i < QInternal::DockCount; ++i) { const QDockAreaLayoutInfo &dock = docks[i]; result += dock.usedSeparatorWidgets(); } return result; } #endif QRect QDockAreaLayout::gapRect(const QList &path) const { const QDockAreaLayoutInfo *info = this->info(path); if (info == 0) return QRect(); const QList &item_list = info->item_list; Qt::Orientation o = info->o; int index = path.last(); if (index < 0 || index >= item_list.count()) return QRect(); const QDockAreaLayoutItem &item = item_list.at(index); if (!(item.flags & QDockAreaLayoutItem::GapItem)) return QRect(); QRect result; #ifndef QT_NO_TABBAR if (info->tabbed) { result = info->tabContentRect(); } else #endif { int pos = item.pos; int size = item.size; int prev = info->prev(index); int next = info->next(index); if (prev != -1 && !(item_list.at(prev).flags & QDockAreaLayoutItem::GapItem)) { pos += sep; size -= sep; } if (next != -1 && !(item_list.at(next).flags & QDockAreaLayoutItem::GapItem)) size -= sep; QPoint p; rpick(o, p) = pos; rperp(o, p) = perp(o, info->rect.topLeft()); QSize s; rpick(o, s) = size; rperp(o, s) = perp(o, info->rect.size()); result = QRect(p, s); } return result; } void QDockAreaLayout::keepSize(QDockWidget *w) { QList path = indexOf(w); if (path.isEmpty()) return; QDockAreaLayoutItem &item = this->item(path); if (item.size != -1) item.flags |= QDockAreaLayoutItem::KeepSize; } void QDockAreaLayout::styleChangedEvent() { sep = mainWindow->style()->pixelMetric(QStyle::PM_DockWidgetSeparatorExtent, 0, mainWindow); if (isValid()) fitLayout(); } QT_END_NAMESPACE #endif // QT_NO_DOCKWIDGET