/**************************************************************************** ** ** 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$ ** ****************************************************************************/ /*! \class QMdiSubWindow \brief The QMdiSubWindow class provides a subwindow class for QMdiArea. \since 4.3 \ingroup mainwindow-classes \inmodule QtWidgets QMdiSubWindow represents a top-level window in a QMdiArea, and consists of a title bar with window decorations, an internal widget, and (depending on the current style) a window frame and a size grip. QMdiSubWindow has its own layout, which consists of the title bar and a center area for the internal widget. \image qmdisubwindowlayout.png The most common way to construct a QMdiSubWindow is to call QMdiArea::addSubWindow() with the internal widget as the argument. You can also create a subwindow yourself, and set an internal widget by calling setWidget(). You use the same API when programming with subwindows as with regular top-level windows (e.g., you can call functions such as show(), hide(), showMaximized(), and setWindowTitle()). \section1 Subwindow Handling QMdiSubWindow also supports behavior specific to subwindows in an MDI area. By default, each QMdiSubWindow is visible inside the MDI area viewport when moved around, but it is also possible to specify transparent window movement and resizing behavior, where only the outline of a subwindow is updated during these operations. The setOption() function is used to enable this behavior. The isShaded() function detects whether the subwindow is currently shaded (i.e., the window is collapsed so that only the title bar is visible). To enter shaded mode, call showShaded(). QMdiSubWindow emits the windowStateChanged() signal whenever the window state has changed (e.g., when the window becomes minimized, or is restored). It also emits aboutToActivate() before it is activated. In keyboard-interactive mode, the windows are moved and resized with the keyboard. You can enter this mode through the system menu of the window. The keyboardSingleStep and keyboardPageStep properties control the distance the widget is moved or resized for each keypress event. When shift is pressed down page step is used; otherwise single step is used. You can also change the active window with the keyboard. By pressing the control and tab keys at the same time, the next (using the current \l{QMdiArea::}{WindowOrder}) subwindow will be activated. By pressing control, shift, and tab, you will activate the previous window. This is equivalent to calling \l{QMdiArea::}{activateNextSubWindow()} and \l{QMdiArea::}{activatePreviousSubWindow()}. Note that these shortcuts overrides global shortcuts, but not the \l{QMdiArea}s shortcuts. \sa QMdiArea */ /*! \enum QMdiSubWindow::SubWindowOption This enum describes options that customize the behavior of QMdiSubWindow. \omitvalue AllowOutsideAreaHorizontally \omitvalue AllowOutsideAreaVertically \value RubberBandResize If you enable this option, a rubber band control is used to represent the subwindow's outline, and the user resizes this instead of the subwindow itself. As a result, the subwindow maintains its original position and size until the resize operation has been completed, at which time it will receive a single QResizeEvent. By default, this option is disabled. \value RubberBandMove If you enable this option, a rubber band control is used to represent the subwindow's outline, and the user moves this instead of the subwindow itself. As a result, the subwindow remains in its original position until the move operation has completed, at which time a QMoveEvent is sent to the window. By default, this option is disabled. */ /*! \fn QMdiSubWindow::windowStateChanged(Qt::WindowStates oldState, Qt::WindowStates newState) QMdiSubWindow emits this signal after the window state changes. \a oldState is the window state before it changed, and \a newState is the new, current state. */ /*! \fn QMdiSubWindow::aboutToActivate() QMdiSubWindow emits this signal immediately before it is activated. After the subwindow has been activated, the QMdiArea that manages the subwindow will also emit the \l{QMdiArea::}{subWindowActivated()} signal. \sa QMdiArea::subWindowActivated() */ #include "qmdisubwindow_p.h" #ifndef QT_NO_MDIAREA #include #include #include #include #include #include #include #include #include #if defined(Q_OS_MAC) && !defined(QT_NO_STYLE_MAC) #include #endif #include #include QT_BEGIN_NAMESPACE using namespace QMdi; static const QStyle::SubControl SubControls[] = { QStyle::SC_TitleBarLabel, // 1 QStyle::SC_TitleBarSysMenu, // 2 QStyle::SC_TitleBarMinButton, // 3 QStyle::SC_TitleBarMaxButton, // 4 QStyle::SC_TitleBarShadeButton, // 5 QStyle::SC_TitleBarCloseButton, // 6 QStyle::SC_TitleBarNormalButton, // 7 QStyle::SC_TitleBarUnshadeButton, // 8 QStyle::SC_TitleBarContextHelpButton // 9 }; static const int NumSubControls = sizeof(SubControls) / sizeof(SubControls[0]); static const Qt::WindowFlags CustomizeWindowFlags = Qt::FramelessWindowHint | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowMinMaxButtonsHint; static const int BoundaryMargin = 5; static inline int getMoveDeltaComponent(uint cflags, uint moveFlag, uint resizeFlag, int delta, int maxDelta, int minDelta) { if (cflags & moveFlag) { if (delta > 0) return (cflags & resizeFlag) ? qMin(delta, maxDelta) : delta; return (cflags & resizeFlag) ? qMax(delta, minDelta) : delta; } return 0; } static inline int getResizeDeltaComponent(uint cflags, uint resizeFlag, uint resizeReverseFlag, int delta) { if (cflags & resizeFlag) { if (cflags & resizeReverseFlag) return -delta; return delta; } return 0; } static inline bool isChildOfQMdiSubWindow(const QWidget *child) { Q_ASSERT(child); QWidget *parent = child->parentWidget(); while (parent) { if (qobject_cast(parent)) return true; parent = parent->parentWidget(); } return false; } static inline bool isChildOfTabbedQMdiArea(const QMdiSubWindow *child) { Q_ASSERT(child); if (QMdiArea *mdiArea = child->mdiArea()) { if (mdiArea->viewMode() == QMdiArea::TabbedView) return true; } return false; } template static inline ControlElement *ptr(QWidget *widget) { if (widget && widget->qt_metacast("ControlElement") && strcmp(widget->metaObject()->className(), T::staticMetaObject.className()) == 0) { return static_cast *>(widget); } return 0; } QString QMdiSubWindowPrivate::originalWindowTitle() { Q_Q(QMdiSubWindow); if (originalTitle.isNull()) { originalTitle = q->window()->windowTitle(); if (originalTitle.isNull()) originalTitle = QLatin1String(""); } return originalTitle; } void QMdiSubWindowPrivate::setNewWindowTitle() { Q_Q(QMdiSubWindow); QString childTitle = q->windowTitle(); if (childTitle.isEmpty()) return; QString original = originalWindowTitle(); if (!original.isEmpty()) { if (!original.contains(QMdiSubWindow::tr("- [%1]").arg(childTitle))) q->window()->setWindowTitle(QMdiSubWindow::tr("%1 - [%2]").arg(original, childTitle)); } else { q->window()->setWindowTitle(childTitle); } } static inline bool isHoverControl(QStyle::SubControl control) { return control != QStyle::SC_None && control != QStyle::SC_TitleBarLabel; } #if 0 // Used to be included in Qt4 for Q_WS_WIN static inline QRgb colorref2qrgb(COLORREF col) { return qRgb(GetRValue(col),GetGValue(col),GetBValue(col)); } #endif #ifndef QT_NO_TOOLTIP static void showToolTip(QHelpEvent *helpEvent, QWidget *widget, const QStyleOptionComplex &opt, QStyle::ComplexControl complexControl, QStyle::SubControl subControl) { Q_ASSERT(helpEvent); Q_ASSERT(helpEvent->type() == QEvent::ToolTip); Q_ASSERT(widget); #if defined(Q_OS_MAC) && !defined(QT_NO_STYLE_MAC) // Native Mac windows don't show tool tip. if (qobject_cast(widget->style())) return; #endif // Convert CC_MdiControls to CC_TitleBar. Sub controls of different complex // controls cannot be in the same switch as they might have the same value. if (complexControl == QStyle::CC_MdiControls) { if (subControl == QStyle::SC_MdiMinButton) subControl = QStyle::SC_TitleBarMinButton; else if (subControl == QStyle::SC_MdiCloseButton) subControl = QStyle::SC_TitleBarCloseButton; else if (subControl == QStyle::SC_MdiNormalButton) subControl = QStyle::SC_TitleBarNormalButton; else subControl = QStyle::SC_None; } // Don't change the tooltip for the base widget itself. if (subControl == QStyle::SC_None) return; QString toolTip; switch (subControl) { case QStyle::SC_TitleBarMinButton: toolTip = QMdiSubWindow::tr("Minimize"); break; case QStyle::SC_TitleBarMaxButton: toolTip = QMdiSubWindow::tr("Maximize"); break; case QStyle::SC_TitleBarUnshadeButton: toolTip = QMdiSubWindow::tr("Unshade"); break; case QStyle::SC_TitleBarShadeButton: toolTip = QMdiSubWindow::tr("Shade"); break; case QStyle::SC_TitleBarNormalButton: if (widget->isMaximized() || !qobject_cast(widget)) toolTip = QMdiSubWindow::tr("Restore Down"); else toolTip = QMdiSubWindow::tr("Restore"); break; case QStyle::SC_TitleBarCloseButton: toolTip = QMdiSubWindow::tr("Close"); break; case QStyle::SC_TitleBarContextHelpButton: toolTip = QMdiSubWindow::tr("Help"); break; case QStyle::SC_TitleBarSysMenu: toolTip = QMdiSubWindow::tr("Menu"); break; default: break; } const QRect rect = widget->style()->subControlRect(complexControl, &opt, subControl, widget); QToolTip::showText(helpEvent->globalPos(), toolTip, widget, rect); } #endif // QT_NO_TOOLTIP namespace QMdi { /* \class ControlLabel \internal */ class ControlLabel : public QWidget { Q_OBJECT public: ControlLabel(QMdiSubWindow *subWindow, QWidget *parent = 0); QSize sizeHint() const Q_DECL_OVERRIDE; signals: void _q_clicked(); void _q_doubleClicked(); protected: bool event(QEvent *event) Q_DECL_OVERRIDE; void paintEvent(QPaintEvent *paintEvent) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent *mouseEvent) Q_DECL_OVERRIDE; void mouseDoubleClickEvent(QMouseEvent *mouseEvent) Q_DECL_OVERRIDE; void mouseReleaseEvent(QMouseEvent *mouseEvent) Q_DECL_OVERRIDE; private: QPixmap label; bool isPressed; void updateWindowIcon(); }; } // namespace QMdi ControlLabel::ControlLabel(QMdiSubWindow *subWindow, QWidget *parent) : QWidget(parent), isPressed(false) { Q_UNUSED(subWindow); setFocusPolicy(Qt::NoFocus); updateWindowIcon(); setFixedSize(label.size()); } /* \internal */ QSize ControlLabel::sizeHint() const { return label.size(); } /* \internal */ bool ControlLabel::event(QEvent *event) { if (event->type() == QEvent::WindowIconChange) updateWindowIcon(); else if (event->type() == QEvent::StyleChange) { updateWindowIcon(); setFixedSize(label.size()); } #ifndef QT_NO_TOOLTIP else if (event->type() == QEvent::ToolTip) { QStyleOptionTitleBar options; options.initFrom(this); showToolTip(static_cast(event), this, options, QStyle::CC_TitleBar, QStyle::SC_TitleBarSysMenu); } #endif return QWidget::event(event); } /* \internal */ void ControlLabel::paintEvent(QPaintEvent * /*paintEvent*/) { QPainter painter(this); painter.drawPixmap(0, 0, label); } /* \internal */ void ControlLabel::mousePressEvent(QMouseEvent *mouseEvent) { if (mouseEvent->button() != Qt::LeftButton) { mouseEvent->ignore(); return; } isPressed = true; } /* \internal */ void ControlLabel::mouseDoubleClickEvent(QMouseEvent *mouseEvent) { if (mouseEvent->button() != Qt::LeftButton) { mouseEvent->ignore(); return; } isPressed = false; emit _q_doubleClicked(); } /* \internal */ void ControlLabel::mouseReleaseEvent(QMouseEvent *mouseEvent) { if (mouseEvent->button() != Qt::LeftButton) { mouseEvent->ignore(); return; } if (isPressed) { isPressed = false; emit _q_clicked(); } } /* \internal */ void ControlLabel::updateWindowIcon() { QIcon menuIcon = windowIcon(); if (menuIcon.isNull()) menuIcon = style()->standardIcon(QStyle::SP_TitleBarMenuButton, 0, parentWidget()); const int iconSize = style()->pixelMetric(QStyle::PM_TitleBarButtonIconSize, 0, parentWidget()); label = menuIcon.pixmap(iconSize); update(); } namespace QMdi { /* \class ControllerWidget \internal */ class ControllerWidget : public QWidget { Q_OBJECT public: ControllerWidget(QMdiSubWindow *subWindow, QWidget *parent = 0); QSize sizeHint() const Q_DECL_OVERRIDE; void setControlVisible(QMdiSubWindowPrivate::WindowStateAction action, bool visible); inline bool hasVisibleControls() const { return (visibleControls & QStyle::SC_MdiMinButton) || (visibleControls & QStyle::SC_MdiNormalButton) || (visibleControls & QStyle::SC_MdiCloseButton); } signals: void _q_minimize(); void _q_restore(); void _q_close(); protected: void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void leaveEvent(QEvent *event) Q_DECL_OVERRIDE; bool event(QEvent *event) Q_DECL_OVERRIDE; private: QStyle::SubControl activeControl; QStyle::SubControl hoverControl; QStyle::SubControls visibleControls; void initStyleOption(QStyleOptionComplex *option) const; QMdiArea *mdiArea; inline QStyle::SubControl getSubControl(const QPoint &pos) const { QStyleOptionComplex opt; initStyleOption(&opt); return style()->hitTestComplexControl(QStyle::CC_MdiControls, &opt, pos, mdiArea); } }; } // namespace QMdi /* \internal */ ControllerWidget::ControllerWidget(QMdiSubWindow *subWindow, QWidget *parent) : QWidget(parent), activeControl(QStyle::SC_None), hoverControl(QStyle::SC_None), visibleControls(QStyle::SC_None), mdiArea(0) { if (subWindow->parentWidget()) mdiArea = qobject_cast(subWindow->parentWidget()->parentWidget()); setFocusPolicy(Qt::NoFocus); setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setMouseTracking(true); } /* \internal */ QSize ControllerWidget::sizeHint() const { ensurePolished(); QStyleOptionComplex opt; initStyleOption(&opt); const int buttonSize = style()->pixelMetric(QStyle::PM_TitleBarButtonSize, &opt, mdiArea); QSize size(3 * buttonSize, buttonSize); return style()->sizeFromContents(QStyle::CT_MdiControls, &opt, size, mdiArea); } void ControllerWidget::setControlVisible(QMdiSubWindowPrivate::WindowStateAction action, bool visible) { QStyle::SubControl subControl = QStyle::SC_None; // Map action from QMdiSubWindowPrivate::WindowStateAction to QStyle::SubControl. if (action == QMdiSubWindowPrivate::MaximizeAction) subControl = QStyle::SC_MdiNormalButton; else if (action == QMdiSubWindowPrivate::CloseAction) subControl = QStyle::SC_MdiCloseButton; else if (action == QMdiSubWindowPrivate::MinimizeAction) subControl = QStyle::SC_MdiMinButton; if (subControl == QStyle::SC_None) return; visibleControls.setFlag(subControl, visible && !(visibleControls & subControl)); } /* \internal */ void ControllerWidget::paintEvent(QPaintEvent * /*paintEvent*/) { QStyleOptionComplex opt; initStyleOption(&opt); if (activeControl == hoverControl) { opt.activeSubControls = activeControl; opt.state |= QStyle::State_Sunken; } else if (hoverControl != QStyle::SC_None && (activeControl == QStyle::SC_None)) { opt.activeSubControls = hoverControl; opt.state |= QStyle::State_MouseOver; } QPainter painter(this); style()->drawComplexControl(QStyle::CC_MdiControls, &opt, &painter, mdiArea); } /* \internal */ void ControllerWidget::mousePressEvent(QMouseEvent *event) { if (event->button() != Qt::LeftButton) { event->ignore(); return; } activeControl = getSubControl(event->pos()); update(); } /* \internal */ void ControllerWidget::mouseReleaseEvent(QMouseEvent *event) { if (event->button() != Qt::LeftButton) { event->ignore(); return; } QStyle::SubControl under_mouse = getSubControl(event->pos()); if (under_mouse == activeControl) { switch (activeControl) { case QStyle::SC_MdiCloseButton: emit _q_close(); break; case QStyle::SC_MdiNormalButton: emit _q_restore(); break; case QStyle::SC_MdiMinButton: emit _q_minimize(); break; default: break; } } activeControl = QStyle::SC_None; update(); } /* \internal */ void ControllerWidget::mouseMoveEvent(QMouseEvent *event) { QStyle::SubControl under_mouse = getSubControl(event->pos()); //test if hover state changes if (hoverControl != under_mouse) { hoverControl = under_mouse; update(); } } /* \internal */ void ControllerWidget::leaveEvent(QEvent * /*event*/) { hoverControl = QStyle::SC_None; update(); } /* \internal */ bool ControllerWidget::event(QEvent *event) { #ifndef QT_NO_TOOLTIP if (event->type() == QEvent::ToolTip) { QStyleOptionComplex opt; initStyleOption(&opt); QHelpEvent *helpEvent = static_cast(event); showToolTip(helpEvent, this, opt, QStyle::CC_MdiControls, getSubControl(helpEvent->pos())); } #endif // QT_NO_TOOLTIP return QWidget::event(event); } /* \internal */ void ControllerWidget::initStyleOption(QStyleOptionComplex *option) const { option->initFrom(this); option->subControls = visibleControls; option->activeSubControls = QStyle::SC_None; } /* \internal */ ControlContainer::ControlContainer(QMdiSubWindow *mdiChild) : QObject(mdiChild), previousLeft(0), previousRight(0), #ifndef QT_NO_MENUBAR m_menuBar(0), #endif mdiChild(mdiChild) { Q_ASSERT(mdiChild); m_controllerWidget = new ControlElement(mdiChild); connect(m_controllerWidget, SIGNAL(_q_close()), mdiChild, SLOT(close())); connect(m_controllerWidget, SIGNAL(_q_restore()), mdiChild, SLOT(showNormal())); connect(m_controllerWidget, SIGNAL(_q_minimize()), mdiChild, SLOT(showMinimized())); m_menuLabel = new ControlElement(mdiChild); m_menuLabel->setWindowIcon(mdiChild->windowIcon()); #ifndef QT_NO_MENU connect(m_menuLabel, SIGNAL(_q_clicked()), mdiChild, SLOT(showSystemMenu())); #endif connect(m_menuLabel, SIGNAL(_q_doubleClicked()), mdiChild, SLOT(close())); } ControlContainer::~ControlContainer() { #ifndef QT_NO_MENUBAR removeButtonsFromMenuBar(); #endif delete m_menuLabel; m_menuLabel = 0; delete m_controllerWidget; m_controllerWidget = 0; } #ifndef QT_NO_MENUBAR /* \internal */ QMenuBar *QMdiSubWindowPrivate::menuBar() const { #if defined(QT_NO_MAINWINDOW) return 0; #else Q_Q(const QMdiSubWindow); if (!q->isMaximized() || drawTitleBarWhenMaximized() || isChildOfTabbedQMdiArea(q)) return 0; if (QMainWindow *mainWindow = qobject_cast(q->window())) return mainWindow->menuBar(); return 0; #endif } /* \internal */ void ControlContainer::showButtonsInMenuBar(QMenuBar *menuBar) { if (!menuBar || !mdiChild || mdiChild->windowFlags() & Qt::FramelessWindowHint) return; m_menuBar = menuBar; if (m_menuLabel && mdiChild->windowFlags() & Qt::WindowSystemMenuHint) { QWidget *currentLeft = menuBar->cornerWidget(Qt::TopLeftCorner); if (currentLeft) currentLeft->hide(); if (currentLeft != m_menuLabel) { menuBar->setCornerWidget(m_menuLabel, Qt::TopLeftCorner); previousLeft = currentLeft; } m_menuLabel->show(); } ControllerWidget *controllerWidget = qobject_cast(m_controllerWidget); if (controllerWidget && controllerWidget->hasVisibleControls()) { QWidget *currentRight = menuBar->cornerWidget(Qt::TopRightCorner); if (currentRight) currentRight->hide(); if (currentRight != m_controllerWidget) { menuBar->setCornerWidget(m_controllerWidget, Qt::TopRightCorner); previousRight = currentRight; } m_controllerWidget->show(); } mdiChild->d_func()->setNewWindowTitle(); } /* \internal */ void ControlContainer::removeButtonsFromMenuBar(QMenuBar *menuBar) { if (menuBar && menuBar != m_menuBar) { // m_menubar was deleted while sub-window was maximized previousRight = 0; previousLeft = 0; m_menuBar = menuBar; } if (!m_menuBar || !mdiChild || qt_widget_private(mdiChild->window())->data.in_destructor) return; QMdiSubWindow *child = 0; if (m_controllerWidget) { QWidget *currentRight = m_menuBar->cornerWidget(Qt::TopRightCorner); if (currentRight == m_controllerWidget) { if (ControlElement *ce = ptr(previousRight)) { if (!ce->mdiChild || !ce->mdiChild->isMaximized()) previousRight = 0; else child = ce->mdiChild; } m_menuBar->setCornerWidget(previousRight, Qt::TopRightCorner); if (previousRight) { previousRight->show(); previousRight = 0; } } m_controllerWidget->hide(); m_controllerWidget->setParent(0); } if (m_menuLabel) { QWidget *currentLeft = m_menuBar->cornerWidget(Qt::TopLeftCorner); if (currentLeft == m_menuLabel) { if (ControlElement *ce = ptr(previousLeft)) { if (!ce->mdiChild || !ce->mdiChild->isMaximized()) previousLeft = 0; else if (!child) child = mdiChild; } m_menuBar->setCornerWidget(previousLeft, Qt::TopLeftCorner); if (previousLeft) { previousLeft->show(); previousLeft = 0; } } m_menuLabel->hide(); m_menuLabel->setParent(0); } m_menuBar->update(); if (child) child->d_func()->setNewWindowTitle(); else if (mdiChild) mdiChild->window()->setWindowTitle(mdiChild->d_func()->originalWindowTitle()); } #endif // QT_NO_MENUBAR void ControlContainer::updateWindowIcon(const QIcon &windowIcon) { if (m_menuLabel) m_menuLabel->setWindowIcon(windowIcon); } /*! \internal */ QMdiSubWindowPrivate::QMdiSubWindowPrivate() : baseWidget(0), restoreFocusWidget(0), controlContainer(0), #ifndef QT_NO_SIZEGRIP sizeGrip(0), #endif #ifndef QT_NO_RUBBERBAND rubberBand(0), #endif userMinimumSize(0,0), resizeEnabled(true), moveEnabled(true), isInInteractiveMode(false), #ifndef QT_NO_RUBBERBAND isInRubberBandMode(false), #endif isShadeMode(false), ignoreWindowTitleChange(false), ignoreNextActivationEvent(false), activationEnabled(true), isShadeRequestFromMinimizeMode(false), isMaximizeMode(false), isWidgetHiddenByUs(false), isActive(false), isExplicitlyDeactivated(false), keyboardSingleStep(5), keyboardPageStep(20), resizeTimerId(-1), currentOperation(None), hoveredSubControl(QStyle::SC_None), activeSubControl(QStyle::SC_None), focusInReason(Qt::ActiveWindowFocusReason) { initOperationMap(); } /*! \internal */ void QMdiSubWindowPrivate::_q_updateStaysOnTopHint() { #ifndef QT_NO_ACTION Q_Q(QMdiSubWindow); if (QAction *senderAction = qobject_cast(q->sender())) { if (senderAction->isChecked()) { q->setWindowFlags(q->windowFlags() | Qt::WindowStaysOnTopHint); q->raise(); } else { q->setWindowFlags(q->windowFlags() & ~Qt::WindowStaysOnTopHint); q->lower(); } } #endif // QT_NO_ACTION } /*! \internal */ void QMdiSubWindowPrivate::_q_enterInteractiveMode() { #ifndef QT_NO_ACTION Q_Q(QMdiSubWindow); QAction *action = qobject_cast(q->sender()); if (!action) return; QPoint pressPos; if (actions[MoveAction] && actions[MoveAction] == action) { currentOperation = Move; pressPos = QPoint(q->width() / 2, titleBarHeight() - 1); } else if (actions[ResizeAction] && actions[ResizeAction] == action) { currentOperation = q->isLeftToRight() ? BottomRightResize : BottomLeftResize; int offset = q->style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, 0, q) / 2; int x = q->isLeftToRight() ? q->width() - offset : offset; pressPos = QPoint(x, q->height() - offset); } else { return; } updateCursor(); #ifndef QT_NO_CURSOR q->cursor().setPos(q->mapToGlobal(pressPos)); #endif mousePressPosition = q->mapToParent(pressPos); oldGeometry = q->geometry(); isInInteractiveMode = true; q->setFocus(); #ifndef QT_NO_RUBBERBAND if ((q->testOption(QMdiSubWindow::RubberBandResize) && (currentOperation == BottomRightResize || currentOperation == BottomLeftResize)) || (q->testOption(QMdiSubWindow::RubberBandMove) && currentOperation == Move)) { enterRubberBandMode(); } else #endif // QT_NO_RUBBERBAND { q->grabMouse(); } #endif // QT_NO_ACTION } /*! \internal */ void QMdiSubWindowPrivate::_q_processFocusChanged(QWidget *old, QWidget *now) { Q_UNUSED(old); Q_Q(QMdiSubWindow); if (now && (now == q || q->isAncestorOf(now))) { if (now == q && !isInInteractiveMode) setFocusWidget(); setActive(true); } } /*! \internal */ void QMdiSubWindowPrivate::leaveInteractiveMode() { Q_Q(QMdiSubWindow); #ifndef QT_NO_RUBBERBAND if (isInRubberBandMode) leaveRubberBandMode(); else #endif q->releaseMouse(); isInInteractiveMode = false; currentOperation = None; updateDirtyRegions(); updateCursor(); if (baseWidget && baseWidget->focusWidget()) baseWidget->focusWidget()->setFocus(); } /*! \internal */ void QMdiSubWindowPrivate::removeBaseWidget() { if (!baseWidget) return; Q_Q(QMdiSubWindow); baseWidget->removeEventFilter(q); if (layout) layout->removeWidget(baseWidget); if (baseWidget->windowTitle() == q->windowTitle()) { ignoreWindowTitleChange = true; q->setWindowTitle(QString()); ignoreWindowTitleChange = false; q->setWindowModified(false); } lastChildWindowTitle.clear(); // QTBUG-47993: parent widget can be reset before this call if (baseWidget->parentWidget() == q) baseWidget->setParent(0); baseWidget = 0; isWidgetHiddenByUs = false; } /*! \internal */ void QMdiSubWindowPrivate::initOperationMap() { operationMap.insert(Move, OperationInfo(HMove | VMove, Qt::ArrowCursor, false)); operationMap.insert(TopResize, OperationInfo(VMove | VResize | VResizeReverse, Qt::SizeVerCursor)); operationMap.insert(BottomResize, OperationInfo(VResize, Qt::SizeVerCursor)); operationMap.insert(LeftResize, OperationInfo(HMove | HResize | HResizeReverse, Qt::SizeHorCursor)); operationMap.insert(RightResize, OperationInfo(HResize, Qt::SizeHorCursor)); operationMap.insert(TopLeftResize, OperationInfo(HMove | VMove | HResize | VResize | VResizeReverse | HResizeReverse, Qt::SizeFDiagCursor)); operationMap.insert(TopRightResize, OperationInfo(VMove | HResize | VResize | VResizeReverse, Qt::SizeBDiagCursor)); operationMap.insert(BottomLeftResize, OperationInfo(HMove | HResize | VResize | HResizeReverse, Qt::SizeBDiagCursor)); operationMap.insert(BottomRightResize, OperationInfo(HResize | VResize, Qt::SizeFDiagCursor)); } #ifndef QT_NO_MENU /*! \internal */ void QMdiSubWindowPrivate::createSystemMenu() { Q_Q(QMdiSubWindow); Q_ASSERT_X(q, "QMdiSubWindowPrivate::createSystemMenu", "You can NOT call this function before QMdiSubWindow's ctor"); systemMenu = new QMenu(q); systemMenu->installEventFilter(q); const QStyle *style = q->style(); addToSystemMenu(RestoreAction, QMdiSubWindow::tr("&Restore"), SLOT(showNormal())); actions[RestoreAction]->setIcon(style->standardIcon(QStyle::SP_TitleBarNormalButton, 0, q)); actions[RestoreAction]->setEnabled(false); addToSystemMenu(MoveAction, QMdiSubWindow::tr("&Move"), SLOT(_q_enterInteractiveMode())); addToSystemMenu(ResizeAction, QMdiSubWindow::tr("&Size"), SLOT(_q_enterInteractiveMode())); addToSystemMenu(MinimizeAction, QMdiSubWindow::tr("Mi&nimize"), SLOT(showMinimized())); actions[MinimizeAction]->setIcon(style->standardIcon(QStyle::SP_TitleBarMinButton, 0, q)); addToSystemMenu(MaximizeAction, QMdiSubWindow::tr("Ma&ximize"), SLOT(showMaximized())); actions[MaximizeAction]->setIcon(style->standardIcon(QStyle::SP_TitleBarMaxButton, 0, q)); addToSystemMenu(StayOnTopAction, QMdiSubWindow::tr("Stay on &Top"), SLOT(_q_updateStaysOnTopHint())); actions[StayOnTopAction]->setCheckable(true); systemMenu->addSeparator(); addToSystemMenu(CloseAction, QMdiSubWindow::tr("&Close"), SLOT(close())); actions[CloseAction]->setIcon(style->standardIcon(QStyle::SP_TitleBarCloseButton, 0, q)); #if !defined(QT_NO_SHORTCUT) actions[CloseAction]->setShortcuts(QKeySequence::Close); #endif updateActions(); } #endif /*! \internal */ void QMdiSubWindowPrivate::updateCursor() { #ifndef QT_NO_CURSOR Q_Q(QMdiSubWindow); #if defined(Q_OS_MAC) && !defined(QT_NO_STYLE_MAC) if (qobject_cast(q->style())) return; #endif if (currentOperation == None) { q->unsetCursor(); return; } if (currentOperation == Move || operationMap.find(currentOperation).value().hover) { q->setCursor(operationMap.find(currentOperation).value().cursorShape); return; } #endif } /*! \internal */ void QMdiSubWindowPrivate::updateDirtyRegions() { // No update necessary if (!parent) return; for (OperationInfoMap::iterator it = operationMap.begin(), end = operationMap.end(); it != end; ++it) it.value().region = getRegion(it.key()); } /*! \internal */ void QMdiSubWindowPrivate::updateGeometryConstraints() { Q_Q(QMdiSubWindow); if (!parent) return; internalMinimumSize = (!q->isMinimized() && !q->minimumSize().isNull()) ? q->minimumSize() : q->minimumSizeHint(); int margin, minWidth; sizeParameters(&margin, &minWidth); q->setContentsMargins(margin, titleBarHeight(), margin, margin); if (q->isMaximized() || (q->isMinimized() && !q->isShaded())) { moveEnabled = false; resizeEnabled = false; } else { moveEnabled = true; if ((q->windowFlags() & Qt::MSWindowsFixedSizeDialogHint) || q->isShaded()) resizeEnabled = false; else resizeEnabled = true; } updateDirtyRegions(); } /*! \internal */ void QMdiSubWindowPrivate::updateMask() { Q_Q(QMdiSubWindow); if (!q->mask().isEmpty()) q->clearMask(); if (!parent) return; if ((q->isMaximized() && !drawTitleBarWhenMaximized()) || q->windowFlags() & Qt::FramelessWindowHint) return; if (resizeTimerId == -1) cachedStyleOptions = titleBarOptions(); cachedStyleOptions.rect = q->rect(); QStyleHintReturnMask frameMask; q->style()->styleHint(QStyle::SH_WindowFrame_Mask, &cachedStyleOptions, q, &frameMask); if (!frameMask.region.isEmpty()) q->setMask(frameMask.region); } /*! \internal */ void QMdiSubWindowPrivate::setNewGeometry(const QPoint &pos) { Q_Q(QMdiSubWindow); Q_ASSERT(currentOperation != None); Q_ASSERT(parent); uint cflags = operationMap.find(currentOperation).value().changeFlags; int posX = pos.x(); int posY = pos.y(); const bool restrictHorizontal = !q->testOption(QMdiSubWindow::AllowOutsideAreaHorizontally); const bool restrictVertical = !q->testOption(QMdiSubWindow::AllowOutsideAreaVertically); if (restrictHorizontal || restrictVertical) { QRect parentRect = q->parentWidget()->rect(); if (restrictVertical && (cflags & VResizeReverse || currentOperation == Move)) { posY = qMin(qMax(mousePressPosition.y() - oldGeometry.y(), posY), parentRect.height() - BoundaryMargin); } if (currentOperation == Move) { if (restrictHorizontal) posX = qMin(qMax(BoundaryMargin, posX), parentRect.width() - BoundaryMargin); if (restrictVertical) posY = qMin(posY, parentRect.height() - BoundaryMargin); } else { if (restrictHorizontal) { if (cflags & HResizeReverse) posX = qMax(mousePressPosition.x() - oldGeometry.x(), posX); else posX = qMin(parentRect.width() - (oldGeometry.x() + oldGeometry.width() - mousePressPosition.x()), posX); } if (restrictVertical && !(cflags & VResizeReverse)) { posY = qMin(parentRect.height() - (oldGeometry.y() + oldGeometry.height() - mousePressPosition.y()), posY); } } } QRect geometry; if (cflags & (HMove | VMove)) { int dx = getMoveDeltaComponent(cflags, HMove, HResize, posX - mousePressPosition.x(), oldGeometry.width() - internalMinimumSize.width(), oldGeometry.width() - q->maximumWidth()); int dy = getMoveDeltaComponent(cflags, VMove, VResize, posY - mousePressPosition.y(), oldGeometry.height() - internalMinimumSize.height(), oldGeometry.height() - q->maximumHeight()); geometry.setTopLeft(oldGeometry.topLeft() + QPoint(dx, dy)); } else { geometry.setTopLeft(q->geometry().topLeft()); } if (cflags & (HResize | VResize)) { int dx = getResizeDeltaComponent(cflags, HResize, HResizeReverse, posX - mousePressPosition.x()); int dy = getResizeDeltaComponent(cflags, VResize, VResizeReverse, posY - mousePressPosition.y()); geometry.setSize(oldGeometry.size() + QSize(dx, dy)); } else { geometry.setSize(q->geometry().size()); } setNewGeometry(&geometry); } /*! \internal */ void QMdiSubWindowPrivate::setMinimizeMode() { Q_Q(QMdiSubWindow); Q_ASSERT(parent); ensureWindowState(Qt::WindowMinimized); isShadeRequestFromMinimizeMode = true; q->showShaded(); isShadeRequestFromMinimizeMode = false; moveEnabled = false; #ifndef QT_NO_ACTION setEnabled(MoveAction, moveEnabled); #endif Q_ASSERT(q->windowState() & Qt::WindowMinimized); Q_ASSERT(!(q->windowState() & Qt::WindowMaximized)); // This should be a valid assert, but people can actually re-implement // setVisible and do crazy stuff, so we're not guaranteed that // the widget is hidden after calling hide(). // Q_ASSERT(baseWidget ? baseWidget->isHidden() : true); setActive(true); } /*! \internal */ void QMdiSubWindowPrivate::setNormalMode() { Q_Q(QMdiSubWindow); Q_ASSERT(parent); isShadeMode = false; isMaximizeMode = false; ensureWindowState(Qt::WindowNoState); #ifndef QT_NO_MENUBAR removeButtonsFromMenuBar(); #endif // Hide the window before we change the geometry to avoid multiple resize // events and wrong window state. const bool wasVisible = q->isVisible(); if (wasVisible) q->setVisible(false); // Restore minimum size if set by user. if (!userMinimumSize.isNull()) { q->setMinimumSize(userMinimumSize); userMinimumSize = QSize(0, 0); } // Show the internal widget if it was hidden by us, if (baseWidget && isWidgetHiddenByUs) { baseWidget->show(); isWidgetHiddenByUs = false; } updateGeometryConstraints(); QRect newGeometry = oldGeometry; newGeometry.setSize(restoreSize.expandedTo(internalMinimumSize)); q->setGeometry(newGeometry); if (wasVisible) q->setVisible(true); // Invalidate the restore size. restoreSize.setWidth(-1); restoreSize.setHeight(-1); #ifndef QT_NO_SIZEGRIP setSizeGripVisible(true); #endif #ifndef QT_NO_ACTION setEnabled(MoveAction, true); setEnabled(MaximizeAction, true); setEnabled(MinimizeAction, true); setEnabled(RestoreAction, false); setEnabled(ResizeAction, resizeEnabled); #endif // QT_NO_ACTION Q_ASSERT(!(q_func()->windowState() & Qt::WindowMinimized)); // This sub-window can be maximized when shown above if not the // QMdiArea::DontMaximizeSubWindowOnActionvation is set. Make sure // the Qt::WindowMaximized flag is set accordingly. Q_ASSERT((isMaximizeMode && q_func()->windowState() & Qt::WindowMaximized) || (!isMaximizeMode && !(q_func()->windowState() & Qt::WindowMaximized))); Q_ASSERT(!isShadeMode); setActive(true); restoreFocus(); updateMask(); } inline void QMdiSubWindowPrivate::storeFocusWidget() { if (QWidget *focus = QApplication::focusWidget()) { if (!restoreFocusWidget && q_func()->isAncestorOf(focus)) restoreFocusWidget = focus; } } /*! \internal */ void QMdiSubWindowPrivate::setMaximizeMode() { Q_Q(QMdiSubWindow); Q_ASSERT(parent); ensureWindowState(Qt::WindowMaximized); isShadeMode = false; isMaximizeMode = true; storeFocusWidget(); #ifndef QT_NO_SIZEGRIP setSizeGripVisible(false); #endif // Store old geometry and set restore size if not already set. if (!restoreSize.isValid()) { oldGeometry = q->geometry(); restoreSize.setWidth(oldGeometry.width()); restoreSize.setHeight(oldGeometry.height()); } // Hide the window before we change the geometry to avoid multiple resize // events and wrong window state. const bool wasVisible = q->isVisible(); if (wasVisible) q->setVisible(false); // Show the internal widget if it was hidden by us. if (baseWidget && isWidgetHiddenByUs) { baseWidget->show(); isWidgetHiddenByUs = false; } updateGeometryConstraints(); if (wasVisible) { #ifndef QT_NO_MENUBAR if (QMenuBar *mBar = menuBar()) showButtonsInMenuBar(mBar); else #endif if (!controlContainer) controlContainer = new ControlContainer(q); } QWidget *parent = q->parentWidget(); QRect availableRect = parent->contentsRect(); // Adjust geometry if the sub-window is inside a scroll area. QAbstractScrollArea *scrollArea = qobject_cast(parent->parentWidget()); if (scrollArea && scrollArea->viewport() == parent) { QScrollBar *hbar = scrollArea->horizontalScrollBar(); QScrollBar *vbar = scrollArea->verticalScrollBar(); const int xOffset = hbar ? hbar->value() : 0; const int yOffset = vbar ? vbar->value() : 0; availableRect.adjust(-xOffset, -yOffset, -xOffset, -yOffset); oldGeometry.adjust(xOffset, yOffset, xOffset, yOffset); } setNewGeometry(&availableRect); // QWidget::setGeometry will reset Qt::WindowMaximized so we have to update it here. ensureWindowState(Qt::WindowMaximized); if (wasVisible) q->setVisible(true); resizeEnabled = false; moveEnabled = false; #ifndef QT_NO_ACTION setEnabled(MoveAction, moveEnabled); setEnabled(MaximizeAction, false); setEnabled(MinimizeAction, true); setEnabled(RestoreAction, true); setEnabled(ResizeAction, resizeEnabled); #endif // QT_NO_ACTION Q_ASSERT(q->windowState() & Qt::WindowMaximized); Q_ASSERT(!(q->windowState() & Qt::WindowMinimized)); restoreFocus(); updateMask(); } /*! \internal */ void QMdiSubWindowPrivate::setActive(bool activate, bool changeFocus) { Q_Q(QMdiSubWindow); if (!parent || !activationEnabled) return; if (activate && !isActive && q->isEnabled()) { isActive = true; isExplicitlyDeactivated = false; Qt::WindowStates oldWindowState = q->windowState(); ensureWindowState(Qt::WindowActive); emit q->aboutToActivate(); #ifndef QT_NO_MENUBAR if (QMenuBar *mBar = menuBar()) showButtonsInMenuBar(mBar); #endif Q_ASSERT(isActive); emit q->windowStateChanged(oldWindowState, q->windowState()); } else if (!activate && isActive) { isActive = false; Qt::WindowStates oldWindowState = q->windowState(); q->overrideWindowState(q->windowState() & ~Qt::WindowActive); if (changeFocus) { storeFocusWidget(); QWidget *focusWidget = QApplication::focusWidget(); if (focusWidget && (focusWidget == q || q->isAncestorOf(focusWidget))) focusWidget->clearFocus(); } if (baseWidget) baseWidget->overrideWindowState(baseWidget->windowState() & ~Qt::WindowActive); Q_ASSERT(!isActive); emit q->windowStateChanged(oldWindowState, q->windowState()); } if (activate && isActive && q->isEnabled() && !q->hasFocus() && !q->isAncestorOf(QApplication::focusWidget())) { if (changeFocus) setFocusWidget(); ensureWindowState(Qt::WindowActive); } int frameWidth = q->style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, 0, q); int titleBarHeight = this->titleBarHeight(); QRegion windowDecoration = QRegion(0, 0, q->width(), q->height()); windowDecoration -= QRegion(frameWidth, titleBarHeight, q->width() - 2 * frameWidth, q->height() - titleBarHeight - frameWidth); // Make sure we don't use cached style options if we get // resize events right before activation/deactivation. if (resizeTimerId != -1) { q->killTimer(resizeTimerId); resizeTimerId = -1; updateDirtyRegions(); } q->update(windowDecoration); } /*! \internal */ void QMdiSubWindowPrivate::processClickedSubControl() { Q_Q(QMdiSubWindow); switch (activeSubControl) { case QStyle::SC_TitleBarContextHelpButton: #ifndef QT_NO_WHATSTHIS QWhatsThis::enterWhatsThisMode(); #endif break; case QStyle::SC_TitleBarShadeButton: q->showShaded(); hoveredSubControl = QStyle::SC_TitleBarUnshadeButton; break; case QStyle::SC_TitleBarUnshadeButton: if (q->isShaded()) hoveredSubControl = QStyle::SC_TitleBarShadeButton; q->showNormal(); break; case QStyle::SC_TitleBarMinButton: #if defined(Q_OS_MAC) && !defined(QT_NO_STYLE_MAC) if (qobject_cast(q->style())) { if (q->isMinimized()) q->showNormal(); else q->showMinimized(); break; } #endif q->showMinimized(); break; case QStyle::SC_TitleBarNormalButton: if (q->isShaded()) hoveredSubControl = QStyle::SC_TitleBarMinButton; q->showNormal(); break; case QStyle::SC_TitleBarMaxButton: #if defined(Q_OS_MAC) && !defined(QT_NO_STYLE_MAC) if (qobject_cast(q->style())) { if (q->isMaximized()) q->showNormal(); else q->showMaximized(); break; } #endif q->showMaximized(); break; case QStyle::SC_TitleBarCloseButton: q->close(); break; default: break; } } /*! \internal */ QRegion QMdiSubWindowPrivate::getRegion(Operation operation) const { Q_Q(const QMdiSubWindow); int width = q->width(); int height = q->height(); int titleBarHeight = this->titleBarHeight(); int frameWidth = q->style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, 0, q); int cornerConst = titleBarHeight - frameWidth; int titleBarConst = 2 * titleBarHeight; if (operation == Move) { QStyleOptionTitleBar titleBarOptions = this->titleBarOptions(); QRegion move(frameWidth, frameWidth, width - 2 * frameWidth, cornerConst); // Depending on which window flags are set, activated sub controllers will // be subtracted from the 'move' region. for (int i = 0; i < NumSubControls; ++i) { if (SubControls[i] == QStyle::SC_TitleBarLabel) continue; move -= QRegion(q->style()->subControlRect(QStyle::CC_TitleBar, &titleBarOptions, SubControls[i])); } return move; } QRegion region; #if defined(Q_OS_MAC) && !defined(QT_NO_STYLE_MAC) if (qobject_cast(q->style())) return region; #endif switch (operation) { case TopResize: region = QRegion(titleBarHeight, 0, width - titleBarConst, frameWidth); break; case BottomResize: region = QRegion(titleBarHeight, height - frameWidth, width - titleBarConst, frameWidth); break; case LeftResize: region = QRegion(0, titleBarHeight, frameWidth, height - titleBarConst); break; case RightResize: region = QRegion(width - frameWidth, titleBarHeight, frameWidth, height - titleBarConst); break; case TopLeftResize: region = QRegion(0, 0, titleBarHeight, titleBarHeight) - QRegion(frameWidth, frameWidth, cornerConst, cornerConst); break; case TopRightResize: region = QRegion(width - titleBarHeight, 0, titleBarHeight, titleBarHeight) - QRegion(width - titleBarHeight, frameWidth, cornerConst, cornerConst); break; case BottomLeftResize: region = QRegion(0, height - titleBarHeight, titleBarHeight, titleBarHeight) - QRegion(frameWidth, height - titleBarHeight, cornerConst, cornerConst); break; case BottomRightResize: region = QRegion(width - titleBarHeight, height - titleBarHeight, titleBarHeight, titleBarHeight) - QRegion(width - titleBarHeight, height - titleBarHeight, cornerConst, cornerConst); break; default: break; } return region; } /*! \internal */ QMdiSubWindowPrivate::Operation QMdiSubWindowPrivate::getOperation(const QPoint &pos) const { OperationInfoMap::const_iterator it; for (it = operationMap.constBegin(); it != operationMap.constEnd(); ++it) if (it.value().region.contains(pos)) return it.key(); return None; } extern QString qt_setWindowTitle_helperHelper(const QString&, const QWidget*); /*! \internal */ QStyleOptionTitleBar QMdiSubWindowPrivate::titleBarOptions() const { Q_Q(const QMdiSubWindow); QStyleOptionTitleBar titleBarOptions; titleBarOptions.initFrom(q); if (activeSubControl != QStyle::SC_None) { if (hoveredSubControl == activeSubControl) { titleBarOptions.state |= QStyle::State_Sunken; titleBarOptions.activeSubControls = activeSubControl; } } else if (autoRaise() && hoveredSubControl != QStyle::SC_None && hoveredSubControl != QStyle::SC_TitleBarLabel) { titleBarOptions.state |= QStyle::State_MouseOver; titleBarOptions.activeSubControls = hoveredSubControl; } else { titleBarOptions.state &= ~QStyle::State_MouseOver; titleBarOptions.activeSubControls = QStyle::SC_None; } titleBarOptions.subControls = QStyle::SC_All; titleBarOptions.titleBarFlags = q->windowFlags(); titleBarOptions.titleBarState = q->windowState(); titleBarOptions.palette = titleBarPalette; titleBarOptions.icon = menuIcon; if (isActive) { titleBarOptions.state |= QStyle::State_Active; titleBarOptions.titleBarState |= QStyle::State_Active; titleBarOptions.palette.setCurrentColorGroup(QPalette::Active); } else { titleBarOptions.state &= ~QStyle::State_Active; titleBarOptions.palette.setCurrentColorGroup(QPalette::Inactive); } int border = hasBorder(titleBarOptions) ? 4 : 0; int paintHeight = titleBarHeight(titleBarOptions); paintHeight -= q->isMinimized() ? 2 * border : border; titleBarOptions.rect = QRect(border, border, q->width() - 2 * border, paintHeight); if (!windowTitle.isEmpty()) { // Set the text here before asking for the width of the title bar label // in case people uses the actual text to calculate the width. titleBarOptions.text = windowTitle; titleBarOptions.fontMetrics = QFontMetrics(font); int width = q->style()->subControlRect(QStyle::CC_TitleBar, &titleBarOptions, QStyle::SC_TitleBarLabel, q).width(); // Set elided text if we don't have enough space for the entire title. titleBarOptions.text = titleBarOptions.fontMetrics.elidedText(windowTitle, Qt::ElideRight, width); } return titleBarOptions; } /*! \internal */ void QMdiSubWindowPrivate::ensureWindowState(Qt::WindowState state) { Q_Q(QMdiSubWindow); Qt::WindowStates windowStates = q->windowState() | state; switch (state) { case Qt::WindowMinimized: windowStates &= ~Qt::WindowMaximized; windowStates &= ~Qt::WindowFullScreen; windowStates &= ~Qt::WindowNoState; break; case Qt::WindowMaximized: windowStates &= ~Qt::WindowMinimized; windowStates &= ~Qt::WindowFullScreen; windowStates &= ~Qt::WindowNoState; break; case Qt::WindowNoState: windowStates &= ~Qt::WindowMinimized; windowStates &= ~Qt::WindowMaximized; windowStates &= ~Qt::WindowFullScreen; break; default: break; } if (baseWidget) { if (!(baseWidget->windowState() & Qt::WindowActive) && windowStates & Qt::WindowActive) baseWidget->overrideWindowState(windowStates & ~Qt::WindowActive); else baseWidget->overrideWindowState(windowStates); } q->overrideWindowState(windowStates); } /*! \internal */ int QMdiSubWindowPrivate::titleBarHeight(const QStyleOptionTitleBar &options) const { Q_Q(const QMdiSubWindow); if (!parent || q->windowFlags() & Qt::FramelessWindowHint || (q->isMaximized() && !drawTitleBarWhenMaximized())) { return 0; } int height = q->style()->pixelMetric(QStyle::PM_TitleBarHeight, &options, q); if (hasBorder(options)) height += q->isMinimized() ? 8 : 4; return height; } /*! \internal */ void QMdiSubWindowPrivate::sizeParameters(int *margin, int *minWidth) const { Q_Q(const QMdiSubWindow); Qt::WindowFlags flags = q->windowFlags(); if (!parent || flags & Qt::FramelessWindowHint) { *margin = 0; *minWidth = 0; return; } if (q->isMaximized() && !drawTitleBarWhenMaximized()) *margin = 0; else *margin = q->style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, 0, q); QStyleOptionTitleBar opt = this->titleBarOptions(); int tempWidth = 0; for (int i = 0; i < NumSubControls; ++i) { if (SubControls[i] == QStyle::SC_TitleBarLabel) { tempWidth += 30; continue; } QRect rect = q->style()->subControlRect(QStyle::CC_TitleBar, &opt, SubControls[i], q); if (!rect.isValid()) continue; tempWidth += rect.width(); } *minWidth = tempWidth; } /*! \internal */ bool QMdiSubWindowPrivate::drawTitleBarWhenMaximized() const { Q_Q(const QMdiSubWindow); if (q->window()->testAttribute(Qt::WA_CanHostQMdiSubWindowTitleBar)) return false; if (isChildOfTabbedQMdiArea(q)) return false; #if defined(Q_OS_DARWIN) && !defined(QT_NO_STYLE_MAC) Q_UNUSED(isChildOfQMdiSubWindow); return true; #else if (q->style()->styleHint(QStyle::SH_Workspace_FillSpaceOnMaximize, 0, q)) return true; #if defined(QT_NO_MENUBAR) || defined(QT_NO_MAINWINDOW) Q_UNUSED(isChildOfQMdiSubWindow); return true; #else QMainWindow *mainWindow = qobject_cast(q->window()); if (!mainWindow || !qobject_cast(mainWindow->menuWidget()) || mainWindow->menuWidget()->isHidden()) return true; return isChildOfQMdiSubWindow(q); #endif #endif } #ifndef QT_NO_MENUBAR /*! \internal */ void QMdiSubWindowPrivate::showButtonsInMenuBar(QMenuBar *menuBar) { Q_Q(QMdiSubWindow); Q_ASSERT(q->isMaximized() && !drawTitleBarWhenMaximized()); if (isChildOfTabbedQMdiArea(q)) return; removeButtonsFromMenuBar(); if (!controlContainer) controlContainer = new ControlContainer(q); ignoreWindowTitleChange = true; controlContainer->showButtonsInMenuBar(menuBar); ignoreWindowTitleChange = false; QWidget *topLevelWindow = q->window(); topLevelWindow->setWindowModified(q->isWindowModified()); topLevelWindow->installEventFilter(q); int buttonHeight = 0; if (controlContainer->controllerWidget()) buttonHeight = controlContainer->controllerWidget()->height(); else if (controlContainer->systemMenuLabel()) buttonHeight = controlContainer->systemMenuLabel()->height(); // This will rarely happen. if (menuBar && menuBar->height() < buttonHeight && topLevelWindow->layout()) { // Make sure topLevelWindow->contentsRect returns correct geometry. // topLevelWidget->updateGeoemtry will not do the trick here since it will post the event. QEvent event(QEvent::LayoutRequest); QApplication::sendEvent(topLevelWindow, &event); } } /*! \internal */ void QMdiSubWindowPrivate::removeButtonsFromMenuBar() { Q_Q(QMdiSubWindow); if (!controlContainer || isChildOfTabbedQMdiArea(q)) return; QMenuBar *currentMenuBar = 0; #ifndef QT_NO_MAINWINDOW if (QMainWindow *mainWindow = qobject_cast(q->window())) { // NB! We can't use menuBar() here because that one will actually create // a menubar for us if not set. That's not what we want :-) currentMenuBar = qobject_cast(mainWindow->menuWidget()); } #endif ignoreWindowTitleChange = true; controlContainer->removeButtonsFromMenuBar(currentMenuBar); ignoreWindowTitleChange = false; QWidget *topLevelWindow = q->window(); topLevelWindow->removeEventFilter(q); if (baseWidget && !drawTitleBarWhenMaximized()) topLevelWindow->setWindowModified(false); originalTitle = QString::null; } #endif // QT_NO_MENUBAR void QMdiSubWindowPrivate::updateWindowTitle(bool isRequestFromChild) { Q_Q(QMdiSubWindow); if (isRequestFromChild && !q->windowTitle().isEmpty() && !lastChildWindowTitle.isEmpty() && lastChildWindowTitle != q->windowTitle()) { return; } QWidget *titleWidget = 0; if (isRequestFromChild) titleWidget = baseWidget; else titleWidget = q; if (!titleWidget || titleWidget->windowTitle().isEmpty()) return; ignoreWindowTitleChange = true; q->setWindowTitle(titleWidget->windowTitle()); if (q->maximizedButtonsWidget()) setNewWindowTitle(); ignoreWindowTitleChange = false; } #ifndef QT_NO_RUBBERBAND void QMdiSubWindowPrivate::enterRubberBandMode() { Q_Q(QMdiSubWindow); if (q->isMaximized()) return; Q_ASSERT(oldGeometry.isValid()); Q_ASSERT(parent); if (!rubberBand) { rubberBand = new QRubberBand(QRubberBand::Rectangle, q->parentWidget()); // For accessibility to identify this special widget. rubberBand->setObjectName(QLatin1String("qt_rubberband")); } QPoint rubberBandPos = q->mapToParent(QPoint(0, 0)); rubberBand->setGeometry(rubberBandPos.x(), rubberBandPos.y(), oldGeometry.width(), oldGeometry.height()); rubberBand->show(); isInRubberBandMode = true; q->grabMouse(); } void QMdiSubWindowPrivate::leaveRubberBandMode() { Q_Q(QMdiSubWindow); Q_ASSERT(rubberBand); Q_ASSERT(isInRubberBandMode); q->releaseMouse(); isInRubberBandMode = false; q->setGeometry(rubberBand->geometry()); rubberBand->hide(); currentOperation = None; } #endif // QT_NO_RUBBERBAND // Taken from the old QWorkspace (::readColors()) QPalette QMdiSubWindowPrivate::desktopPalette() const { Q_Q(const QMdiSubWindow); QPalette newPalette = q->palette(); bool colorsInitialized = false; #if 0 // Used to be included in Qt4 for Q_WS_WIN // ask system properties on windows #ifndef SPI_GETGRADIENTCAPTIONS #define SPI_GETGRADIENTCAPTIONS 0x1008 #endif #ifndef COLOR_GRADIENTACTIVECAPTION #define COLOR_GRADIENTACTIVECAPTION 27 #endif #ifndef COLOR_GRADIENTINACTIVECAPTION #define COLOR_GRADIENTINACTIVECAPTION 28 #endif if (QApplication::desktopSettingsAware()) { newPalette.setColor(QPalette::Active, QPalette::Highlight, colorref2qrgb(GetSysColor(COLOR_ACTIVECAPTION))); newPalette.setColor(QPalette::Inactive, QPalette::Highlight, colorref2qrgb(GetSysColor(COLOR_INACTIVECAPTION))); newPalette.setColor(QPalette::Active, QPalette::HighlightedText, colorref2qrgb(GetSysColor(COLOR_CAPTIONTEXT))); newPalette.setColor(QPalette::Inactive, QPalette::HighlightedText, colorref2qrgb(GetSysColor(COLOR_INACTIVECAPTIONTEXT))); colorsInitialized = true; BOOL hasGradient = false; SystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, &hasGradient, 0); if (hasGradient) { newPalette.setColor(QPalette::Active, QPalette::Base, colorref2qrgb(GetSysColor(COLOR_GRADIENTACTIVECAPTION))); newPalette.setColor(QPalette::Inactive, QPalette::Base, colorref2qrgb(GetSysColor(COLOR_GRADIENTINACTIVECAPTION))); } else { newPalette.setColor(QPalette::Active, QPalette::Base, newPalette.color(QPalette::Active, QPalette::Highlight)); newPalette.setColor(QPalette::Inactive, QPalette::Base, newPalette.color(QPalette::Inactive, QPalette::Highlight)); } } #endif if (!colorsInitialized) { newPalette.setColor(QPalette::Active, QPalette::Highlight, newPalette.color(QPalette::Active, QPalette::Highlight)); newPalette.setColor(QPalette::Active, QPalette::Base, newPalette.color(QPalette::Active, QPalette::Highlight)); newPalette.setColor(QPalette::Inactive, QPalette::Highlight, newPalette.color(QPalette::Inactive, QPalette::Dark)); newPalette.setColor(QPalette::Inactive, QPalette::Base, newPalette.color(QPalette::Inactive, QPalette::Dark)); newPalette.setColor(QPalette::Inactive, QPalette::HighlightedText, newPalette.color(QPalette::Inactive, QPalette::Window)); } return newPalette; } void QMdiSubWindowPrivate::updateActions() { Qt::WindowFlags windowFlags = q_func()->windowFlags(); // Hide all for (int i = 0; i < NumWindowStateActions; ++i) setVisible(WindowStateAction(i), false); if (windowFlags & Qt::FramelessWindowHint) return; setVisible(StayOnTopAction, true); setVisible(MoveAction, moveEnabled); setVisible(ResizeAction, resizeEnabled); // CloseAction if (windowFlags & Qt::WindowSystemMenuHint) setVisible(CloseAction, true); // RestoreAction if (windowFlags & (Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint)) setVisible(RestoreAction, true); // MinimizeAction if (windowFlags & Qt::WindowMinimizeButtonHint) setVisible(MinimizeAction, true); // MaximizeAction if (windowFlags & Qt::WindowMaximizeButtonHint) setVisible(MaximizeAction, true); } void QMdiSubWindowPrivate::setFocusWidget() { Q_Q(QMdiSubWindow); if (!baseWidget) { q->setFocus(); return; } // This will give focus to the next child if possible, otherwise // do nothing, hence it's not possible to tab between windows with // just hitting tab (unless Qt::TabFocus is removed from the focus policy). if (focusInReason == Qt::TabFocusReason) { q->focusNextChild(); return; } // Same as above, but gives focus to the previous child. if (focusInReason == Qt::BacktabFocusReason) { q->focusPreviousChild(); return; } if (!(q->windowState() & Qt::WindowMinimized) && restoreFocus()) return; if (QWidget *focusWidget = baseWidget->focusWidget()) { if (!focusWidget->hasFocus() && q->isAncestorOf(focusWidget) && focusWidget->isVisible() && !q->isMinimized() && focusWidget->focusPolicy() != Qt::NoFocus) { focusWidget->setFocus(); } else { q->setFocus(); } return; } QWidget *focusWidget = q->nextInFocusChain(); while (focusWidget && focusWidget != q && focusWidget->focusPolicy() == Qt::NoFocus) focusWidget = focusWidget->nextInFocusChain(); if (focusWidget && q->isAncestorOf(focusWidget)) focusWidget->setFocus(); else if (baseWidget->focusPolicy() != Qt::NoFocus) baseWidget->setFocus(); else if (!q->hasFocus()) q->setFocus(); } bool QMdiSubWindowPrivate::restoreFocus() { if (restoreFocusWidget.isNull()) return false; QWidget *candidate = restoreFocusWidget; restoreFocusWidget.clear(); if (!candidate->hasFocus() && q_func()->isAncestorOf(candidate) && candidate->isVisible() && candidate->focusPolicy() != Qt::NoFocus) { candidate->setFocus(); return true; } return candidate->hasFocus(); } /*! \internal */ void QMdiSubWindowPrivate::setWindowFlags(Qt::WindowFlags windowFlags) { Q_Q(QMdiSubWindow); if (!parent) { QWidgetPrivate::setWindowFlags(windowFlags); return; } Qt::WindowFlags windowType = windowFlags & Qt::WindowType_Mask; if (windowType == Qt::Dialog || windowFlags & Qt::MSWindowsFixedSizeDialogHint) windowFlags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint; // Set standard flags if none of the customize flags are set if (!(windowFlags & CustomizeWindowFlags)) windowFlags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint; else if (windowFlags & Qt::FramelessWindowHint && windowFlags & Qt::WindowStaysOnTopHint) windowFlags = Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint; else if (windowFlags & Qt::FramelessWindowHint) windowFlags = Qt::FramelessWindowHint; windowFlags &= ~windowType; windowFlags &= ~Qt::WindowFullscreenButtonHint; windowFlags |= Qt::SubWindow; #ifndef QT_NO_ACTION if (QAction *stayOnTopAction = actions[QMdiSubWindowPrivate::StayOnTopAction]) { if (windowFlags & Qt::WindowStaysOnTopHint) stayOnTopAction->setChecked(true); else stayOnTopAction->setChecked(false); } #endif #ifndef QT_NO_SIZEGRIP if ((windowFlags & Qt::FramelessWindowHint) && sizeGrip) delete sizeGrip; #endif QWidgetPrivate::setWindowFlags(windowFlags); updateGeometryConstraints(); updateActions(); QSize currentSize = q->size(); if (q->isVisible() && (currentSize.width() < internalMinimumSize.width() || currentSize.height() < internalMinimumSize.height())) { q->resize(currentSize.expandedTo(internalMinimumSize)); } } void QMdiSubWindowPrivate::setVisible(WindowStateAction action, bool visible) { #ifndef QT_NO_ACTION if (actions[action]) actions[action]->setVisible(visible); #endif Q_Q(QMdiSubWindow); if (!controlContainer) controlContainer = new ControlContainer(q); if (ControllerWidget *ctrlWidget = qobject_cast (controlContainer->controllerWidget())) { ctrlWidget->setControlVisible(action, visible); } } #ifndef QT_NO_ACTION void QMdiSubWindowPrivate::setEnabled(WindowStateAction action, bool enable) { if (actions[action]) actions[action]->setEnabled(enable); } #ifndef QT_NO_MENU void QMdiSubWindowPrivate::addToSystemMenu(WindowStateAction action, const QString &text, const char *slot) { if (!systemMenu) return; actions[action] = systemMenu->addAction(text, q_func(), slot); } #endif #endif // QT_NO_ACTION /*! \internal */ QSize QMdiSubWindowPrivate::iconSize() const { Q_Q(const QMdiSubWindow); if (!parent || q->windowFlags() & Qt::FramelessWindowHint) return QSize(-1, -1); return QSize(q->style()->pixelMetric(QStyle::PM_MdiSubWindowMinimizedWidth, 0, q), titleBarHeight()); } #ifndef QT_NO_SIZEGRIP /*! \internal */ void QMdiSubWindowPrivate::setSizeGrip(QSizeGrip *newSizeGrip) { Q_Q(QMdiSubWindow); if (!newSizeGrip || sizeGrip || q->windowFlags() & Qt::FramelessWindowHint) return; if (layout && layout->indexOf(newSizeGrip) != -1) return; newSizeGrip->setFixedSize(newSizeGrip->sizeHint()); bool putSizeGripInLayout = layout ? true : false; #if defined(Q_OS_MAC) && !defined(QT_NO_STYLE_MAC) if (qobject_cast(q->style())) putSizeGripInLayout = false; #endif if (putSizeGripInLayout) { layout->addWidget(newSizeGrip); layout->setAlignment(newSizeGrip, Qt::AlignBottom | Qt::AlignRight); } else { newSizeGrip->setParent(q); newSizeGrip->move(q->isLeftToRight() ? q->width() - newSizeGrip->width() : 0, q->height() - newSizeGrip->height()); sizeGrip = newSizeGrip; } newSizeGrip->raise(); updateGeometryConstraints(); newSizeGrip->installEventFilter(q); } /*! \internal */ void QMdiSubWindowPrivate::setSizeGripVisible(bool visible) const { // See if we can find any size grips const QList sizeGrips = q_func()->findChildren(); for (QSizeGrip *grip : sizeGrips) grip->setVisible(visible); } #endif // QT_NO_SIZEGRIP /*! \internal */ void QMdiSubWindowPrivate::updateInternalWindowTitle() { Q_Q(QMdiSubWindow); if (q->isWindowModified()) { windowTitle = q->windowTitle(); windowTitle.replace(QLatin1String("[*]"), QLatin1String("*")); } else { windowTitle = qt_setWindowTitle_helperHelper(q->windowTitle(), q); } q->update(0, 0, q->width(), titleBarHeight()); } /*! Constructs a new QMdiSubWindow widget. The \a parent and \a flags arguments are passed to QWidget's constructor. Instead of using addSubWindow(), it is also simply possible to use setParent() when you add the subwindow to a QMdiArea. Note that only \l{QMdiSubWindow}s can be set as children of QMdiArea; you cannot, for instance, write: \code //bad code QMdiArea mdiArea; QTextEdit editor(&mdiArea); // invalid child widget \endcode \sa QMdiArea::addSubWindow() */ QMdiSubWindow::QMdiSubWindow(QWidget *parent, Qt::WindowFlags flags) : QWidget(*new QMdiSubWindowPrivate, parent, 0) { Q_D(QMdiSubWindow); #ifndef QT_NO_MENU d->createSystemMenu(); addActions(d->systemMenu->actions()); #endif d->setWindowFlags(flags); setBackgroundRole(QPalette::Window); setAutoFillBackground(true); setMouseTracking(true); setLayout(new QVBoxLayout); setFocusPolicy(Qt::StrongFocus); layout()->setMargin(0); d->updateGeometryConstraints(); setAttribute(Qt::WA_Resized, false); d->titleBarPalette = d->desktopPalette(); d->font = QApplication::font("QMdiSubWindowTitleBar"); // We don't want the menu icon by default on mac. #ifndef Q_OS_MAC if (windowIcon().isNull()) d->menuIcon = style()->standardIcon(QStyle::SP_TitleBarMenuButton, 0, this); else d->menuIcon = windowIcon(); #endif connect(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)), this, SLOT(_q_processFocusChanged(QWidget*,QWidget*))); } /*! Destroys the subwindow. \sa QMdiArea::removeSubWindow() */ QMdiSubWindow::~QMdiSubWindow() { Q_D(QMdiSubWindow); #ifndef QT_NO_MENUBAR d->removeButtonsFromMenuBar(); #endif d->setActive(false); } /*! Sets \a widget as the internal widget of this subwindow. The internal widget is displayed in the center of the subwindow beneath the title bar. QMdiSubWindow takes temporary ownership of \a widget; you do not have to delete it. Any existing internal widget will be removed and reparented to the root window. \sa widget() */ void QMdiSubWindow::setWidget(QWidget *widget) { Q_D(QMdiSubWindow); if (!widget) { d->removeBaseWidget(); return; } if (Q_UNLIKELY(widget == d->baseWidget)) { qWarning("QMdiSubWindow::setWidget: widget is already set"); return; } bool wasResized = testAttribute(Qt::WA_Resized); d->removeBaseWidget(); if (QLayout *layout = this->layout()) layout->addWidget(widget); else widget->setParent(this); #ifndef QT_NO_SIZEGRIP QSizeGrip *sizeGrip = widget->findChild(); if (sizeGrip) sizeGrip->installEventFilter(this); if (d->sizeGrip) d->sizeGrip->raise(); #endif d->baseWidget = widget; d->baseWidget->installEventFilter(this); d->ignoreWindowTitleChange = true; bool isWindowModified = this->isWindowModified(); if (windowTitle().isEmpty()) { d->updateWindowTitle(true); isWindowModified = d->baseWidget->isWindowModified(); } if (!this->isWindowModified() && isWindowModified && windowTitle().contains(QLatin1String("[*]"))) { setWindowModified(isWindowModified); } d->lastChildWindowTitle = d->baseWidget->windowTitle(); d->ignoreWindowTitleChange = false; if (windowIcon().isNull() && !d->baseWidget->windowIcon().isNull()) setWindowIcon(d->baseWidget->windowIcon()); d->updateGeometryConstraints(); if (!wasResized && testAttribute(Qt::WA_Resized)) setAttribute(Qt::WA_Resized, false); } /*! Returns the current internal widget. \sa setWidget() */ QWidget *QMdiSubWindow::widget() const { return d_func()->baseWidget; } /*! \internal */ QWidget *QMdiSubWindow::maximizedButtonsWidget() const { Q_D(const QMdiSubWindow); if (isVisible() && d->controlContainer && isMaximized() && !d->drawTitleBarWhenMaximized() && !isChildOfTabbedQMdiArea(this)) { return d->controlContainer->controllerWidget(); } return 0; } /*! \internal */ QWidget *QMdiSubWindow::maximizedSystemMenuIconWidget() const { Q_D(const QMdiSubWindow); if (isVisible() && d->controlContainer && isMaximized() && !d->drawTitleBarWhenMaximized() && !isChildOfTabbedQMdiArea(this)) { return d->controlContainer->systemMenuLabel(); } return 0; } /*! Returns \c true if this window is shaded; otherwise returns \c false. A window is shaded if it is collapsed so that only the title bar is visible. */ bool QMdiSubWindow::isShaded() const { return d_func()->isShadeMode; } /*! If \a on is true, \a option is enabled on the subwindow; otherwise it is disabled. See SubWindowOption for the effect of each option. \sa SubWindowOption, testOption() */ void QMdiSubWindow::setOption(SubWindowOption option, bool on) { Q_D(QMdiSubWindow); d->options.setFlag(option, on); #ifndef QT_NO_RUBBERBAND if ((option & (RubberBandResize | RubberBandMove)) && !on && d->isInRubberBandMode) d->leaveRubberBandMode(); #endif } /*! Returns \c true if \a option is enabled; otherwise returns \c false. \sa SubWindowOption, setOption() */ bool QMdiSubWindow::testOption(SubWindowOption option) const { return d_func()->options & option; } /*! \property QMdiSubWindow::keyboardSingleStep \brief sets how far a widget should move or resize when using the keyboard arrow keys. When in keyboard-interactive mode, you can use the arrow and page keys to either move or resize the window. This property controls the arrow keys. The common way to enter keyboard interactive mode is to enter the subwindow menu, and select either "resize" or "move". The default keyboard single step value is 5 pixels. \sa keyboardPageStep */ int QMdiSubWindow::keyboardSingleStep() const { return d_func()->keyboardSingleStep; } void QMdiSubWindow::setKeyboardSingleStep(int step) { // Haven't done any boundary check here since negative step only // means inverted behavior, which is OK if the user want it. // A step equal to zero means "do nothing". d_func()->keyboardSingleStep = step; } /*! \property QMdiSubWindow::keyboardPageStep \brief sets how far a widget should move or resize when using the keyboard page keys. When in keyboard-interactive mode, you can use the arrow and page keys to either move or resize the window. This property controls the page keys. The common way to enter keyboard interactive mode is to enter the subwindow menu, and select either "resize" or "move". The default keyboard page step value is 20 pixels. \sa keyboardSingleStep */ int QMdiSubWindow::keyboardPageStep() const { return d_func()->keyboardPageStep; } void QMdiSubWindow::setKeyboardPageStep(int step) { // Haven't done any boundary check here since negative step only // means inverted behavior, which is OK if the user want it. // A step equal to zero means "do nothing". d_func()->keyboardPageStep = step; } #ifndef QT_NO_MENU /*! Sets \a systemMenu as the current system menu for this subwindow. By default, each QMdiSubWindow has a standard system menu. QActions for the system menu created by QMdiSubWindow will automatically be updated depending on the current window state; e.g., the minimize action will be disabled after the window is minimized. QActions added by the user are not updated by QMdiSubWindow. QMdiSubWindow takes ownership of \a systemMenu; you do not have to delete it. Any existing menus will be deleted. \sa systemMenu(), showSystemMenu() */ void QMdiSubWindow::setSystemMenu(QMenu *systemMenu) { Q_D(QMdiSubWindow); if (Q_UNLIKELY(systemMenu && systemMenu == d->systemMenu)) { qWarning("QMdiSubWindow::setSystemMenu: system menu is already set"); return; } if (d->systemMenu) { delete d->systemMenu; d->systemMenu = 0; } if (!systemMenu) return; if (systemMenu->parent() != this) systemMenu->setParent(this); d->systemMenu = systemMenu; } /*! Returns a pointer to the current system menu, or zero if no system menu is set. QMdiSubWindow provides a default system menu, but you can also set the menu with setSystemMenu(). \sa setSystemMenu(), showSystemMenu() */ QMenu *QMdiSubWindow::systemMenu() const { return d_func()->systemMenu; } /*! Shows the system menu below the system menu icon in the title bar. \sa setSystemMenu(), systemMenu() */ void QMdiSubWindow::showSystemMenu() { Q_D(QMdiSubWindow); if (!d->systemMenu) return; QPoint globalPopupPos; if (QWidget *icon = maximizedSystemMenuIconWidget()) { if (isLeftToRight()) globalPopupPos = icon->mapToGlobal(QPoint(0, icon->y() + icon->height())); else globalPopupPos = icon->mapToGlobal(QPoint(icon->width(), icon->y() + icon->height())); } else { if (isLeftToRight()) globalPopupPos = mapToGlobal(contentsRect().topLeft()); else // + QPoint(1, 0) because topRight() == QPoint(left() + width() -1, top()) globalPopupPos = mapToGlobal(contentsRect().topRight()) + QPoint(1, 0); } // Adjust x() with -menuwidth in reverse mode. if (isRightToLeft()) globalPopupPos -= QPoint(d->systemMenu->sizeHint().width(), 0); d->systemMenu->popup(globalPopupPos); } #endif // QT_NO_MENU /*! \since 4.4 Returns the area containing this sub-window, or 0 if there is none. \sa QMdiArea::addSubWindow() */ QMdiArea *QMdiSubWindow::mdiArea() const { QWidget *parent = parentWidget(); while (parent) { if (QMdiArea *area = qobject_cast(parent)) { if (area->viewport() == parentWidget()) return area; } parent = parent->parentWidget(); } return 0; } /*! Calling this function makes the subwindow enter the shaded mode. When the subwindow is shaded, only the title bar is visible. Although shading is not supported by all styles, this function will still show the subwindow as shaded, regardless of whether support for shading is available. However, when used with styles without shading support, the user will be unable to return from shaded mode through the user interface (e.g., through a shade button in the title bar). \sa isShaded() */ void QMdiSubWindow::showShaded() { if (!parent()) return; Q_D(QMdiSubWindow); // setMinimizeMode uses this function. if (!d->isShadeRequestFromMinimizeMode && isShaded()) return; d->isMaximizeMode = false; d->storeFocusWidget(); if (!d->isShadeRequestFromMinimizeMode) { d->isShadeMode = true; d->ensureWindowState(Qt::WindowMinimized); } #ifndef QT_NO_MENUBAR d->removeButtonsFromMenuBar(); #endif // showMinimized() will reset Qt::WindowActive, which makes sense // for top level widgets, but in MDI it makes sense to have an // active window which is minimized. if (hasFocus() || isAncestorOf(QApplication::focusWidget())) d->ensureWindowState(Qt::WindowActive); #ifndef QT_NO_SIZEGRIP d->setSizeGripVisible(false); #endif if (!d->restoreSize.isValid() || d->isShadeMode) { d->oldGeometry = geometry(); d->restoreSize.setWidth(d->oldGeometry.width()); d->restoreSize.setHeight(d->oldGeometry.height()); } // Hide the window before we change the geometry to avoid multiple resize // events and wrong window state. const bool wasVisible = isVisible(); if (wasVisible) setVisible(false); d->updateGeometryConstraints(); // Update minimum size to internalMinimumSize if set by user. if (!minimumSize().isNull()) { d->userMinimumSize = minimumSize(); setMinimumSize(d->internalMinimumSize); } resize(d->internalMinimumSize); // Hide the internal widget if not already hidden by the user. if (d->baseWidget && !d->baseWidget->isHidden() && !(windowFlags() & Qt::FramelessWindowHint)) { d->baseWidget->hide(); d->isWidgetHiddenByUs = true; } if (wasVisible) setVisible(true); d->setFocusWidget(); d->resizeEnabled = false; d->moveEnabled = true; d->updateDirtyRegions(); d->updateMask(); #ifndef QT_NO_ACTION d->setEnabled(QMdiSubWindowPrivate::MinimizeAction, false); d->setEnabled(QMdiSubWindowPrivate::ResizeAction, d->resizeEnabled); d->setEnabled(QMdiSubWindowPrivate::MaximizeAction, true); d->setEnabled(QMdiSubWindowPrivate::RestoreAction, true); d->setEnabled(QMdiSubWindowPrivate::MoveAction, d->moveEnabled); #endif } /*! \reimp */ bool QMdiSubWindow::eventFilter(QObject *object, QEvent *event) { Q_D(QMdiSubWindow); if (!object) return QWidget::eventFilter(object, event); #ifndef QT_NO_MENU // System menu events. if (d->systemMenu && d->systemMenu == object) { if (event->type() == QEvent::MouseButtonDblClick) { const QMouseEvent *mouseEvent = static_cast(event); const QAction *action = d->systemMenu->actionAt(mouseEvent->pos()); if (!action || action->isEnabled()) close(); } else if (event->type() == QEvent::MouseMove) { QMouseEvent *mouseEvent = static_cast(event); d->hoveredSubControl = d->getSubControl(mapFromGlobal(mouseEvent->globalPos())); } else if (event->type() == QEvent::Hide) { d->activeSubControl = QStyle::SC_None; update(QRegion(0, 0, width(), d->titleBarHeight())); } return QWidget::eventFilter(object, event); } #endif #ifndef QT_NO_SIZEGRIP if (object != d->baseWidget && parent() && qobject_cast(object)) { if (event->type() != QEvent::MouseButtonPress || !testOption(QMdiSubWindow::RubberBandResize)) return QWidget::eventFilter(object, event); const QMouseEvent *mouseEvent = static_cast(event); d->mousePressPosition = parentWidget()->mapFromGlobal(mouseEvent->globalPos()); d->oldGeometry = geometry(); d->currentOperation = isLeftToRight() ? QMdiSubWindowPrivate::BottomRightResize : QMdiSubWindowPrivate::BottomLeftResize; #ifndef QT_NO_RUBBERBAND d->enterRubberBandMode(); #endif return true; } #endif if (object != d->baseWidget && event->type() != QEvent::WindowTitleChange) return QWidget::eventFilter(object, event); switch (event->type()) { case QEvent::Show: d->setActive(true); break; case QEvent::ShowToParent: if (!d->isWidgetHiddenByUs) show(); break; case QEvent::WindowStateChange: { QWindowStateChangeEvent *changeEvent = static_cast(event); if (changeEvent->isOverride()) break; Qt::WindowStates oldState = changeEvent->oldState(); Qt::WindowStates newState = d->baseWidget->windowState(); if (!(oldState & Qt::WindowMinimized) && (newState & Qt::WindowMinimized)) showMinimized(); else if (!(oldState & Qt::WindowMaximized) && (newState & Qt::WindowMaximized)) showMaximized(); else if (!(newState & (Qt::WindowMaximized | Qt::WindowMinimized | Qt::WindowFullScreen))) showNormal(); break; } case QEvent::Enter: d->currentOperation = QMdiSubWindowPrivate::None; d->updateCursor(); break; case QEvent::LayoutRequest: d->updateGeometryConstraints(); break; case QEvent::WindowTitleChange: if (d->ignoreWindowTitleChange) break; if (object == d->baseWidget) { d->updateWindowTitle(true); d->lastChildWindowTitle = d->baseWidget->windowTitle(); #ifndef QT_NO_MENUBAR } else if (maximizedButtonsWidget() && d->controlContainer->menuBar() && d->controlContainer->menuBar() ->cornerWidget(Qt::TopRightCorner) == maximizedButtonsWidget()) { d->originalTitle = QString::null; if (d->baseWidget && d->baseWidget->windowTitle() == windowTitle()) d->updateWindowTitle(true); else d->updateWindowTitle(false); #endif } break; case QEvent::ModifiedChange: { if (object != d->baseWidget) break; bool windowModified = d->baseWidget->isWindowModified(); if (!windowModified && d->baseWidget->windowTitle() != windowTitle()) break; if (windowTitle().contains(QLatin1String("[*]"))) setWindowModified(windowModified); break; } default: break; } return QWidget::eventFilter(object, event); } /*! \reimp */ bool QMdiSubWindow::event(QEvent *event) { Q_D(QMdiSubWindow); switch (event->type()) { case QEvent::StyleChange: { bool wasShaded = isShaded(); bool wasMinimized = isMinimized(); bool wasMaximized = isMaximized(); // Don't emit subWindowActivated, the app doesn't have to know about our hacks const QScopedValueRollback activationEnabledSaver(d->activationEnabled); d->activationEnabled = false; ensurePolished(); setContentsMargins(0, 0, 0, 0); if (wasMinimized || wasMaximized || wasShaded) showNormal(); d->updateGeometryConstraints(); resize(d->internalMinimumSize.expandedTo(size())); d->updateMask(); d->updateDirtyRegions(); if (wasShaded) showShaded(); else if (wasMinimized) showMinimized(); else if (wasMaximized) showMaximized(); break; } case QEvent::ParentAboutToChange: d->setActive(false); break; case QEvent::ParentChange: { bool wasResized = testAttribute(Qt::WA_Resized); #ifndef QT_NO_MENUBAR d->removeButtonsFromMenuBar(); #endif d->currentOperation = QMdiSubWindowPrivate::None; d->activeSubControl = QStyle::SC_None; d->hoveredSubControl = QStyle::SC_None; #ifndef QT_NO_RUBBERBAND if (d->isInRubberBandMode) d->leaveRubberBandMode(); #endif d->isShadeMode = false; d->isMaximizeMode = false; d->isWidgetHiddenByUs = false; if (!parent()) { #if !defined(QT_NO_SIZEGRIP) && defined(Q_OS_MAC) && !defined(QT_NO_STYLE_MAC) if (qobject_cast(style())) delete d->sizeGrip; #endif setOption(RubberBandResize, false); setOption(RubberBandMove, false); } else { d->setWindowFlags(windowFlags()); } setContentsMargins(0, 0, 0, 0); d->updateGeometryConstraints(); d->updateCursor(); d->updateMask(); d->updateDirtyRegions(); d->updateActions(); if (!wasResized && testAttribute(Qt::WA_Resized)) setAttribute(Qt::WA_Resized, false); break; } case QEvent::WindowActivate: if (d->ignoreNextActivationEvent) { d->ignoreNextActivationEvent = false; break; } d->isExplicitlyDeactivated = false; d->setActive(true); break; case QEvent::WindowDeactivate: if (d->ignoreNextActivationEvent) { d->ignoreNextActivationEvent = false; break; } d->isExplicitlyDeactivated = true; d->setActive(false); break; case QEvent::WindowTitleChange: if (!d->ignoreWindowTitleChange) d->updateWindowTitle(false); d->updateInternalWindowTitle(); break; case QEvent::ModifiedChange: if (!windowTitle().contains(QLatin1String("[*]"))) break; #ifndef QT_NO_MENUBAR if (maximizedButtonsWidget() && d->controlContainer->menuBar() && d->controlContainer->menuBar() ->cornerWidget(Qt::TopRightCorner) == maximizedButtonsWidget()) { window()->setWindowModified(isWindowModified()); } #endif // QT_NO_MENUBAR d->updateInternalWindowTitle(); break; case QEvent::LayoutDirectionChange: d->updateDirtyRegions(); break; case QEvent::LayoutRequest: d->updateGeometryConstraints(); break; case QEvent::WindowIconChange: d->menuIcon = windowIcon(); if (d->menuIcon.isNull()) d->menuIcon = style()->standardIcon(QStyle::SP_TitleBarMenuButton, 0, this); if (d->controlContainer) d->controlContainer->updateWindowIcon(d->menuIcon); if (!maximizedSystemMenuIconWidget()) update(0, 0, width(), d->titleBarHeight()); break; case QEvent::PaletteChange: d->titleBarPalette = d->desktopPalette(); break; case QEvent::FontChange: d->font = font(); break; #ifndef QT_NO_TOOLTIP case QEvent::ToolTip: showToolTip(static_cast(event), this, d->titleBarOptions(), QStyle::CC_TitleBar, d->hoveredSubControl); break; #endif default: break; } return QWidget::event(event); } /*! \reimp */ void QMdiSubWindow::showEvent(QShowEvent *showEvent) { Q_D(QMdiSubWindow); if (!parent()) { QWidget::showEvent(showEvent); return; } #if !defined(QT_NO_SIZEGRIP) && defined(Q_OS_MAC) && !defined(QT_NO_STYLE_MAC) if (qobject_cast(style()) && !d->sizeGrip && !(windowFlags() & Qt::FramelessWindowHint)) { d->setSizeGrip(new QSizeGrip(this)); Q_ASSERT(d->sizeGrip); if (isMinimized()) d->setSizeGripVisible(false); else d->setSizeGripVisible(true); resize(size().expandedTo(d->internalMinimumSize)); } #endif d->updateDirtyRegions(); // Show buttons in the menu bar if they're already not there. // We want to do this when QMdiSubWindow becomes visible after being hidden. #ifndef QT_NO_MENUBAR if (d->controlContainer) { if (QMenuBar *menuBar = d->menuBar()) { if (menuBar->cornerWidget(Qt::TopRightCorner) != maximizedButtonsWidget()) d->showButtonsInMenuBar(menuBar); } } #endif d->setActive(true); } /*! \reimp */ void QMdiSubWindow::hideEvent(QHideEvent * /*hideEvent*/) { #ifndef QT_NO_MENUBAR d_func()->removeButtonsFromMenuBar(); #endif } /*! \reimp */ void QMdiSubWindow::changeEvent(QEvent *changeEvent) { if (!parent()) { QWidget::changeEvent(changeEvent); return; } if (changeEvent->type() != QEvent::WindowStateChange) { QWidget::changeEvent(changeEvent); return; } QWindowStateChangeEvent *event = static_cast(changeEvent); if (event->isOverride()) { event->ignore(); return; } Qt::WindowStates oldState = event->oldState(); Qt::WindowStates newState = windowState(); if (oldState == newState) { changeEvent->ignore(); return; } // QWidget ensures that the widget is visible _after_ setWindowState(), // but we need to ensure that the widget is visible _before_ // setWindowState() returns. Q_D(QMdiSubWindow); if (!isVisible()) { d->ensureWindowState(Qt::WindowNoState); setVisible(true); } if (!d->oldGeometry.isValid()) d->oldGeometry = geometry(); if ((oldState & Qt::WindowActive) && (newState & Qt::WindowActive)) d->currentOperation = QMdiSubWindowPrivate::None; if (!(oldState & Qt::WindowMinimized) && (newState & Qt::WindowMinimized)) d->setMinimizeMode(); else if (!(oldState & Qt::WindowMaximized) && (newState & Qt::WindowMaximized)) d->setMaximizeMode(); else if (!(newState & (Qt::WindowMaximized | Qt::WindowMinimized | Qt::WindowFullScreen))) d->setNormalMode(); if (d->isActive) d->ensureWindowState(Qt::WindowActive); if (d->activationEnabled) emit windowStateChanged(oldState, windowState()); } /*! \reimp */ void QMdiSubWindow::closeEvent(QCloseEvent *closeEvent) { Q_D(QMdiSubWindow); bool acceptClose = true; if (d->baseWidget) acceptClose = d->baseWidget->close(); if (!acceptClose) { closeEvent->ignore(); return; } #ifndef QT_NO_MENUBAR d->removeButtonsFromMenuBar(); #endif d->setActive(false); if (parentWidget() && testAttribute(Qt::WA_DeleteOnClose)) { QChildEvent childRemoved(QEvent::ChildRemoved, this); QApplication::sendEvent(parentWidget(), &childRemoved); } closeEvent->accept(); } /*! \reimp */ void QMdiSubWindow::leaveEvent(QEvent * /*leaveEvent*/) { Q_D(QMdiSubWindow); if (d->hoveredSubControl != QStyle::SC_None) { d->hoveredSubControl = QStyle::SC_None; update(QRegion(0, 0, width(), d->titleBarHeight())); } } /*! \reimp \warning When maximizing or restoring a subwindow, the resulting call to this function may have an invalid QResizeEvent::oldSize(). */ void QMdiSubWindow::resizeEvent(QResizeEvent *resizeEvent) { Q_D(QMdiSubWindow); #ifndef QT_NO_SIZEGRIP if (d->sizeGrip) { d->sizeGrip->move(isLeftToRight() ? width() - d->sizeGrip->width() : 0, height() - d->sizeGrip->height()); } #endif if (!parent()) { QWidget::resizeEvent(resizeEvent); return; } if (d->isMaximizeMode) d->ensureWindowState(Qt::WindowMaximized); d->updateMask(); if (!isVisible()) return; if (d->resizeTimerId <= 0) d->cachedStyleOptions = d->titleBarOptions(); else killTimer(d->resizeTimerId); d->resizeTimerId = startTimer(200); } /*! \reimp */ void QMdiSubWindow::timerEvent(QTimerEvent *timerEvent) { Q_D(QMdiSubWindow); if (timerEvent->timerId() == d->resizeTimerId) { killTimer(d->resizeTimerId); d->resizeTimerId = -1; d->updateDirtyRegions(); } } /*! \reimp */ void QMdiSubWindow::moveEvent(QMoveEvent *moveEvent) { if (!parent()) { QWidget::moveEvent(moveEvent); return; } Q_D(QMdiSubWindow); if (d->isMaximizeMode) d->ensureWindowState(Qt::WindowMaximized); } /*! \reimp */ void QMdiSubWindow::paintEvent(QPaintEvent *paintEvent) { if (!parent() || (windowFlags() & Qt::FramelessWindowHint)) { QWidget::paintEvent(paintEvent); return; } Q_D(QMdiSubWindow); if (isMaximized() && !d->drawTitleBarWhenMaximized()) return; if (d->resizeTimerId != -1) { // Only update the style option rect and the window title. int border = d->hasBorder(d->cachedStyleOptions) ? 4 : 0; int titleBarHeight = d->titleBarHeight(d->cachedStyleOptions); titleBarHeight -= isMinimized() ? 2 * border : border; d->cachedStyleOptions.rect = QRect(border, border, width() - 2 * border, titleBarHeight); if (!d->windowTitle.isEmpty()) { int width = style()->subControlRect(QStyle::CC_TitleBar, &d->cachedStyleOptions, QStyle::SC_TitleBarLabel, this).width(); d->cachedStyleOptions.text = d->cachedStyleOptions.fontMetrics .elidedText(d->windowTitle, Qt::ElideRight, width); } } else { // Force full update. d->cachedStyleOptions = d->titleBarOptions(); } QStylePainter painter(this); if (!d->windowTitle.isEmpty()) painter.setFont(d->font); painter.drawComplexControl(QStyle::CC_TitleBar, d->cachedStyleOptions); if (isMinimized() && !d->hasBorder(d->cachedStyleOptions)) return; QStyleOptionFrame frameOptions; frameOptions.initFrom(this); frameOptions.lineWidth = style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, 0, this); frameOptions.state.setFlag(QStyle::State_Active, d->isActive); // ### Ensure that we do not require setting the cliprect for 4.4 if (!isMinimized() && !d->hasBorder(d->cachedStyleOptions)) painter.setClipRect(rect().adjusted(0, d->titleBarHeight(d->cachedStyleOptions), 0, 0)); if (!isMinimized() || d->hasBorder(d->cachedStyleOptions)) painter.drawPrimitive(QStyle::PE_FrameWindow, frameOptions); } /*! \reimp */ void QMdiSubWindow::mousePressEvent(QMouseEvent *mouseEvent) { if (!parent()) { QWidget::mousePressEvent(mouseEvent); return; } Q_D(QMdiSubWindow); if (d->isInInteractiveMode) d->leaveInteractiveMode(); #ifndef QT_NO_RUBBERBAND if (d->isInRubberBandMode) d->leaveRubberBandMode(); #endif if (mouseEvent->button() != Qt::LeftButton) { mouseEvent->ignore(); return; } if (d->currentOperation != QMdiSubWindowPrivate::None) { d->updateCursor(); d->mousePressPosition = mapToParent(mouseEvent->pos()); if (d->resizeEnabled || d->moveEnabled) d->oldGeometry = geometry(); #ifndef QT_NO_RUBBERBAND if ((testOption(QMdiSubWindow::RubberBandResize) && d->isResizeOperation()) || (testOption(QMdiSubWindow::RubberBandMove) && d->isMoveOperation())) { d->enterRubberBandMode(); } #endif return; } d->activeSubControl = d->hoveredSubControl; #ifndef QT_NO_MENU if (d->activeSubControl == QStyle::SC_TitleBarSysMenu) showSystemMenu(); else #endif update(QRegion(0, 0, width(), d->titleBarHeight())); } /*! \reimp */ void QMdiSubWindow::mouseDoubleClickEvent(QMouseEvent *mouseEvent) { if (!parent()) { QWidget::mouseDoubleClickEvent(mouseEvent); return; } if (mouseEvent->button() != Qt::LeftButton) { mouseEvent->ignore(); return; } Q_D(QMdiSubWindow); if (!d->isMoveOperation()) { #ifndef QT_NO_MENU if (d->hoveredSubControl == QStyle::SC_TitleBarSysMenu) close(); #endif return; } Qt::WindowFlags flags = windowFlags(); if (isMinimized()) { if ((isShaded() && (flags & Qt::WindowShadeButtonHint)) || (flags & Qt::WindowMinimizeButtonHint)) { showNormal(); } return; } if (isMaximized()) { if (flags & Qt::WindowMaximizeButtonHint) showNormal(); return; } if (flags & Qt::WindowShadeButtonHint) showShaded(); else if (flags & Qt::WindowMaximizeButtonHint) showMaximized(); } /*! \reimp */ void QMdiSubWindow::mouseReleaseEvent(QMouseEvent *mouseEvent) { if (!parent()) { QWidget::mouseReleaseEvent(mouseEvent); return; } if (mouseEvent->button() != Qt::LeftButton) { mouseEvent->ignore(); return; } Q_D(QMdiSubWindow); if (d->currentOperation != QMdiSubWindowPrivate::None) { #ifndef QT_NO_RUBBERBAND if (d->isInRubberBandMode && !d->isInInteractiveMode) d->leaveRubberBandMode(); #endif if (d->resizeEnabled || d->moveEnabled) d->oldGeometry = geometry(); } d->currentOperation = d->getOperation(mouseEvent->pos()); d->updateCursor(); d->hoveredSubControl = d->getSubControl(mouseEvent->pos()); if (d->activeSubControl != QStyle::SC_None && d->activeSubControl == d->hoveredSubControl) { d->processClickedSubControl(); } d->activeSubControl = QStyle::SC_None; update(QRegion(0, 0, width(), d->titleBarHeight())); } /*! \reimp */ void QMdiSubWindow::mouseMoveEvent(QMouseEvent *mouseEvent) { if (!parent()) { QWidget::mouseMoveEvent(mouseEvent); return; } Q_D(QMdiSubWindow); // No update needed if we're in a move/resize operation. if (!d->isMoveOperation() && !d->isResizeOperation()) { // Find previous and current hover region. const QStyleOptionTitleBar options = d->titleBarOptions(); QStyle::SubControl oldHover = d->hoveredSubControl; d->hoveredSubControl = d->getSubControl(mouseEvent->pos()); QRegion hoverRegion; if (isHoverControl(oldHover) && oldHover != d->hoveredSubControl) hoverRegion += style()->subControlRect(QStyle::CC_TitleBar, &options, oldHover, this); if (isHoverControl(d->hoveredSubControl) && d->hoveredSubControl != oldHover) { hoverRegion += style()->subControlRect(QStyle::CC_TitleBar, &options, d->hoveredSubControl, this); } #if defined(Q_OS_MAC) && !defined(QT_NO_STYLE_MAC) if (qobject_cast(style()) && !hoverRegion.isEmpty()) hoverRegion += QRegion(0, 0, width(), d->titleBarHeight(options)); #endif if (!hoverRegion.isEmpty()) update(hoverRegion); } if ((mouseEvent->buttons() & Qt::LeftButton) || d->isInInteractiveMode) { if ((d->isResizeOperation() && d->resizeEnabled) || (d->isMoveOperation() && d->moveEnabled)) d->setNewGeometry(mapToParent(mouseEvent->pos())); return; } // Do not resize/move if not allowed. d->currentOperation = d->getOperation(mouseEvent->pos()); if ((d->isResizeOperation() && !d->resizeEnabled) || (d->isMoveOperation() && !d->moveEnabled)) d->currentOperation = QMdiSubWindowPrivate::None; d->updateCursor(); } /*! \reimp */ void QMdiSubWindow::keyPressEvent(QKeyEvent *keyEvent) { Q_D(QMdiSubWindow); if (!d->isInInteractiveMode || !parent()) { keyEvent->ignore(); return; } QPoint delta; switch (keyEvent->key()) { case Qt::Key_Right: if (keyEvent->modifiers() & Qt::ShiftModifier) delta = QPoint(d->keyboardPageStep, 0); else delta = QPoint(d->keyboardSingleStep, 0); break; case Qt::Key_Up: if (keyEvent->modifiers() & Qt::ShiftModifier) delta = QPoint(0, -d->keyboardPageStep); else delta = QPoint(0, -d->keyboardSingleStep); break; case Qt::Key_Left: if (keyEvent->modifiers() & Qt::ShiftModifier) delta = QPoint(-d->keyboardPageStep, 0); else delta = QPoint(-d->keyboardSingleStep, 0); break; case Qt::Key_Down: if (keyEvent->modifiers() & Qt::ShiftModifier) delta = QPoint(0, d->keyboardPageStep); else delta = QPoint(0, d->keyboardSingleStep); break; case Qt::Key_Escape: case Qt::Key_Return: case Qt::Key_Enter: d->leaveInteractiveMode(); return; default: keyEvent->ignore(); return; } #ifndef QT_NO_CURSOR QPoint newPosition = parentWidget()->mapFromGlobal(cursor().pos() + delta); QRect oldGeometry = #ifndef QT_NO_RUBBERBAND d->isInRubberBandMode ? d->rubberBand->geometry() : #endif geometry(); d->setNewGeometry(newPosition); QRect currentGeometry = #ifndef QT_NO_RUBBERBAND d->isInRubberBandMode ? d->rubberBand->geometry() : #endif geometry(); if (currentGeometry == oldGeometry) return; // Update cursor position QPoint actualDelta; if (d->isMoveOperation()) { actualDelta = QPoint(currentGeometry.x() - oldGeometry.x(), currentGeometry.y() - oldGeometry.y()); } else { int dx = isLeftToRight() ? currentGeometry.width() - oldGeometry.width() : currentGeometry.x() - oldGeometry.x(); actualDelta = QPoint(dx, currentGeometry.height() - oldGeometry.height()); } // Adjust in case we weren't able to move as long as wanted. if (actualDelta != delta) newPosition += (actualDelta - delta); cursor().setPos(parentWidget()->mapToGlobal(newPosition)); #endif } #ifndef QT_NO_CONTEXTMENU /*! \reimp */ void QMdiSubWindow::contextMenuEvent(QContextMenuEvent *contextMenuEvent) { Q_D(QMdiSubWindow); if (!d->systemMenu) { contextMenuEvent->ignore(); return; } if (d->hoveredSubControl == QStyle::SC_TitleBarSysMenu || d->getRegion(QMdiSubWindowPrivate::Move).contains(contextMenuEvent->pos())) { d->systemMenu->exec(contextMenuEvent->globalPos()); } else { contextMenuEvent->ignore(); } } #endif // QT_NO_CONTEXTMENU /*! \reimp */ void QMdiSubWindow::focusInEvent(QFocusEvent *focusInEvent) { d_func()->focusInReason = focusInEvent->reason(); } /*! \reimp */ void QMdiSubWindow::focusOutEvent(QFocusEvent * /*focusOutEvent*/) { // To avoid update() in QWidget::focusOutEvent. } /*! \reimp */ void QMdiSubWindow::childEvent(QChildEvent *childEvent) { if (childEvent->type() != QEvent::ChildPolished) return; #ifndef QT_NO_SIZEGRIP if (QSizeGrip *sizeGrip = qobject_cast(childEvent->child())) d_func()->setSizeGrip(sizeGrip); #endif } /*! \reimp */ QSize QMdiSubWindow::sizeHint() const { Q_D(const QMdiSubWindow); int margin, minWidth; d->sizeParameters(&margin, &minWidth); QSize size(2 * margin, d->titleBarHeight() + margin); if (d->baseWidget && d->baseWidget->sizeHint().isValid()) size += d->baseWidget->sizeHint(); return size.expandedTo(minimumSizeHint()); } /*! \reimp */ QSize QMdiSubWindow::minimumSizeHint() const { Q_D(const QMdiSubWindow); if (isVisible()) ensurePolished(); // Minimized window. if (parent() && isMinimized() && !isShaded()) return d->iconSize(); // Calculate window decoration. int margin, minWidth; d->sizeParameters(&margin, &minWidth); int decorationHeight = margin + d->titleBarHeight(); int minHeight = decorationHeight; // Shaded window. if (parent() && isShaded()) return QSize(qMax(minWidth, width()), d->titleBarHeight()); // Content if (layout()) { QSize minLayoutSize = layout()->minimumSize(); if (minLayoutSize.isValid()) { minWidth = qMax(minWidth, minLayoutSize.width() + 2 * margin); minHeight += minLayoutSize.height(); } } else if (d->baseWidget && d->baseWidget->isVisible()) { QSize minBaseWidgetSize = d->baseWidget->minimumSizeHint(); if (minBaseWidgetSize.isValid()) { minWidth = qMax(minWidth, minBaseWidgetSize.width() + 2 * margin); minHeight += minBaseWidgetSize.height(); } } #ifndef QT_NO_SIZEGRIP // SizeGrip int sizeGripHeight = 0; if (d->sizeGrip && d->sizeGrip->isVisibleTo(const_cast(this))) sizeGripHeight = d->sizeGrip->height(); #if defined(Q_OS_MAC) && !defined(QT_NO_STYLE_MAC) else if (parent() && qobject_cast(style()) && !d->sizeGrip) sizeGripHeight = style()->pixelMetric(QStyle::PM_SizeGripSize, 0, this); #endif minHeight = qMax(minHeight, decorationHeight + sizeGripHeight); #endif return QSize(minWidth, minHeight).expandedTo(QApplication::globalStrut()); } QT_END_NAMESPACE #include "moc_qmdisubwindow.cpp" #include "qmdisubwindow.moc" #endif //QT_NO_MDIAREA