diff options
Diffstat (limited to 'src/gui/widgets/qmdisubwindow.cpp')
-rw-r--r-- | src/gui/widgets/qmdisubwindow.cpp | 3552 |
1 files changed, 3552 insertions, 0 deletions
diff --git a/src/gui/widgets/qmdisubwindow.cpp b/src/gui/widgets/qmdisubwindow.cpp new file mode 100644 index 0000000..6bf7633 --- /dev/null +++ b/src/gui/widgets/qmdisubwindow.cpp @@ -0,0 +1,3552 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QMdiSubWindow + \brief The QMdiSubWindow class provides a subwindow class for + QMdiArea. + \since 4.3 + \ingroup application + \mainclass + + 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 <QApplication> +#include <QStylePainter> +#include <QVBoxLayout> +#include <QMouseEvent> +#include <QWhatsThis> +#include <QToolTip> +#include <QMainWindow> +#include <QScrollBar> +#include <QDebug> +#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC) +#include <QMacStyle> +#endif +#include <QMdiArea> + +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 QStyle::StandardPixmap ButtonPixmaps[] = +{ + QStyle::SP_TitleBarMinButton, + QStyle::SP_TitleBarNormalButton, + QStyle::SP_TitleBarCloseButton +}; +static const int NumButtonPixmaps = sizeof(ButtonPixmaps) / sizeof(ButtonPixmaps[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<QMdiSubWindow *>(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<typename T> +static inline ControlElement<T> *ptr(QWidget *widget) +{ + if (widget && widget->qt_metacast("ControlElement") + && strcmp(widget->metaObject()->className(), T::staticMetaObject.className()) == 0) { + return static_cast<ControlElement<T> *>(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 defined(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_WS_MAC) && !defined(QT_NO_STYLE_MAC) + // Native Mac windows don't show tool tip. + if (qobject_cast<QMacStyle *>(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<QMdiSubWindow *>(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; + +signals: + void _q_clicked(); + void _q_doubleClicked(); + +protected: + bool event(QEvent *event); + void paintEvent(QPaintEvent *paintEvent); + void mousePressEvent(QMouseEvent *mouseEvent); + void mouseDoubleClickEvent(QMouseEvent *mouseEvent); + void mouseReleaseEvent(QMouseEvent *mouseEvent); + +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(); +#ifndef QT_NO_TOOLTIP + else if (event->type() == QEvent::ToolTip) { + QStyleOptionTitleBar options; + options.initFrom(this); + showToolTip(static_cast<QHelpEvent *>(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()); + label = menuIcon.pixmap(16, 16); + update(); +} + +namespace QMdi { +/* + \class ControllerWidget + \internal +*/ +class ControllerWidget : public QWidget +{ + Q_OBJECT +public: + ControllerWidget(QMdiSubWindow *subWindow, QWidget *parent = 0); + QSize sizeHint() const; + 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); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void leaveEvent(QEvent *event); + bool event(QEvent *event); + +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<QMdiArea *>(subWindow->parentWidget()->parentWidget()); + setFocusPolicy(Qt::NoFocus); + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setMouseTracking(true); +} + +/* + \internal +*/ +QSize ControllerWidget::sizeHint() const +{ + ensurePolished(); + QStyleOptionComplex opt; + initStyleOption(&opt); + QSize size(48, 16); + 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; + + if (visible && !(visibleControls & subControl)) + visibleControls |= subControl; + else if (!visible && (visibleControls & subControl)) + 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<QHelpEvent *>(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<ControllerWidget>(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<ControlLabel>(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<QMainWindow *>(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<ControllerWidget *>(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<ControllerWidget> *ce = ptr<ControllerWidget>(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<ControlLabel> *ce = ptr<ControlLabel>(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<QAction *>(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<QAction *>(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 (QLayout *layout = q->layout()) + layout->removeWidget(baseWidget); + if (baseWidget->windowTitle() == q->windowTitle()) { + ignoreWindowTitleChange = true; + q->setWindowTitle(QString()); + ignoreWindowTitleChange = false; + q->setWindowModified(false); + } + lastChildWindowTitle.clear(); + 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); + 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]->setShortcut(QKeySequence::Close); +#endif + updateActions(); +} +#endif + +/*! + \internal +*/ +void QMdiSubWindowPrivate::updateCursor() +{ +#ifndef QT_NO_CURSOR + Q_Q(QMdiSubWindow); +#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC) + if (qobject_cast<QMacStyle *>(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 (!q_func()->parent()) + return; + + foreach (Operation operation, operationMap.keys()) + operationMap.find(operation).value().region = getRegion(operation); +} + +/*! + \internal +*/ +void QMdiSubWindowPrivate::updateGeometryConstraints() +{ + Q_Q(QMdiSubWindow); + if (!q->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 (!q->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(q->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(q->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(q->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(); +} + +/*! + \internal +*/ +void QMdiSubWindowPrivate::setMaximizeMode() +{ + Q_Q(QMdiSubWindow); + Q_ASSERT(q->parent()); + + ensureWindowState(Qt::WindowMaximized); + isShadeMode = false; + isMaximizeMode = true; + + if (!restoreFocusWidget && q->isAncestorOf(QApplication::focusWidget())) + restoreFocusWidget = QApplication::focusWidget(); + +#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<QAbstractScrollArea *>(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 (!q->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) { + 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_WS_MAC) && !defined(QT_NO_STYLE_MAC) + if (qobject_cast<QMacStyle *>(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_WS_MAC) && !defined(QT_NO_STYLE_MAC) + if (qobject_cast<QMacStyle *>(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_WS_MAC) && !defined(QT_NO_STYLE_MAC) + if (qobject_cast<QMacStyle *>(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::WindowNoState; + break; + case Qt::WindowMaximized: + windowStates &= ~Qt::WindowMinimized; + windowStates &= ~Qt::WindowNoState; + break; + case Qt::WindowNoState: + windowStates &= ~Qt::WindowMinimized; + windowStates &= ~Qt::WindowMaximized; + 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 (!q->parent() || q->windowFlags() & Qt::FramelessWindowHint + || (q->isMaximized() && !drawTitleBarWhenMaximized())) { + return 0; + } + + int height = q->style()->pixelMetric(QStyle::PM_TitleBarHeight, &options, q); +#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC) + // ### Fix mac style, the +4 pixels hack is not necessary anymore + if (qobject_cast<QMacStyle *>(q->style())) + height -= 4; +#endif + 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 (!q->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_WS_MAC) && !defined(QT_NO_STYLE_MAC) || defined(Q_OS_WINCE_WM) + return true; +#else + if (q->style()->styleHint(QStyle::SH_Workspace_FillSpaceOnMaximize, 0, q)) + return true; +#if defined(QT_NO_MENUBAR) || defined(QT_NO_MAINWINDOW) + return true; +#else + QMainWindow *mainWindow = qobject_cast<QMainWindow *>(q->window()); + if (!mainWindow || !qobject_cast<QMenuBar *>(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<QMainWindow *>(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<QMenuBar *>(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(q->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; +#ifdef 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))); + if (QSysInfo::WindowsVersion != QSysInfo::WV_95 + && QSysInfo::WindowsVersion != QSysInfo::WV_NT) { + colorsInitialized = true; + BOOL hasGradient; + QT_WA({ + SystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, &hasGradient, 0); + } , { + SystemParametersInfoA(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 // Q_WS_WIN + 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 (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(); +} + +void QMdiSubWindowPrivate::restoreFocus() +{ + if (!restoreFocusWidget) + return; + if (!restoreFocusWidget->hasFocus() && q_func()->isAncestorOf(restoreFocusWidget) + && restoreFocusWidget->isVisible() + && restoreFocusWidget->focusPolicy() != Qt::NoFocus) { + restoreFocusWidget->setFocus(); + } + restoreFocusWidget = 0; +} + +/*! + \internal + ### Please add QEvent::WindowFlagsChange event +*/ +void QMdiSubWindowPrivate::setWindowFlags(Qt::WindowFlags windowFlags) +{ + Q_Q(QMdiSubWindow); + if (!q->parent()) { + q->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::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 + + q->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<ControllerWidget *> + (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 (!q->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 (q->layout() && q->layout()->indexOf(newSizeGrip) != -1) + return; + newSizeGrip->setFixedSize(newSizeGrip->sizeHint()); + bool putSizeGripInLayout = q->layout() ? true : false; +#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC) + if (qobject_cast<QMacStyle *>(q->style())) + putSizeGripInLayout = false; +#endif + if (putSizeGripInLayout) { + q->layout()->addWidget(newSizeGrip); + q->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 + QList<QSizeGrip *> sizeGrips = qFindChildren<QSizeGrip *>(q_func()); + foreach (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: + + \badcode + 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("QWorkspaceTitleBar"); + // We don't want the menu icon by default on mac. +#ifndef Q_WS_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 (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 = qFindChild<QSizeGrip *>(widget); + 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 true if this window is shaded; otherwise returns 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); + if (on && !(d->options & option)) + d->options |= option; + else if (!on && (d->options & option)) + d->options &= ~option; + +#ifndef QT_NO_RUBBERBAND + if ((option & (RubberBandResize | RubberBandMove)) && !on && d->isInRubberBandMode) + d->leaveRubberBandMode(); +#endif +} + +/*! + Returns true if \a option is enabled; otherwise returns 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 (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->installEventFilter(this); + 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<QMdiArea *>(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; + + QWidget *currentFocusWidget = QApplication::focusWidget(); + if (!d->restoreFocusWidget && isAncestorOf(currentFocusWidget)) + d->restoreFocusWidget = currentFocusWidget; + + 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(currentFocusWidget)) + 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()) { + 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) { + close(); + } else if (event->type() == QEvent::MouseMove) { + QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); + d->hoveredSubControl = d->getSubControl(mapFromGlobal(mouseEvent->globalPos())); + } else if (event->type() == QEvent::Hide) { + d->systemMenu->removeEventFilter(this); + 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<QSizeGrip *>(object)) { + if (event->type() != QEvent::MouseButtonPress || !testOption(QMdiSubWindow::RubberBandResize)) + return QWidget::eventFilter(object, event); + const QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(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<QWindowStateChangeEvent*>(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))) + 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(); + 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_WS_MAC) && !defined(QT_NO_STYLE_MAC) + if (qobject_cast<QMacStyle *>(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<QHelpEvent *>(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_WS_MAC) && !defined(QT_NO_STYLE_MAC) + if (qobject_cast<QMacStyle *>(style()) && !d->sizeGrip + && !(windowFlags() & Qt::FramelessWindowHint)) { + d->setSizeGrip(new QSizeGrip(0)); + 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<QWindowStateChangeEvent *>(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))) + d->setNormalMode(); + + if (d->isActive) + d->ensureWindowState(Qt::WindowActive); + 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 +*/ +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); + if (d->isActive) + frameOptions.state |= QStyle::State_Active; + else + frameOptions.state &= ~QStyle::State_Active; + + // ### 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_WS_MAC) && !defined(QT_NO_STYLE_MAC) + if (qobject_cast<QMacStyle *>(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<QSizeGrip *>(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<QMdiSubWindow *>(this))) + sizeGripHeight = d->sizeGrip->height(); +#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC) + else if (parent() && qobject_cast<QMacStyle *>(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 |