diff options
Diffstat (limited to 'src/widgets/widgets/qmdiarea.cpp')
-rw-r--r-- | src/widgets/widgets/qmdiarea.cpp | 164 |
1 files changed, 84 insertions, 80 deletions
diff --git a/src/widgets/widgets/qmdiarea.cpp b/src/widgets/widgets/qmdiarea.cpp index 3ab8632f80..79b83453ac 100644 --- a/src/widgets/widgets/qmdiarea.cpp +++ b/src/widgets/widgets/qmdiarea.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /*! \class QMdiArea @@ -176,6 +140,7 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; using namespace QMdi; // Asserts in debug mode, gives warning otherwise. @@ -255,19 +220,7 @@ static inline QMdiArea *mdiAreaParent(QWidget *widget) } #if QT_CONFIG(tabwidget) -static inline QTabBar::Shape tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position) -{ - const bool rounded = (shape == QTabWidget::Rounded); - if (position == QTabWidget::North) - return rounded ? QTabBar::RoundedNorth : QTabBar::TriangularNorth; - if (position == QTabWidget::South) - return rounded ? QTabBar::RoundedSouth : QTabBar::TriangularSouth; - if (position == QTabWidget::East) - return rounded ? QTabBar::RoundedEast : QTabBar::TriangularEast; - if (position == QTabWidget::West) - return rounded ? QTabBar::RoundedWest : QTabBar::TriangularWest; - return QTabBar::RoundedNorth; -} +QTabBar::Shape _q_tb_tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position); #endif // QT_CONFIG(tabwidget) static inline QString tabTextFor(QMdiSubWindow *subWindow) @@ -277,7 +230,7 @@ static inline QString tabTextFor(QMdiSubWindow *subWindow) QString title = subWindow->windowTitle(); if (subWindow->isWindowModified()) { - title.replace(QLatin1String("[*]"), QLatin1String("*")); + title.replace("[*]"_L1, "*"_L1); } else { extern QString qt_setWindowTitle_helperHelper(const QString&, const QWidget*); title = qt_setWindowTitle_helperHelper(title, subWindow); @@ -469,8 +422,8 @@ QList<QRect> MinOverlapPlacer::getCandidatePlacements(const QSize &size, const Q ylist.erase(std::unique(ylist.begin(), ylist.end()), ylist.end()); result.reserve(ylist.size() * xlist.size()); - for (int y : qAsConst(ylist)) - for (int x : qAsConst(xlist)) + for (int y : std::as_const(ylist)) + for (int x : std::as_const(xlist)) result << QRect(QPoint(x, y), size); return result; } @@ -718,7 +671,11 @@ void QMdiAreaPrivate::_q_deactivateAllWindows(QMdiSubWindow *aboutToActivate) aboutToBecomeActive = aboutToActivate; Q_ASSERT(aboutToBecomeActive); - foreach (QMdiSubWindow *child, childWindows) { + // Take a copy because child->showNormal() could indirectly call + // QCoreApplication::sendEvent(), which could call unknown code that e.g. + // recurses into the class modifying childWindows. + const auto subWindows = childWindows; + for (QMdiSubWindow *child : subWindows) { if (!sanityCheck(child, "QMdiArea::deactivateAllWindows") || aboutToBecomeActive == child) continue; // We don't want to handle signals caused by child->showNormal(). @@ -785,7 +742,7 @@ void QMdiAreaPrivate::_q_currentTabChanged(int index) // If the previous active sub-window was hidden, disable the tab. if (indexToLastActiveTab >= 0 && indexToLastActiveTab < tabBar->count() - && indexToLastActiveTab < childWindows.count()) { + && indexToLastActiveTab < childWindows.size()) { QMdiSubWindow *lastActive = childWindows.at(indexToLastActiveTab); if (lastActive && lastActive->isHidden()) tabBar->setTabEnabled(indexToLastActiveTab, false); @@ -859,7 +816,7 @@ void QMdiAreaPrivate::appendChild(QMdiSubWindow *child) if (tabBar) { tabBar->addTab(child->windowIcon(), tabTextFor(child)); updateTabBarGeometry(); - if (childWindows.count() == 1 && !(options & QMdiArea::DontMaximizeSubWindowOnActivation)) + if (childWindows.size() == 1 && !(options & QMdiArea::DontMaximizeSubWindowOnActivation)) showActiveWindowMaximized = true; } #endif @@ -893,7 +850,7 @@ void QMdiAreaPrivate::place(Placer *placer, QMdiSubWindow *child) QList<QRect> rects; rects.reserve(childWindows.size()); QRect parentRect = q->rect(); - foreach (QMdiSubWindow *window, childWindows) { + for (QMdiSubWindow *window : std::as_const(childWindows)) { if (!sanityCheck(window, "QMdiArea::place") || window == child || !window->isVisibleTo(q) || !window->testAttribute(Qt::WA_Moved)) { continue; @@ -935,7 +892,7 @@ void QMdiAreaPrivate::rearrange(Rearranger *rearranger) const bool reverseList = rearranger->type() == Rearranger::RegularTiler; const QList<QMdiSubWindow *> subWindows = subWindowList(activationOrder, reverseList); QSize minSubWindowSize; - foreach (QMdiSubWindow *child, subWindows) { + for (QMdiSubWindow *child : subWindows) { if (!sanityCheck(child, "QMdiArea::rearrange") || !child->isVisible()) continue; if (rearranger->type() == Rearranger::IconTiler) { @@ -954,7 +911,7 @@ void QMdiAreaPrivate::rearrange(Rearranger *rearranger) QRect domain = viewport->rect(); if (rearranger->type() == Rearranger::RegularTiler && !widgets.isEmpty()) - domain = resizeToMinimumTileSize(minSubWindowSize, widgets.count()); + domain = resizeToMinimumTileSize(minSubWindowSize, widgets.size()); rearranger->rearrange(widgets, domain); @@ -1331,7 +1288,7 @@ bool QMdiAreaPrivate::scrollBarsEnabled() const */ bool QMdiAreaPrivate::lastWindowAboutToBeDestroyed() const { - if (childWindows.count() != 1) + if (childWindows.size() != 1) return false; QMdiSubWindow *last = childWindows.at(0); @@ -1349,7 +1306,7 @@ bool QMdiAreaPrivate::lastWindowAboutToBeDestroyed() const */ void QMdiAreaPrivate::setChildActivationEnabled(bool enable, bool onlyNextActivationEvent) const { - foreach (QMdiSubWindow *subWindow, childWindows) { + for (QMdiSubWindow *subWindow : childWindows) { if (!subWindow || !subWindow->isVisible()) continue; if (onlyNextActivationEvent) @@ -1371,7 +1328,11 @@ void QMdiAreaPrivate::scrollBarPolicyChanged(Qt::Orientation orientation, Qt::Sc const QMdiSubWindow::SubWindowOption option = orientation == Qt::Horizontal ? QMdiSubWindow::AllowOutsideAreaHorizontally : QMdiSubWindow::AllowOutsideAreaVertically; const bool enable = policy != Qt::ScrollBarAlwaysOff; - foreach (QMdiSubWindow *child, childWindows) { + // Take a copy because child->setOption() may indirectly call QCoreApplication::sendEvent(), + // the latter could call unknown code that could e.g. recurse into the class + // modifying childWindows. + const auto subWindows = childWindows; + for (QMdiSubWindow *child : subWindows) { if (!sanityCheck(child, "QMdiArea::scrollBarPolicyChanged")) continue; child->setOption(option, enable); @@ -1387,7 +1348,7 @@ QMdiAreaPrivate::subWindowList(QMdiArea::WindowOrder order, bool reversed) const return list; if (order == QMdiArea::CreationOrder) { - foreach (QMdiSubWindow *child, childWindows) { + for (QMdiSubWindow *child : childWindows) { if (!child) continue; if (!reversed) @@ -1407,7 +1368,7 @@ QMdiAreaPrivate::subWindowList(QMdiArea::WindowOrder order, bool reversed) const } } else { // ActivationHistoryOrder Q_ASSERT(indicesToActivatedChildren.size() == childWindows.size()); - for (int i = indicesToActivatedChildren.count() - 1; i >= 0; --i) { + for (int i = indicesToActivatedChildren.size() - 1; i >= 0; --i) { QMdiSubWindow *child = childWindows.at(indicesToActivatedChildren.at(i)); if (!child) continue; @@ -1515,7 +1476,7 @@ void QMdiAreaPrivate::highlightNextSubWindow(int increaseFactor) if (!rubberBand) { rubberBand = new QRubberBand(QRubberBand::Rectangle, q); // For accessibility to identify this special widget. - rubberBand->setObjectName(QLatin1String("qt_rubberband")); + rubberBand->setObjectName("qt_rubberband"_L1); rubberBand->setWindowFlags(rubberBand->windowFlags() | Qt::WindowStaysOnTopHint); } #endif @@ -1568,12 +1529,17 @@ void QMdiAreaPrivate::setViewMode(QMdiArea::ViewMode mode) tabBar->setTabsClosable(tabsClosable); tabBar->setMovable(tabsMovable); #if QT_CONFIG(tabwidget) - tabBar->setShape(tabBarShapeFrom(tabShape, tabPosition)); + tabBar->setShape(_q_tb_tabBarShapeFrom(tabShape, tabPosition)); #endif isSubWindowsTiled = false; - foreach (QMdiSubWindow *subWindow, childWindows) + // Take a copy as tabBar->addTab() will (indirectly) create a connection between + // the tab close button clicked() signal and the _q_closeTab() slot, which may + // indirectly call QCoreApplication::sendEvent(), the latter could result in + // invoking unknown code that could e.g. recurse into the class modifying childWindows. + const auto subWindows = childWindows; + for (QMdiSubWindow *subWindow : subWindows) tabBar->addTab(subWindow->windowIcon(), tabTextFor(subWindow)); QMdiSubWindow *current = q->currentSubWindow(); @@ -1632,7 +1598,7 @@ void QMdiAreaPrivate::updateTabBarGeometry() Q_Q(QMdiArea); #if QT_CONFIG(tabwidget) - Q_ASSERT(tabBarShapeFrom(tabShape, tabPosition) == tabBar->shape()); + Q_ASSERT(_q_tb_tabBarShapeFrom(tabShape, tabPosition) == tabBar->shape()); #endif const QSize tabBarSizeHint = tabBar->sizeHint(); @@ -1689,7 +1655,7 @@ void QMdiAreaPrivate::refreshTabBar() tabBar->setTabsClosable(tabsClosable); tabBar->setMovable(tabsMovable); #if QT_CONFIG(tabwidget) - tabBar->setShape(tabBarShapeFrom(tabShape, tabPosition)); + tabBar->setShape(_q_tb_tabBarShapeFrom(tabShape, tabPosition)); #endif updateTabBarGeometry(); } @@ -1796,7 +1762,7 @@ QMdiSubWindow *QMdiArea::currentSubWindow() const if (d->isActivated && !window()->isMinimized()) return nullptr; - Q_ASSERT(d->indicesToActivatedChildren.count() > 0); + Q_ASSERT(d->indicesToActivatedChildren.size() > 0); int index = d->indicesToActivatedChildren.at(0); Q_ASSERT(index >= 0 && index < d->childWindows.size()); QMdiSubWindow *current = d->childWindows.at(index); @@ -1895,7 +1861,11 @@ void QMdiArea::closeAllSubWindows() return; d->isSubWindowsTiled = false; - foreach (QMdiSubWindow *child, d->childWindows) { + // Take a copy because the child->close() call below may end up indirectly calling + // QCoreApplication::send{Spontaneous}Event(), which may call unknown code that + // could e.g. recurse into the class modifying d->childWindows. + const auto subWindows = d->childWindows; + for (QMdiSubWindow *child : subWindows) { if (!sanityCheck(child, "QMdiArea::closeAllSubWindows")) continue; child->close(); @@ -2034,7 +2004,11 @@ void QMdiArea::removeSubWindow(QWidget *widget) } bool found = false; - foreach (QMdiSubWindow *child, d->childWindows) { + // Take a copy because child->setWidget(nullptr) will indirectly + // QCoreApplication::sendEvent(); the latter could call unknown code that could + // e.g. recurse into the class modifying d->childWindows. + const auto subWindows = d->childWindows; + for (QMdiSubWindow *child : subWindows) { if (!sanityCheck(child, "QMdiArea::removeSubWindow")) continue; if (child->widget() == widget) { @@ -2315,10 +2289,21 @@ void QMdiArea::resizeEvent(QResizeEvent *resizeEvent) // Resize maximized views. bool hasMaximizedSubWindow = false; - foreach (QMdiSubWindow *child, d->childWindows) { + // Take a copy because child->resize() may call QCoreApplication::sendEvent() + // which may invoke unknown code, that could e.g. recurse into the class + // modifying d->childWindows. + const auto subWindows = d->childWindows; + for (QMdiSubWindow *child : subWindows) { if (sanityCheck(child, "QMdiArea::resizeEvent") && child->isMaximized() && child->size() != resizeEvent->size()) { - child->resize(resizeEvent->size()); + auto realSize = resizeEvent->size(); + const auto minSizeHint = child->minimumSizeHint(); + // QMdiSubWindow is no tlw so minimumSize() is not set by the layout manager + // and therefore we have to take care by ourself that we're not getting smaller + // than allowed + if (minSizeHint.isValid()) + realSize = realSize.expandedTo(minSizeHint); + child->resize(realSize); if (!hasMaximizedSubWindow) hasMaximizedSubWindow = true; } @@ -2367,7 +2352,9 @@ void QMdiArea::showEvent(QShowEvent *showEvent) Q_D(QMdiArea); if (!d->pendingRearrangements.isEmpty()) { bool skipPlacement = false; - foreach (Rearranger *rearranger, d->pendingRearrangements) { + // Take a copy because d->rearrange() may modify d->pendingRearrangements + const auto pendingRearrange = d->pendingRearrangements; + for (Rearranger *rearranger : pendingRearrange) { // If this is the case, we don't have to lay out pending child windows // since the rearranger will find a placement for them. if (rearranger->type() != Rearranger::IconTiler && !skipPlacement) @@ -2381,9 +2368,19 @@ void QMdiArea::showEvent(QShowEvent *showEvent) } if (!d->pendingPlacements.isEmpty()) { - foreach (QMdiSubWindow *window, d->pendingPlacements) { + // Nothing obvious in the loop body changes the container (in this code path) + // during iteration, this is calling into a non-const method that does change + // the container when called from other places. So take a copy anyway for good + // measure. + const auto copy = d->pendingPlacements; + for (QMdiSubWindow *window : copy) { if (!window) continue; + if (d->viewMode == TabbedView && window->d_func()->isActive && !d->active) { + d->showActiveWindowMaximized = true; + d->emitWindowActivated(window); // Also maximizes the window + continue; + } if (!window->testAttribute(Qt::WA_Resized)) { QSize newSize(window->sizeHint().boundedTo(viewport()->size())); window->resize(newSize.expandedTo(qSmartMinSize(window))); @@ -2517,12 +2514,16 @@ bool QMdiArea::event(QEvent *event) d->isSubWindowsTiled = true; } break; - case QEvent::WindowIconChange: - foreach (QMdiSubWindow *window, d->childWindows) { + case QEvent::WindowIconChange: { + // Take a copy because QCoreApplication::sendEvent() may call unknown code, + // that may cause recursing into the class + const auto subWindows = d->childWindows; + for (QMdiSubWindow *window : subWindows) { if (sanityCheck(window, "QMdiArea::WindowIconChange")) QCoreApplication::sendEvent(window, event); } break; + } case QEvent::Hide: d->setActive(d->active, false, false); d->setChildActivationEnabled(false); @@ -2677,7 +2678,10 @@ void QMdiArea::setupViewport(QWidget *viewport) Q_D(QMdiArea); if (viewport) viewport->setAttribute(Qt::WA_OpaquePaintEvent, d->background.isOpaque()); - foreach (QMdiSubWindow *child, d->childWindows) { + // Take a copy because the child->setParent() call below may call QCoreApplication::sendEvent() + // which may call unknown code that could e.g. recurse into the class modifying d->childWindows. + const auto subWindows = d->childWindows; + for (QMdiSubWindow *child : subWindows) { if (!sanityCheck(child, "QMdiArea::setupViewport")) continue; child->setParent(viewport, child->windowFlags()); |