/**************************************************************************** ** ** 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$ ** ****************************************************************************/ #include "qglobal.h" #ifndef QT_NO_GRAPHICSVIEW #include #include "qgraphicswidget_p.h" #include "qgraphicslayout.h" #include "qgraphicsscene_p.h" #include #include #include #include #include #if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC) # include #endif QT_BEGIN_NAMESPACE void QGraphicsWidgetPrivate::init(QGraphicsItem *parentItem, Qt::WindowFlags wFlags) { Q_Q(QGraphicsWidget); attributes = 0; isWidget = 1; // QGraphicsItem::isWidget() returns true. focusNext = focusPrev = q; focusPolicy = Qt::NoFocus; adjustWindowFlags(&wFlags); windowFlags = wFlags; q->setParentItem(parentItem); q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::DefaultType)); q->setGraphicsItem(q); resolveLayoutDirection(); q->unsetWindowFrameMargins(); } qreal QGraphicsWidgetPrivate::titleBarHeight(const QStyleOptionTitleBar &options) const { Q_Q(const QGraphicsWidget); int height = q->style()->pixelMetric(QStyle::PM_TitleBarHeight, &options); #if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC) if (qobject_cast(q->style())) { height -=4; } #endif return (qreal)height; } /*! \internal */ QGraphicsWidgetPrivate::~QGraphicsWidgetPrivate() { // Remove any lazily allocated data delete[] margins; delete[] windowFrameMargins; delete windowData; } /*! \internal Ensures that margins is allocated. This function must be called before any dereferencing. */ void QGraphicsWidgetPrivate::ensureMargins() const { if (!margins) { margins = new qreal[4]; for (int i = 0; i < 4; ++i) margins[i] = 0; } } /*! \internal Ensures that windowFrameMargins is allocated. This function must be called before any dereferencing. */ void QGraphicsWidgetPrivate::ensureWindowFrameMargins() const { if (!windowFrameMargins) { windowFrameMargins = new qreal[4]; for (int i = 0; i < 4; ++i) windowFrameMargins[i] = 0; } } /*! \internal Ensures that windowData is allocated. This function must be called before any dereferencing. */ void QGraphicsWidgetPrivate::ensureWindowData() { if (!windowData) windowData = new WindowData; } void QGraphicsWidgetPrivate::setPalette_helper(const QPalette &palette) { if (this->palette == palette && this->palette.resolve() == palette.resolve()) return; updatePalette(palette); } void QGraphicsWidgetPrivate::resolvePalette(uint inheritedMask) { inheritedPaletteResolveMask = inheritedMask; QPalette naturalPalette = naturalWidgetPalette(); QPalette resolvedPalette = palette.resolve(naturalPalette); updatePalette(resolvedPalette); } void QGraphicsWidgetPrivate::updatePalette(const QPalette &palette) { Q_Q(QGraphicsWidget); // Update local palette setting. this->palette = palette; // Calculate new mask. if (q->isWindow() && !q->testAttribute(Qt::WA_WindowPropagation)) inheritedPaletteResolveMask = 0; int mask = palette.resolve() | inheritedPaletteResolveMask; // Propagate to children. for (int i = 0; i < children.size(); ++i) { QGraphicsItem *item = children.at(i); if (item->isWidget()) { QGraphicsWidget *w = static_cast(item); if (!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation)) w->d_func()->resolvePalette(mask); } else { item->d_ptr->resolvePalette(mask); } } // Notify change. QEvent event(QEvent::PaletteChange); QApplication::sendEvent(q, &event); } void QGraphicsWidgetPrivate::setLayoutDirection_helper(Qt::LayoutDirection direction) { Q_Q(QGraphicsWidget); if ((direction == Qt::RightToLeft) == (testAttribute(Qt::WA_RightToLeft))) return; q->setAttribute(Qt::WA_RightToLeft, (direction == Qt::RightToLeft)); // Propagate this change to all children. for (int i = 0; i < children.size(); ++i) { QGraphicsItem *item = children.at(i); if (item->isWidget()) { QGraphicsWidget *widget = static_cast(item); if (widget->parentWidget() && !widget->testAttribute(Qt::WA_SetLayoutDirection)) widget->d_func()->setLayoutDirection_helper(direction); } } // Send the notification event to this widget item. QEvent e(QEvent::LayoutDirectionChange); QApplication::sendEvent(q, &e); } void QGraphicsWidgetPrivate::resolveLayoutDirection() { Q_Q(QGraphicsWidget); if (q->testAttribute(Qt::WA_SetLayoutDirection)) { return; } if (QGraphicsWidget *parentWidget = q->parentWidget()) { setLayoutDirection_helper(parentWidget->layoutDirection()); } else if (scene) { // ### shouldn't the scene have a layoutdirection really? how does // ### QGraphicsWidget get changes from QApplication::layoutDirection? setLayoutDirection_helper(QApplication::layoutDirection()); } else { setLayoutDirection_helper(QApplication::layoutDirection()); } } QPalette QGraphicsWidgetPrivate::naturalWidgetPalette() const { Q_Q(const QGraphicsWidget); QPalette palette; if (QGraphicsWidget *parent = q->parentWidget()) { palette = parent->palette(); } else if (scene) { palette = scene->palette(); } palette.resolve(0); return palette; } void QGraphicsWidgetPrivate::setFont_helper(const QFont &font) { if (this->font == font && this->font.resolve() == font.resolve()) return; updateFont(font); } void QGraphicsWidgetPrivate::resolveFont(uint inheritedMask) { inheritedFontResolveMask = inheritedMask; QFont naturalFont = naturalWidgetFont(); QFont resolvedFont = font.resolve(naturalFont); updateFont(resolvedFont); } void QGraphicsWidgetPrivate::updateFont(const QFont &font) { Q_Q(QGraphicsWidget); // Update the local font setting. this->font = font; // Calculate new mask. if (q->isWindow() && !q->testAttribute(Qt::WA_WindowPropagation)) inheritedFontResolveMask = 0; int mask = font.resolve() | inheritedFontResolveMask; // Propagate to children. for (int i = 0; i < children.size(); ++i) { QGraphicsItem *item = children.at(i); if (item->isWidget()) { QGraphicsWidget *w = static_cast(item); if (!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation)) w->d_func()->resolveFont(mask); } else { item->d_ptr->resolveFont(mask); } } if (!polished) return; // Notify change. QEvent event(QEvent::FontChange); QApplication::sendEvent(q, &event); } QFont QGraphicsWidgetPrivate::naturalWidgetFont() const { Q_Q(const QGraphicsWidget); QFont naturalFont; // ### no application font support if (QGraphicsWidget *parent = q->parentWidget()) { naturalFont = parent->font(); } else if (scene) { naturalFont = scene->font(); } naturalFont.resolve(0); return naturalFont; } void QGraphicsWidgetPrivate::initStyleOptionTitleBar(QStyleOptionTitleBar *option) { Q_Q(QGraphicsWidget); ensureWindowData(); q->initStyleOption(option); option->rect.setHeight(titleBarHeight(*option)); option->titleBarFlags = windowFlags; option->subControls = QStyle::SC_TitleBarCloseButton | QStyle::SC_TitleBarLabel | QStyle::SC_TitleBarSysMenu; option->activeSubControls = windowData->hoveredSubControl; bool isActive = q->isActiveWindow(); if (isActive) { option->state |= QStyle::State_Active; option->titleBarState = Qt::WindowActive; option->titleBarState |= QStyle::State_Active; } else { option->state &= ~QStyle::State_Active; option->titleBarState = Qt::WindowNoState; } QFont windowTitleFont = QApplication::font("QWorkspaceTitleBar"); QRect textRect = q->style()->subControlRect(QStyle::CC_TitleBar, option, QStyle::SC_TitleBarLabel, 0); option->text = QFontMetrics(windowTitleFont).elidedText( windowData->windowTitle, Qt::ElideRight, textRect.width()); } void QGraphicsWidgetPrivate::adjustWindowFlags(Qt::WindowFlags *flags) { bool customize = (*flags & (Qt::CustomizeWindowHint | Qt::FramelessWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowContextHelpButtonHint)); uint type = (*flags & Qt::WindowType_Mask); if (customize) ; else if (type == Qt::Dialog || type == Qt::Sheet) *flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowContextHelpButtonHint; else if (type == Qt::Tool) *flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint; else if (type == Qt::Window || type == Qt::SubWindow) *flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint; } void QGraphicsWidgetPrivate::windowFrameMouseReleaseEvent(QGraphicsSceneMouseEvent *event) { Q_Q(QGraphicsWidget); ensureWindowData(); if (windowData->grabbedSection != Qt::NoSection) { if (windowData->grabbedSection == Qt::TitleBarArea) { windowData->buttonSunken = false; QStyleOptionTitleBar bar; initStyleOptionTitleBar(&bar); // make sure that the coordinates (rect and pos) we send to the style are positive. bar.rect = q->windowFrameRect().toRect(); bar.rect.moveTo(0,0); bar.rect.setHeight(q->style()->pixelMetric(QStyle::PM_TitleBarHeight, &bar)); QPointF pos = event->pos(); if (windowFrameMargins) { pos.rx() += windowFrameMargins[Left]; pos.ry() += windowFrameMargins[Top]; } bar.subControls = QStyle::SC_TitleBarCloseButton; if (q->style()->subControlRect(QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarCloseButton, event->widget()).contains(pos.toPoint())) { q->close(); } } if (!(static_cast(event)->buttons())) windowData->grabbedSection = Qt::NoSection; event->accept(); } } void QGraphicsWidgetPrivate::windowFrameMousePressEvent(QGraphicsSceneMouseEvent *event) { Q_Q(QGraphicsWidget); if (event->button() != Qt::LeftButton) return; ensureWindowData(); windowData->startGeometry = q->geometry(); windowData->grabbedSection = q->windowFrameSectionAt(event->pos()); ensureWindowData(); if (windowData->grabbedSection == Qt::TitleBarArea && windowData->hoveredSubControl == QStyle::SC_TitleBarCloseButton) { windowData->buttonSunken = true; q->update(); } event->setAccepted(windowData->grabbedSection != Qt::NoSection); } static void _q_boundGeometryToSizeConstraints(const QRectF &startGeometry, QRectF *rect, Qt::WindowFrameSection section, const QSizeF &min, const QSizeF &max) { int height; int width; switch (section) { case Qt::LeftSection: width = qRound(qBound(min.width(), rect->width(), max.width())); rect->setRect(startGeometry.right() - width, startGeometry.top(), width, startGeometry.height()); break; case Qt::TopLeftSection: width = qRound(qBound(min.width(), rect->width(), max.width())); height = qRound(qBound(min.height(), rect->height(), max.height())); rect->setRect(startGeometry.right() - width, startGeometry.bottom() - height, width, height); break; case Qt::TopSection: height = qRound(qBound(min.height(), rect->height(), max.height())); rect->setRect(startGeometry.left(), startGeometry.bottom() - height, startGeometry.width(), height); break; case Qt::TopRightSection: height = qRound(qBound(min.height(), rect->height(), max.height())); rect->setTop(rect->bottom() - height); rect->setWidth(qBound(min.width(), rect->width(), max.width())); break; case Qt::RightSection: rect->setWidth(qBound(min.width(), rect->width(), max.width())); break; case Qt::BottomRightSection: rect->setWidth(qBound(min.width(), rect->width(), max.width())); rect->setHeight(qBound(min.height(), rect->height(), max.height())); break; case Qt::BottomSection: rect->setHeight(qBound(min.height(), rect->height(), max.height())); break; case Qt::BottomLeftSection: height = qRound(qBound(min.height(), rect->height(), max.height())); width = qRound(qBound(min.width(), rect->width(), max.width())); rect->setRect(startGeometry.right() - width, startGeometry.top(), width, height); break; default: break; } } void QGraphicsWidgetPrivate::windowFrameMouseMoveEvent(QGraphicsSceneMouseEvent *event) { Q_Q(QGraphicsWidget); ensureWindowData(); if (!(event->buttons() & Qt::LeftButton) || windowData->hoveredSubControl != QStyle::SC_TitleBarLabel) return; QLineF delta(q->mapFromScene(event->buttonDownScenePos(Qt::LeftButton)), event->pos()); QLineF parentDelta(q->mapToParent(delta.p1()), q->mapToParent(delta.p2())); QLineF parentXDelta(q->mapToParent(QPointF(delta.p1().x(), 0)), q->mapToParent(QPointF(delta.p2().x(), 0))); QLineF parentYDelta(q->mapToParent(QPointF(0, delta.p1().y())), q->mapToParent(QPointF(0, delta.p2().y()))); QRectF newGeometry; switch (windowData->grabbedSection) { case Qt::LeftSection: newGeometry = QRectF(windowData->startGeometry.topLeft() + QPointF(parentXDelta.dx(), parentXDelta.dy()), windowData->startGeometry.size() - QSizeF(delta.dx(), delta.dy())); break; case Qt::TopLeftSection: newGeometry = QRectF(windowData->startGeometry.topLeft() + QPointF(parentDelta.dx(), parentDelta.dy()), windowData->startGeometry.size() - QSizeF(delta.dx(), delta.dy())); break; case Qt::TopSection: newGeometry = QRectF(windowData->startGeometry.topLeft() + QPointF(parentYDelta.dx(), parentYDelta.dy()), windowData->startGeometry.size() - QSizeF(0, delta.dy())); break; case Qt::TopRightSection: newGeometry = QRectF(windowData->startGeometry.topLeft() + QPointF(parentYDelta.dx(), parentYDelta.dy()), windowData->startGeometry.size() - QSizeF(-delta.dx(), delta.dy())); break; case Qt::RightSection: newGeometry = QRectF(windowData->startGeometry.topLeft(), windowData->startGeometry.size() + QSizeF(delta.dx(), 0)); break; case Qt::BottomRightSection: newGeometry = QRectF(windowData->startGeometry.topLeft(), windowData->startGeometry.size() + QSizeF(delta.dx(), delta.dy())); break; case Qt::BottomSection: newGeometry = QRectF(windowData->startGeometry.topLeft(), windowData->startGeometry.size() + QSizeF(0, delta.dy())); break; case Qt::BottomLeftSection: newGeometry = QRectF(windowData->startGeometry.topLeft() + QPointF(parentXDelta.dx(), parentXDelta.dy()), windowData->startGeometry.size() - QSizeF(delta.dx(), -delta.dy())); break; case Qt::TitleBarArea: newGeometry = QRectF(windowData->startGeometry.topLeft() + QPointF(parentDelta.dx(), parentDelta.dy()), windowData->startGeometry.size()); break; case Qt::NoSection: break; } if (windowData->grabbedSection != Qt::NoSection) { _q_boundGeometryToSizeConstraints(windowData->startGeometry, &newGeometry, windowData->grabbedSection, q->effectiveSizeHint(Qt::MinimumSize), q->effectiveSizeHint(Qt::MaximumSize)); q->setGeometry(newGeometry); } } void QGraphicsWidgetPrivate::windowFrameHoverMoveEvent(QGraphicsSceneHoverEvent *event) { Q_Q(QGraphicsWidget); if (!hasDecoration()) return; ensureWindowData(); if (q->rect().contains(event->pos())) { if (windowData->buttonMouseOver || windowData->hoveredSubControl != QStyle::SC_None) windowFrameHoverLeaveEvent(event); return; } bool wasMouseOver = windowData->buttonMouseOver; QRect oldButtonRect = windowData->buttonRect; windowData->buttonRect = QRect(); windowData->buttonMouseOver = false; QPointF pos = event->pos(); QStyleOptionTitleBar bar; // make sure that the coordinates (rect and pos) we send to the style are positive. if (windowFrameMargins) { pos.rx() += windowFrameMargins[Left]; pos.ry() += windowFrameMargins[Top]; } initStyleOptionTitleBar(&bar); bar.rect = q->windowFrameRect().toRect(); bar.rect.moveTo(0,0); bar.rect.setHeight(int(titleBarHeight(bar))); Qt::CursorShape cursorShape = Qt::ArrowCursor; bool needsSetCursorCall = true; switch (q->windowFrameSectionAt(event->pos())) { case Qt::TopLeftSection: case Qt::BottomRightSection: cursorShape = Qt::SizeFDiagCursor; break; case Qt::TopRightSection: case Qt::BottomLeftSection: cursorShape = Qt::SizeBDiagCursor; break; case Qt::LeftSection: case Qt::RightSection: cursorShape = Qt::SizeHorCursor; break; case Qt::TopSection: case Qt::BottomSection: cursorShape = Qt::SizeVerCursor; break; case Qt::TitleBarArea: windowData->buttonRect = q->style()->subControlRect( QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarCloseButton, 0); #ifdef Q_WS_MAC // On mac we should hover if we are in the 'area' of the buttons windowData->buttonRect |= q->style()->subControlRect( QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarMinButton, 0); windowData->buttonRect |= q->style()->subControlRect( QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarMaxButton, 0); #endif if (windowData->buttonRect.contains(pos.toPoint())) windowData->buttonMouseOver = true; event->ignore(); break; default: needsSetCursorCall = false; event->ignore(); } #ifndef QT_NO_CURSOR if (needsSetCursorCall) q->setCursor(cursorShape); #endif // update buttons if we hover over them windowData->hoveredSubControl = q->style()->hitTestComplexControl(QStyle::CC_TitleBar, &bar, pos.toPoint(), 0); if (windowData->hoveredSubControl != QStyle::SC_TitleBarCloseButton) windowData->hoveredSubControl = QStyle::SC_TitleBarLabel; if (windowData->buttonMouseOver != wasMouseOver) { if (!oldButtonRect.isNull()) q->update(QRectF(oldButtonRect).translated(q->windowFrameRect().topLeft())); if (!windowData->buttonRect.isNull()) q->update(QRectF(windowData->buttonRect).translated(q->windowFrameRect().topLeft())); } } void QGraphicsWidgetPrivate::windowFrameHoverLeaveEvent(QGraphicsSceneHoverEvent *event) { Q_UNUSED(event); Q_Q(QGraphicsWidget); if (hasDecoration()) { // ### restore the cursor, don't override it #ifndef QT_NO_CURSOR q->unsetCursor(); #endif ensureWindowData(); bool needsUpdate = false; if (windowData->hoveredSubControl == QStyle::SC_TitleBarCloseButton || windowData->buttonMouseOver) needsUpdate = true; // update the hover state (of buttons etc...) windowData->hoveredSubControl = QStyle::SC_None; windowData->buttonMouseOver = false; windowData->buttonRect = QRect(); if (needsUpdate) q->update(windowData->buttonRect); } } bool QGraphicsWidgetPrivate::hasDecoration() const { return (windowFlags & Qt::Window) && (windowFlags & Qt::WindowTitleHint); } /*! \internal */ void QGraphicsWidgetPrivate::setFocusWidget() { // Update focus child chain. QGraphicsWidget *widget = static_cast(q_ptr); QGraphicsWidget *parent = widget; bool hidden = !visible; do { parent->d_func()->focusChild = widget; } while (!parent->isWindow() && (parent = parent->parentWidget()) && (!hidden || !parent->d_func()->visible)); } /*! \internal */ void QGraphicsWidgetPrivate::clearFocusWidget() { // Reset focus child chain. QGraphicsWidget *parent = static_cast(q_ptr); do { if (parent->d_func()->focusChild != q_ptr) break; parent->d_func()->focusChild = 0; } while (!parent->isWindow() && (parent = parent->parentWidget())); } /** * is called after a reparent has taken place to fix up the focus chain(s) */ void QGraphicsWidgetPrivate::fixFocusChainBeforeReparenting(QGraphicsWidget *newParent, QGraphicsScene *newScene) { Q_Q(QGraphicsWidget); Q_ASSERT(focusNext && focusPrev); QGraphicsWidget *n = q; //last one in 'new' list QGraphicsWidget *o = 0; //last one in 'old' list QGraphicsWidget *w = focusNext; QGraphicsWidget *firstOld = 0; bool wasPreviousNew = true; if (focusChild) { // Ensure that the current focus child doesn't leave pointers around // before reparenting. focusChild->clearFocus(); } while (w != q) { bool isCurrentNew = q->isAncestorOf(w); if (isCurrentNew) { if (!wasPreviousNew) { n->d_func()->focusNext = w; w->d_func()->focusPrev = n; } n = w; } else /*if (!isCurrentNew)*/ { if (wasPreviousNew) { if (o) { o->d_func()->focusNext = w; w->d_func()->focusPrev = o; } else { firstOld = w; } } o = w; } w = w->d_func()->focusNext; wasPreviousNew = isCurrentNew; } // repair the 'old' chain if (firstOld) { o->d_func()->focusNext = firstOld; firstOld->d_func()->focusPrev = o; } // update tabFocusFirst for oldScene if the item is going to be removed from oldScene if (newParent) newScene = newParent->scene(); QGraphicsScene *oldScene = q->scene(); if (oldScene && newScene != oldScene) oldScene->d_func()->tabFocusFirst = firstOld; QGraphicsItem *topLevelItem = newParent ? newParent->topLevelItem() : 0; QGraphicsWidget *topLevel = 0; if (topLevelItem && topLevelItem->isWidget()) topLevel = static_cast(topLevelItem); if (topLevel && newParent) { QGraphicsWidget *last = topLevel->d_func()->focusPrev; // link last with new chain last->d_func()->focusNext = q; focusPrev = last; // link last in chain with topLevel->d_func()->focusPrev = n; n->d_func()->focusNext = topLevel; } else { // q is the start of the focus chain n->d_func()->focusNext = q; focusPrev = n; } } void QGraphicsWidgetPrivate::setLayout_helper(QGraphicsLayout *l) { delete (this->layout); layout = l; if (!l) { Q_Q(QGraphicsWidget); q->updateGeometry(); } } QT_END_NAMESPACE #endif //QT_NO_GRAPHICSVIEW